Merge tag 'sound-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Mar 2024 18:10:43 +0000 (11:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Mar 2024 18:10:43 +0000 (11:10 -0700)
Pull sound updates from Takashi Iwai:
 "This was a relatively calm development cycle. Most of changes are
  rather small device-specific fixes and enhancements. The only
  significant changes in ALSA core are code refactoring with the recent
  cleanup infrastructure, which should bring no functionality changes.
  Some highlights below:

  Core:
   - Lots of cleanups in ALSA core code with automatic kfree cleanup and
     locking guard macros
   - New ALSA core kunit test

  ASoC:
   - SoundWire support for AMD ACP 6.3 systems
   - Support for reporting version information for AVS firmware
   - Support DSPless mode for Intel Soundwire systems
   - Support for configuring CS35L56 amplifiers using EFI calibration
     data
   - Log which component is being operated on as part of power
     management trace events.
   - Support for Microchip SAM9x7, NXP i.MX95 and Qualcomm WCD939x

  HD- and USB-audio:
   - More Cirrus HD-audio codec support
   - TAS2781 HD-audio codec fixes
   - Scarlett2 mixer fixes

  Others:
   - Enhancement of virtio driver for audio control supports
   - Cleanups of legacy PM code with new macros
   - Firewire sound updates"

* tag 'sound-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (307 commits)
  ALSA: usb-audio: Stop parsing channels bits when all channels are found.
  ALSA: hda/tas2781: remove unnecessary runtime_pm calls
  ALSA: hda/realtek - ALC236 fix volume mute & mic mute LED on some HP models
  ALSA: aaci: Delete unused variable in aaci_do_suspend
  ALSA: scarlett2: Fix Scarlett 4th Gen input gain range again
  ALSA: scarlett2: Fix Scarlett 4th Gen input gain range
  ALSA: scarlett2: Fix Scarlett 4th Gen autogain status values
  ALSA: scarlett2: Fix Scarlett 4th Gen 4i4 low-voltage detection
  ALSA: hda/tas2781: restore power state after system_resume
  ALSA: hda/tas2781: do not call pm_runtime_force_* in system_resume/suspend
  ALSA: hda/tas2781: do not reset cur_* values in runtime_suspend
  ALSA: hda/tas2781: add lock to system_suspend
  ALSA: hda/tas2781: use dev_dbg in system_resume
  ALSA: hda/realtek: fix ALC285 issues on HP Envy x360 laptops
  platform/x86: serial-multi-instantiate: Add support for CS35L54 and CS35L57
  ALSA: hda: cs35l56: Add support for CS35L54 and CS35L57
  ASoC: cs35l56: Add support for CS35L54 and CS35L57
  ASoC: Intel: catpt: Carefully use PCI bitwise constants
  ALSA: hda: hda_component: Include sound/hda_codec.h
  ALSA: hda: hda_component: Add missing #include guards
  ...

344 files changed:
Documentation/ABI/testing/sysfs-bus-pci-devices-avs [new file with mode: 0644]
Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/atmel,sama5d2-classd.yaml
Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt [deleted file]
Documentation/devicetree/bindings/sound/atmel-wm8904.txt [deleted file]
Documentation/devicetree/bindings/sound/audio-graph-port.yaml
Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
Documentation/devicetree/bindings/sound/everest,es8326.yaml
Documentation/devicetree/bindings/sound/fsl,asrc.txt [deleted file]
Documentation/devicetree/bindings/sound/fsl,easrc.yaml
Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl,micfil.yaml
Documentation/devicetree/bindings/sound/fsl,sai.yaml
Documentation/devicetree/bindings/sound/infineon,peb2466.yaml
Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml
Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml
Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
Documentation/devicetree/bindings/sound/realtek,rt1015.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt1015.txt [deleted file]
Documentation/devicetree/bindings/sound/samsung,tm2.yaml
Documentation/sound/kernel-api/writing-an-alsa-driver.rst
MAINTAINERS
drivers/acpi/scan.c
drivers/firmware/cirrus/cs_dsp.c
drivers/platform/x86/serial-multi-instantiate.c
drivers/soundwire/Makefile
drivers/soundwire/amd_init.c [new file with mode: 0644]
drivers/soundwire/amd_init.h [new file with mode: 0644]
drivers/soundwire/amd_manager.c
drivers/soundwire/amd_manager.h
drivers/soundwire/dmi-quirks.c
include/linux/firmware/cirrus/cs_dsp.h
include/linux/soundwire/sdw_amd.h
include/linux/spi/spi.h
include/sound/ak4531_codec.h
include/sound/cs-amp-lib.h [new file with mode: 0644]
include/sound/cs35l56.h
include/sound/cs42l42.h
include/sound/emux_synth.h
include/sound/hda-mlink.h
include/sound/hda_register.h
include/sound/pcm.h
include/sound/sb.h
include/sound/soc.h
include/sound/sof/dai-amd.h
include/sound/sof/dai.h
include/sound/tas2781.h
include/trace/events/asoc.h
include/uapi/linux/virtio_snd.h
include/uapi/sound/intel/avs/tokens.h
include/uapi/sound/sof/tokens.h
sound/aoa/fabrics/layout.c
sound/aoa/soundbus/core.c
sound/arm/aaci.c
sound/arm/pxa2xx-ac97.c
sound/core/Kconfig
sound/core/Makefile
sound/core/compress_offload.c
sound/core/control.c
sound/core/control_compat.c
sound/core/control_led.c
sound/core/hrtimer.c
sound/core/hwdep.c
sound/core/info.c
sound/core/info_oss.c
sound/core/init.c
sound/core/jack.c
sound/core/oss/mixer_oss.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/pcm_compat.c
sound/core/pcm_lib.c
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/core/rawmidi.c
sound/core/seq/Kconfig
sound/core/seq/oss/seq_oss_device.h
sound/core/seq/oss/seq_oss_init.c
sound/core/seq/oss/seq_oss_midi.c
sound/core/seq/seq_compat.c
sound/core/seq/seq_fifo.c
sound/core/seq/seq_memory.c
sound/core/seq/seq_midi.c
sound/core/seq/seq_midi_event.c
sound/core/seq/seq_ports.c
sound/core/seq/seq_prioq.c
sound/core/seq/seq_queue.c
sound/core/seq/seq_timer.c
sound/core/seq/seq_ump_client.c
sound/core/seq/seq_virmidi.c
sound/core/seq_device.c
sound/core/sound.c
sound/core/sound_kunit.c [new file with mode: 0644]
sound/core/sound_oss.c
sound/core/timer.c
sound/core/timer_compat.c
sound/core/ump.c
sound/core/vmaster.c
sound/drivers/aloop.c
sound/drivers/dummy.c
sound/drivers/pcsp/pcsp.c
sound/firewire/Kconfig
sound/firewire/amdtp-stream.c
sound/firewire/amdtp-stream.h
sound/firewire/motu/motu-protocol-v3.c
sound/firewire/motu/motu.c
sound/firewire/motu/motu.h
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/hda/hdac_i915.c
sound/hda/hdac_stream.c
sound/hda/intel-sdw-acpi.c
sound/pci/ac97/ac97_patch.c
sound/pci/ali5451/ali5451.c
sound/pci/als300.c
sound/pci/als4000.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/aw2/aw2-saa7146.h
sound/pci/azt3328.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/ctxfi/ctamixer.c
sound/pci/ctxfi/ctamixer.h
sound/pci/ctxfi/ctatc.c
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/ctdaio.h
sound/pci/ctxfi/ctsrc.c
sound/pci/ctxfi/ctsrc.h
sound/pci/echoaudio/echoaudio.c
sound/pci/echoaudio/echoaudio.h
sound/pci/echoaudio/echoaudio_3g.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/cs35l41_hda_property.c
sound/pci/hda/cs35l56_hda.c
sound/pci/hda/cs35l56_hda.h
sound/pci/hda/cs35l56_hda_i2c.c
sound/pci/hda/cs35l56_hda_spi.c
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_component.c [new file with mode: 0644]
sound/pci/hda/hda_component.h
sound/pci/hda/hda_controller.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/tas2781_hda_i2c.c
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/maestro3.c
sound/pci/nm256/nm256.c
sound/pci/riptide/riptide.c
sound/pci/rme96.c
sound/pci/sis7019.c
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/soc/amd/Kconfig
sound/soc/amd/Makefile
sound/soc/amd/acp/Kconfig
sound/soc/amd/acp/Makefile
sound/soc/amd/acp/acp-mach-common.c
sound/soc/amd/acp/acp-sof-mach.c
sound/soc/amd/acp/amd-sdw-acpi.c [new file with mode: 0644]
sound/soc/amd/ps/Makefile
sound/soc/amd/ps/acp63.h
sound/soc/amd/ps/pci-ps.c
sound/soc/amd/ps/ps-mach.c
sound/soc/amd/ps/ps-pdm-dma.c
sound/soc/amd/ps/ps-sdw-dma.c
sound/soc/atmel/mikroe-proto.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/cs-amp-lib-test.c [new file with mode: 0644]
sound/soc/codecs/cs-amp-lib.c [new file with mode: 0644]
sound/soc/codecs/cs35l56-sdw.c
sound/soc/codecs/cs35l56-shared.c
sound/soc/codecs/cs35l56.c
sound/soc/codecs/cs42l42.c
sound/soc/codecs/cs42l43-jack.c
sound/soc/codecs/cs42l43-sdw.c
sound/soc/codecs/cs42l43.c
sound/soc/codecs/cs42l43.h
sound/soc/codecs/da7213.c
sound/soc/codecs/es8326.c
sound/soc/codecs/es8326.h
sound/soc/codecs/framer-codec.c [new file with mode: 0644]
sound/soc/codecs/hda.c
sound/soc/codecs/lpass-macro-common.h
sound/soc/codecs/lpass-tx-macro.c
sound/soc/codecs/lpass-va-macro.c
sound/soc/codecs/max98363.c
sound/soc/codecs/max98373-sdw.c
sound/soc/codecs/nau8540.c
sound/soc/codecs/nau8540.h
sound/soc/codecs/nau8825.c
sound/soc/codecs/rt1017-sdca-sdw.c
sound/soc/codecs/rt274.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5514-spi.c
sound/soc/codecs/rt5514.c
sound/soc/codecs/rt5616.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5651.c
sound/soc/codecs/rt5659.c
sound/soc/codecs/rt5660.c
sound/soc/codecs/rt5663.c
sound/soc/codecs/rt5665.c
sound/soc/codecs/rt5668.c
sound/soc/codecs/rt5682-i2c.c
sound/soc/codecs/rt5682s.c
sound/soc/codecs/rt712-sdca-dmic.c
sound/soc/codecs/rt712-sdca-sdw.c
sound/soc/codecs/rt722-sdca-sdw.c
sound/soc/codecs/tas2781-comlib.c
sound/soc/codecs/tas2781-fmwlib.c
sound/soc/codecs/wcd-clsh-v2.h
sound/soc/codecs/wcd-mbhc-v2.c
sound/soc/codecs/wcd-mbhc-v2.h
sound/soc/codecs/wcd939x-sdw.c [new file with mode: 0644]
sound/soc/codecs/wcd939x.c [new file with mode: 0644]
sound/soc/codecs/wcd939x.h [new file with mode: 0644]
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wsa884x.c
sound/soc/fsl/eukrea-tlv320.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/p1022_rdk.c
sound/soc/intel/atom/sst/sst_ipc.c
sound/soc/intel/avs/Makefile
sound/soc/intel/avs/apl.c
sound/soc/intel/avs/avs.h
sound/soc/intel/avs/board_selection.c
sound/soc/intel/avs/cnl.c [new file with mode: 0644]
sound/soc/intel/avs/core.c
sound/soc/intel/avs/icl.c [new file with mode: 0644]
sound/soc/intel/avs/ipc.c
sound/soc/intel/avs/loader.c
sound/soc/intel/avs/messages.c
sound/soc/intel/avs/messages.h
sound/soc/intel/avs/path.c
sound/soc/intel/avs/pcm.c
sound/soc/intel/avs/registers.h
sound/soc/intel/avs/skl.c
sound/soc/intel/avs/sysfs.c [new file with mode: 0644]
sound/soc/intel/avs/tgl.c [new file with mode: 0644]
sound/soc/intel/avs/topology.c
sound/soc/intel/avs/topology.h
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/sof_board_helpers.c
sound/soc/intel/boards/sof_board_helpers.h
sound/soc/intel/boards/sof_cs42l42.c
sound/soc/intel/boards/sof_rt5682.c
sound/soc/intel/boards/sof_sdw.c
sound/soc/intel/boards/sof_sdw_common.h
sound/soc/intel/boards/sof_sdw_cs42l42.c
sound/soc/intel/boards/sof_sdw_cs42l43.c
sound/soc/intel/boards/sof_sdw_cs_amp.c
sound/soc/intel/boards/sof_sdw_maxim.c
sound/soc/intel/boards/sof_sdw_rt5682.c
sound/soc/intel/boards/sof_sdw_rt700.c
sound/soc/intel/boards/sof_sdw_rt711.c
sound/soc/intel/boards/sof_sdw_rt712_sdca.c
sound/soc/intel/boards/sof_sdw_rt715.c
sound/soc/intel/boards/sof_sdw_rt715_sdca.c
sound/soc/intel/boards/sof_sdw_rt_amp.c
sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
sound/soc/intel/boards/sof_ssp_common.h
sound/soc/intel/catpt/dsp.c
sound/soc/intel/common/soc-acpi-intel-adl-match.c
sound/soc/intel/common/soc-acpi-intel-jsl-match.c
sound/soc/intel/common/soc-acpi-intel-lnl-match.c
sound/soc/intel/common/soc-acpi-intel-mtl-match.c
sound/soc/intel/common/soc-acpi-intel-rpl-match.c
sound/soc/intel/common/soc-acpi-intel-tgl-match.c
sound/soc/meson/aiu.c
sound/soc/meson/aiu.h
sound/soc/meson/axg-fifo.c
sound/soc/meson/axg-fifo.h
sound/soc/meson/axg-frddr.c
sound/soc/meson/axg-spdifin.c
sound/soc/meson/axg-tdm-interface.c
sound/soc/meson/axg-toddr.c
sound/soc/meson/t9015.c
sound/soc/pxa/Kconfig
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/qcom/common.c
sound/soc/sh/rz-ssi.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/sof/amd/Kconfig
sound/soc/sof/amd/acp-common.c
sound/soc/sof/amd/acp-dsp-offset.h
sound/soc/sof/amd/acp-loader.c
sound/soc/sof/amd/acp.c
sound/soc/sof/amd/acp.h
sound/soc/sof/amd/pci-acp63.c
sound/soc/sof/core.c
sound/soc/sof/debug.c
sound/soc/sof/fw-file-profile.c
sound/soc/sof/imx/imx8.c
sound/soc/sof/imx/imx8m.c
sound/soc/sof/imx/imx8ulp.c
sound/soc/sof/intel/hda-common-ops.c
sound/soc/sof/intel/hda-dai-ops.c
sound/soc/sof/intel/hda-dai.c
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda-stream.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/lnl.c
sound/soc/sof/ipc3-loader.c
sound/soc/sof/ipc3-pcm.c
sound/soc/sof/ipc3-topology.c
sound/soc/sof/ipc4-pcm.c
sound/soc/sof/ipc4-priv.h
sound/soc/sof/ipc4-topology.c
sound/soc/sof/ops.h
sound/soc/sof/sof-audio.c
sound/soc/sof/sof-audio.h
sound/soc/sof/sof-priv.h
sound/soc/sof/topology.c
sound/soc/ti/j721e-evm.c
sound/soc/ti/omap-hdmi.c
sound/spi/at73c213.c
sound/synth/emux/emux.c
sound/usb/mixer.c
sound/usb/mixer_scarlett2.c
sound/usb/stream.c
sound/virtio/Makefile
sound/virtio/virtio_card.c
sound/virtio/virtio_card.h
sound/virtio/virtio_kctl.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-avs b/Documentation/ABI/testing/sysfs-bus-pci-devices-avs
new file mode 100644 (file)
index 0000000..ebff3fa
--- /dev/null
@@ -0,0 +1,8 @@
+What:          /sys/devices/pci0000:00/<dev>/avs/fw_version
+Date:          February 2024
+Contact:       Cezary Rojewski <cezary.rojewski@intel.com>
+Description:
+               Version of AudioDSP firmware ASoC avs driver is communicating
+               with.
+
+               Format: %d.%d.%d.%d, type:major:minor:build.
diff --git a/Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml b/Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml
new file mode 100644 (file)
index 0000000..89a67f8
--- /dev/null
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/atmel,asoc-wm8904.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Atmel wm8904 audio codec complex
+
+maintainers:
+  - Dharma Balasubiramani <dharma.b@microchip.com>
+
+description:
+  The ASoC audio complex configuration for Atmel with WM8904 audio codec.
+
+properties:
+  compatible:
+    const: atmel,asoc-wm8904
+
+  atmel,model:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: The user-visible name of this sound complex.
+
+  atmel,ssc-controller:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of the SSC controller.
+
+  atmel,audio-codec:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of the WM8731 audio codec.
+
+  atmel,audio-routing:
+    description:
+      A list of the connections between audio components. Each entry is a pair
+      of strings, the first being the connection's sink, the second being the
+      connection's source.
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    items:
+      enum:
+        # Board Connectors
+        - Headphone Jack
+        - Line In Jack
+        - Mic
+        # WM8904 CODEC Pins
+        - IN1L
+        - IN1R
+        - IN2L
+        - IN2R
+        - IN3L
+        - IN3R
+        - HPOUTL
+        - HPOUTR
+        - LINEOUTL
+        - LINEOUTR
+        - MICBIAS
+
+required:
+  - compatible
+  - atmel,model
+  - atmel,audio-routing
+  - atmel,ssc-controller
+  - atmel,audio-codec
+
+additionalProperties: false
+
+examples:
+  - |
+    sound {
+        compatible = "atmel,asoc-wm8904";
+        pinctrl-names = "default";
+        pinctrl-0 = <&pinctrl_pck0_as_mck>;
+
+        atmel,model = "wm8904 @ AT91SAM9N12EK";
+
+        atmel,audio-routing =
+                "Headphone Jack", "HPOUTL",
+                "Headphone Jack", "HPOUTR",
+                "IN2L", "Line In Jack",
+                "IN2R", "Line In Jack",
+                "Mic", "MICBIAS",
+                "IN1L", "Mic";
+
+        atmel,ssc-controller = <&ssc0>;
+        atmel,audio-codec = <&wm8904>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml b/Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml
new file mode 100644 (file)
index 0000000..33717b7
--- /dev/null
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/atmel,sam9x5-wm8731-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Atmel at91sam9x5ek wm8731 audio complex
+
+maintainers:
+  - Dharma Balasubiramani <dharma.b@microchip.com>
+
+description:
+  The audio complex configuration for Atmel at91sam9x5ek with WM8731 audio codec.
+
+properties:
+  compatible:
+    const: atmel,sam9x5-wm8731-audio
+
+  atmel,model:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: The user-visible name of this sound complex.
+
+  atmel,ssc-controller:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of the SSC controller.
+
+  atmel,audio-codec:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of the WM8731 audio codec.
+
+  atmel,audio-routing:
+    description:
+      A list of the connections between audio components. Each entry is a pair
+      of strings, the first being the connection's sink, the second being the
+      connection's source.
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    items:
+      enum:
+        # Board Connectors
+        - Headphone Jack
+        - Line In Jack
+
+        # CODEC Pins
+        - LOUT
+        - ROUT
+        - LHPOUT
+        - RHPOUT
+        - LLINEIN
+        - RLINEIN
+        - MICIN
+
+required:
+  - compatible
+  - atmel,model
+  - atmel,ssc-controller
+  - atmel,audio-codec
+  - atmel,audio-routing
+
+additionalProperties: false
+
+examples:
+  - |
+    sound {
+        compatible = "atmel,sam9x5-wm8731-audio";
+
+        atmel,model = "wm8731 @ AT91SAM9X5EK";
+
+        atmel,audio-routing =
+                "Headphone Jack", "RHPOUT",
+                "Headphone Jack", "LHPOUT",
+                "LLINEIN", "Line In Jack",
+                "RLINEIN", "Line In Jack";
+
+        atmel,ssc-controller = <&ssc0>;
+        atmel,audio-codec = <&wm8731>;
+    };
index 43d04702ac2d8867ee6ba8c5264453cfbaec2f48..ae3162fcfe02f7fcbfc4454a12318464468a9ffa 100644 (file)
@@ -18,7 +18,12 @@ description:
 
 properties:
   compatible:
-    const: atmel,sama5d2-classd
+    oneOf:
+      - items:
+          - const: atmel,sama5d2-classd
+      - items:
+          - const: microchip,sam9x7-classd
+          - const: atmel,sama5d2-classd
 
   reg:
     maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt
deleted file mode 100644 (file)
index 8facbce..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-* Atmel at91sam9x5ek wm8731 audio complex
-
-Required properties:
-  - compatible: "atmel,sam9x5-wm8731-audio"
-  - atmel,model: The user-visible name of this sound complex.
-  - atmel,ssc-controller: The phandle of the SSC controller
-  - atmel,audio-codec: The phandle of the WM8731 audio codec
-  - atmel,audio-routing: A list of the connections between audio components.
-    Each entry is a pair of strings, the first being the connection's sink,
-    the second being the connection's source.
-
-Available audio endpoints for the audio-routing table:
-
-Board connectors:
- * Headphone Jack
- * Line In Jack
-
-wm8731 pins:
-cf Documentation/devicetree/bindings/sound/wlf,wm8731.yaml
-
-Example:
-sound {
-       compatible = "atmel,sam9x5-wm8731-audio";
-
-       atmel,model = "wm8731 @ AT91SAM9X5EK";
-
-       atmel,audio-routing =
-               "Headphone Jack", "RHPOUT",
-               "Headphone Jack", "LHPOUT",
-               "LLINEIN", "Line In Jack",
-               "RLINEIN", "Line In Jack";
-
-       atmel,ssc-controller = <&ssc0>;
-       atmel,audio-codec = <&wm8731>;
-};
diff --git a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt
deleted file mode 100644 (file)
index 8bbe50c..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Atmel ASoC driver with wm8904 audio codec complex
-
-Required properties:
-  - compatible: "atmel,asoc-wm8904"
-  - atmel,model: The user-visible name of this sound complex.
-  - atmel,audio-routing: A list of the connections between audio components.
-    Each entry is a pair of strings, the first being the connection's sink,
-    the second being the connection's source. Valid names for sources and
-    sinks are the WM8904's pins, and the jacks on the board:
-
-    WM8904 pins:
-
-    * IN1L
-    * IN1R
-    * IN2L
-    * IN2R
-    * IN3L
-    * IN3R
-    * HPOUTL
-    * HPOUTR
-    * LINEOUTL
-    * LINEOUTR
-    * MICBIAS
-
-    Board connectors:
-
-    * Headphone Jack
-    * Line In Jack
-    * Mic
-
-  - atmel,ssc-controller: The phandle of the SSC controller
-  - atmel,audio-codec: The phandle of the WM8904 audio codec
-
-Optional properties:
-  - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
-
-Example:
-sound {
-       compatible = "atmel,asoc-wm8904";
-       pinctrl-names = "default";
-       pinctrl-0 = <&pinctrl_pck0_as_mck>;
-
-       atmel,model = "wm8904 @ AT91SAM9N12EK";
-
-       atmel,audio-routing =
-               "Headphone Jack", "HPOUTL",
-               "Headphone Jack", "HPOUTR",
-               "IN2L", "Line In Jack",
-               "IN2R", "Line In Jack",
-               "Mic", "MICBIAS",
-               "IN1L", "Mic";
-
-       atmel,ssc-controller = <&ssc0>;
-       atmel,audio-codec = <&wm8904>;
-};
index b13c08de505e41480732c2060df05c8081b17d31..28b27e7e45de6beb5037d6e7e3d616e3d4188294 100644 (file)
@@ -51,7 +51,7 @@ definitions:
           - $ref: /schemas/types.yaml#/definitions/phandle
       clocks:
         description: Indicates system clock
-        $ref: /schemas/types.yaml#/definitions/phandle
+        maxItems: 1
       system-clock-frequency:
         $ref: simple-card.yaml#/definitions/system-clock-frequency
       system-clock-direction-out:
index 4c9acb8d4c4c6174a0b4f84f05e36550c5c92792..70f6c62aedca84920b8f50cc20c90e53e44b6011 100644 (file)
@@ -25,6 +25,9 @@ properties:
   reg:
     maxItems: 1
 
+  interrupts:
+    maxItems: 1
+
   '#sound-dai-cells':
     const: 1
 
index 07781408e78820ad49d093d4bddcfd14eeadfa39..8c82d47375ec769a970d1cae483f2f1c86539cd4 100644 (file)
@@ -38,6 +38,7 @@ properties:
     default: 0x0f
 
   everest,mic1-src:
+    deprecated: true
     $ref: /schemas/types.yaml#/definitions/uint8
     description:
       the value of reg 2A when headset plugged.
@@ -46,6 +47,7 @@ properties:
     default: 0x22
 
   everest,mic2-src:
+    deprecated: true
     $ref: /schemas/types.yaml#/definitions/uint8
     description:
       the value of reg 2A when headset unplugged.
@@ -87,7 +89,7 @@ properties:
        0 means the chip detect jack type again after button released.
     minimum: 0
     maximum: 0x7f
-    default: 0x45
+    default: 0x00
 
 required:
   - compatible
@@ -107,10 +109,8 @@ examples:
         clocks = <&clks 10>;
         clock-names = "mclk";
         #sound-dai-cells = <0>;
-        everest,mic1-src = [22];
-        everest,mic2-src = [44];
         everest,jack-pol = [0e];
         everest,interrupt-src = [08];
-        everest,interrupt-clk = [45];
+        everest,interrupt-clk = [00];
       };
     };
diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
deleted file mode 100644 (file)
index 998b4c8..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-Freescale Asynchronous Sample Rate Converter (ASRC) Controller
-
-The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
-signal associated with an input clock into a signal associated with a different
-output clock. The driver currently works as a Front End of DPCM with other Back
-Ends Audio controller such as ESAI, SSI and SAI. It has three pairs to support
-three substreams within totally 10 channels.
-
-Required properties:
-
-  - compatible         : Compatible list, should contain one of the following
-                         compatibles:
-                         "fsl,imx35-asrc",
-                         "fsl,imx53-asrc",
-                         "fsl,imx8qm-asrc",
-                         "fsl,imx8qxp-asrc",
-
-  - reg                        : Offset and length of the register set for the device.
-
-  - interrupts         : Contains the spdif interrupt.
-
-  - dmas               : Generic dma devicetree binding as described in
-                         Documentation/devicetree/bindings/dma/dma.txt.
-
-  - dma-names          : Contains "rxa", "rxb", "rxc", "txa", "txb" and "txc".
-
-  - clocks             : Contains an entry for each entry in clock-names.
-
-  - clock-names                : Contains the following entries
-       "mem"             Peripheral access clock to access registers.
-       "ipg"             Peripheral clock to driver module.
-       "asrck_<0-f>"     Clock sources for input and output clock.
-       "spba"            The spba clock is required when ASRC is placed as a
-                         bus slave of the Shared Peripheral Bus and when two
-                         or more bus masters (CPU, DMA or DSP) try to access
-                         it. This property is optional depending on the SoC
-                         design.
-
-   - fsl,asrc-rate     : Defines a mutual sample rate used by DPCM Back Ends.
-
-   - fsl,asrc-width    : Defines a mutual sample width used by DPCM Back Ends.
-
-   - fsl,asrc-clk-map   : Defines clock map used in driver. which is required
-                         by imx8qm/imx8qxp platform
-                         <0> - select the map for asrc0 in imx8qm/imx8qxp
-                         <1> - select the map for asrc1 in imx8qm/imx8qxp
-
-Optional properties:
-
-   - big-endian                : If this property is absent, the little endian mode
-                         will be in use as default. Otherwise, the big endian
-                         mode will be in use for all the device registers.
-
-   - fsl,asrc-format   : Defines a mutual sample format used by DPCM Back
-                         Ends, which can replace the fsl,asrc-width.
-                         The value is 2 (S16_LE), or 6 (S24_LE).
-
-Example:
-
-asrc: asrc@2034000 {
-       compatible = "fsl,imx53-asrc";
-       reg = <0x02034000 0x4000>;
-       interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
-       clocks = <&clks 107>, <&clks 107>, <&clks 0>,
-              <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
-              <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
-              <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
-              <&clks 107>, <&clks 0>, <&clks 0>;
-       clock-names = "mem", "ipg", "asrck0",
-               "asrck_1", "asrck_2", "asrck_3", "asrck_4",
-               "asrck_5", "asrck_6", "asrck_7", "asrck_8",
-               "asrck_9", "asrck_a", "asrck_b", "asrck_c",
-               "asrck_d", "asrck_e", "asrck_f";
-       dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,
-            <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
-       dma-names = "rxa", "rxb", "rxc",
-               "txa", "txb", "txc";
-       fsl,asrc-rate  = <48000>;
-       fsl,asrc-width = <16>;
-};
index a680d7aff2373ca4bb7fad93b6f9667cbc3d2289..0782f3f9947f85b58aa86ca5d654afc123d8316b 100644 (file)
@@ -51,8 +51,8 @@ properties:
       - const: ctx3_tx
 
   firmware-name:
-    $ref: /schemas/types.yaml#/definitions/string
-    const: imx/easrc/easrc-imx8mn.bin
+    items:
+      - const: imx/easrc/easrc-imx8mn.bin
     description: The coefficient table for the filters
 
   fsl,asrc-rate:
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
new file mode 100644 (file)
index 0000000..bfef2fc
--- /dev/null
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl,imx-asrc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Asynchronous Sample Rate Converter (ASRC) Controller
+
+description:
+  The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of
+  a signal associated with an input clock into a signal associated with a
+  different output clock. The driver currently works as a Front End of DPCM
+  with other Back Ends Audio controller such as ESAI, SSI and SAI. It has
+  three pairs to support three substreams within totally 10 channels.
+
+maintainers:
+  - Shawn Guo <shawnguo@kernel.org>
+  - Sascha Hauer <s.hauer@pengutronix.de>
+
+properties:
+  compatible:
+    oneOf:
+      - enum:
+          - fsl,imx35-asrc
+          - fsl,imx53-asrc
+          - fsl,imx8qm-asrc
+          - fsl,imx8qxp-asrc
+      - items:
+          - enum:
+              - fsl,imx6sx-asrc
+              - fsl,imx6ul-asrc
+          - const: fsl,imx53-asrc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  dmas:
+    maxItems: 6
+
+  dma-names:
+    items:
+      - const: rxa
+      - const: rxb
+      - const: rxc
+      - const: txa
+      - const: txb
+      - const: txc
+
+  clocks:
+    maxItems: 19
+
+  clock-names:
+    items:
+      - const: mem
+      - const: ipg
+      - const: asrck_0
+      - const: asrck_1
+      - const: asrck_2
+      - const: asrck_3
+      - const: asrck_4
+      - const: asrck_5
+      - const: asrck_6
+      - const: asrck_7
+      - const: asrck_8
+      - const: asrck_9
+      - const: asrck_a
+      - const: asrck_b
+      - const: asrck_c
+      - const: asrck_d
+      - const: asrck_e
+      - const: asrck_f
+      - const: spba
+
+  fsl,asrc-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: The mutual sample rate used by DPCM Back Ends
+
+  fsl,asrc-width:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: The mutual sample width used by DPCM Back Ends
+    enum: [16, 24]
+
+  fsl,asrc-clk-map:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Defines clock map used in driver
+      <0> - select the map for asrc0 in imx8qm/imx8qxp
+      <1> - select the map for asrc1 in imx8qm/imx8qxp
+    enum: [0, 1]
+
+  big-endian:
+    type: boolean
+    description:
+      If this property is absent, the little endian mode will be in use as
+      default. Otherwise, the big endian mode will be in use for all the
+      device registers.
+
+  fsl,asrc-format:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Defines a mutual sample format used by DPCM Back Ends, which can
+      replace the fsl,asrc-width. The value is 2 (S16_LE), or 6 (S24_LE).
+    enum: [2, 6]
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - dmas
+  - dma-names
+  - clocks
+  - clock-names
+  - fsl,asrc-rate
+  - fsl,asrc-width
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - fsl,imx8qm-asrc
+              - fsl,imx8qxp-asrc
+    then:
+      required:
+        - fsl,asrc-clk-map
+    else:
+      properties:
+        fsl,asrc-clk-map: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/clock/imx6qdl-clock.h>
+    asrc: asrc@2034000 {
+        compatible = "fsl,imx53-asrc";
+        reg = <0x02034000 0x4000>;
+        interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clks IMX6QDL_CLK_ASRC_IPG>,
+                 <&clks IMX6QDL_CLK_ASRC_MEM>, <&clks 0>,
+                 <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+                 <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+                 <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+                 <&clks IMX6QDL_CLK_ASRC>, <&clks 0>, <&clks 0>,
+                 <&clks IMX6QDL_CLK_SPBA>;
+        clock-names = "mem", "ipg", "asrck_0",
+                "asrck_1", "asrck_2", "asrck_3", "asrck_4",
+                "asrck_5", "asrck_6", "asrck_7", "asrck_8",
+                "asrck_9", "asrck_a", "asrck_b", "asrck_c",
+                "asrck_d", "asrck_e", "asrck_f", "spba";
+        dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,
+               <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
+        dma-names = "rxa", "rxb", "rxc",
+                    "txa", "txb", "txc";
+        fsl,asrc-rate  = <48000>;
+        fsl,asrc-width = <16>;
+    };
index b7e60583563918001b8333b7e473a15a8ddca172..c1e9803fc113c0a8b2a75b38fff6df9a4d758f58 100644 (file)
@@ -15,10 +15,16 @@ description: |
 
 properties:
   compatible:
-    enum:
-      - fsl,imx8mm-micfil
-      - fsl,imx8mp-micfil
-      - fsl,imx93-micfil
+    oneOf:
+      - items:
+          - enum:
+              - fsl,imx95-micfil
+          - const: fsl,imx93-micfil
+
+      - enum:
+          - fsl,imx8mm-micfil
+          - fsl,imx8mp-micfil
+          - fsl,imx93-micfil
 
   reg:
     maxItems: 1
index 088c26b001cc025c097eda937524ea43fa80fa9b..2456d958adeef6ac217e5698f7b0d89c702e7ad7 100644 (file)
@@ -39,6 +39,7 @@ properties:
               - fsl,imx8qm-sai
               - fsl,imx8ulp-sai
               - fsl,imx93-sai
+              - fsl,imx95-sai
               - fsl,vf610-sai
 
   reg:
@@ -75,12 +76,17 @@ properties:
           - const: pll11k
         minItems: 4
 
+  power-domains:
+    maxItems: 1
+
   dmas:
+    minItems: 1
     items:
       - description: DMA controller phandle and request line for RX
       - description: DMA controller phandle and request line for TX
 
   dma-names:
+    minItems: 1
     items:
       - const: rx
       - const: tx
index 66993d378aaf59ff47ae9b867787cdf95fa70fe3..5e11ce2c13aca1649638e9f98957d16527ebb974 100644 (file)
@@ -51,7 +51,7 @@ properties:
     maxItems: 1
 
   firmware-name:
-    $ref: /schemas/types.yaml#/definitions/string
+    maxItems: 1
     description:
       Filters coefficients file to load. If this property is omitted, internal
       filters are disabled.
index 651f61c7c25a00eef4d0651fdf2a2179c349b5cd..fb630a184350bdcc1ce9401066ad86903d78526f 100644 (file)
@@ -24,9 +24,14 @@ properties:
     const: 0
 
   compatible:
-    enum:
-      - microchip,sam9x60-i2smcc
-      - microchip,sama7g5-i2smcc
+    oneOf:
+      - enum:
+          - microchip,sam9x60-i2smcc
+          - microchip,sama7g5-i2smcc
+      - items:
+          - enum:
+              - microchip,sam9x7-i2smcc
+          - const: microchip,sam9x60-i2smcc
 
   reg:
     maxItems: 1
index 6f419747273e373e72223ebe7dbf76da0b00fccd..2ab6871e89e5e03eb34071edb9f11c471533cd60 100644 (file)
@@ -107,7 +107,7 @@ patternProperties:
         properties:
           sound-dai:
             minItems: 1
-            maxItems: 4
+            maxItems: 8
 
     required:
       - link-name
index adbfa67f88ed93b99c28a0545e2c40eb2818fac2..cf6c3787adfeff846a13391775fa188c15d17e33 100644 (file)
@@ -15,6 +15,7 @@ description: |
 
 allOf:
   - $ref: dai-common.yaml#
+  - $ref: qcom,wcd93xx-common.yaml#
 
 properties:
   compatible:
@@ -22,92 +23,12 @@ properties:
       - qcom,wcd9380-codec
       - qcom,wcd9385-codec
 
-  reset-gpios:
-    description: GPIO spec for reset line to use
-    maxItems: 1
-
   us-euro-gpios:
     description: GPIO spec for swapping gnd and mic segments
     maxItems: 1
 
-  vdd-buck-supply:
-    description: A reference to the 1.8V buck supply
-
-  vdd-rxtx-supply:
-    description: A reference to the 1.8V rx supply
-
-  vdd-io-supply:
-    description: A reference to the 1.8V I/O supply
-
-  vdd-mic-bias-supply:
-    description: A reference to the 3.8V mic bias supply
-
-  qcom,tx-device:
-    $ref: /schemas/types.yaml#/definitions/phandle-array
-    description: A reference to Soundwire tx device phandle
-
-  qcom,rx-device:
-    $ref: /schemas/types.yaml#/definitions/phandle-array
-    description: A reference to Soundwire rx device phandle
-
-  qcom,micbias1-microvolt:
-    description: micbias1 voltage
-    minimum: 1800000
-    maximum: 2850000
-
-  qcom,micbias2-microvolt:
-    description: micbias2 voltage
-    minimum: 1800000
-    maximum: 2850000
-
-  qcom,micbias3-microvolt:
-    description: micbias3 voltage
-    minimum: 1800000
-    maximum: 2850000
-
-  qcom,micbias4-microvolt:
-    description: micbias4 voltage
-    minimum: 1800000
-    maximum: 2850000
-
-  qcom,hphl-jack-type-normally-closed:
-    description: Indicates that HPHL jack switch type is normally closed
-    type: boolean
-
-  qcom,ground-jack-type-normally-closed:
-    description: Indicates that Headset Ground switch type is normally closed
-    type: boolean
-
-  qcom,mbhc-headset-vthreshold-microvolt:
-    description: Voltage threshold value for headset detection
-    minimum: 0
-    maximum: 2850000
-
-  qcom,mbhc-headphone-vthreshold-microvolt:
-    description: Voltage threshold value for headphone detection
-    minimum: 0
-    maximum: 2850000
-
-  qcom,mbhc-buttons-vthreshold-microvolt:
-    description:
-      Array of 8 Voltage threshold values corresponding to headset
-      button0 - button7
-    minItems: 8
-    maxItems: 8
-
-  '#sound-dai-cells':
-    const: 1
-
 required:
   - compatible
-  - reset-gpios
-  - qcom,tx-device
-  - qcom,rx-device
-  - qcom,micbias1-microvolt
-  - qcom,micbias2-microvolt
-  - qcom,micbias3-microvolt
-  - qcom,micbias4-microvolt
-  - "#sound-dai-cells"
 
 unevaluatedProperties: false
 
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml
new file mode 100644 (file)
index 0000000..67ed770
--- /dev/null
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wcd939x-sdw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SoundWire devices on WCD9390/WCD9395
+
+maintainers:
+  - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description: |
+  Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC.
+  It has RX and TX Soundwire devices. This bindings is for the devices.
+
+properties:
+  compatible:
+    const: sdw20217010e00
+
+  reg:
+    maxItems: 1
+
+  qcom,tx-port-mapping:
+    description: |
+      Specifies static port mapping between device and host tx ports.
+      In the order of the device port index.
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 4
+    maxItems: 4
+
+  qcom,rx-port-mapping:
+    description: |
+      Specifies static port mapping between device and host rx ports.
+      In the order of device port index.
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 6
+    maxItems: 6
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    soundwire@3210000 {
+        #address-cells = <2>;
+        #size-cells = <0>;
+        reg = <0x03210000 0x2000>;
+        wcd938x_rx: codec@0,4 {
+            compatible = "sdw20217010e00";
+            reg = <0 4>;
+            qcom,rx-port-mapping = <1 2 3 4 5 6>;
+        };
+    };
+
+    soundwire@3230000 {
+        #address-cells = <2>;
+        #size-cells = <0>;
+        reg = <0x03230000 0x2000>;
+        wcd938x_tx: codec@0,3 {
+            compatible = "sdw20217010e00";
+            reg = <0 3>;
+            qcom,tx-port-mapping = <2 3 4 5>;
+        };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml
new file mode 100644 (file)
index 0000000..6e76f6a
--- /dev/null
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wcd939x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm WCD9380/WCD9385 Audio Codec
+
+maintainers:
+  - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description: |
+  Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC.
+  It has RX and TX Soundwire devices.
+  The WCD9390/WCD9395 IC has a functionally separate USB-C Mux subsystem
+  accessible over an I2C interface.
+  The Audio Headphone and Microphone data path between the Codec and the USB-C Mux
+  subsystems are external to the IC, thus requiring DT port-endpoint graph description
+  to handle USB-C altmode & orientation switching for Audio Accessory Mode.
+
+allOf:
+  - $ref: dai-common.yaml#
+  - $ref: qcom,wcd93xx-common.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      - const: qcom,wcd9390-codec
+      - items:
+          - const: qcom,wcd9395-codec
+          - const: qcom,wcd9390-codec
+
+  mode-switch:
+    description: Flag the port as possible handler of altmode switching
+    type: boolean
+
+  orientation-switch:
+    description: Flag the port as possible handler of orientation switching
+    type: boolean
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description:
+      A port node to link the WCD939x Codec node to USB MUX subsystems for the
+      purpose of handling altmode muxing and orientation switching to detect and
+      enable Audio Accessory Mode.
+
+required:
+  - compatible
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    codec {
+        compatible = "qcom,wcd9390-codec";
+        reset-gpios = <&tlmm 32 IRQ_TYPE_NONE>;
+        #sound-dai-cells = <1>;
+        qcom,tx-device = <&wcd939x_tx>;
+        qcom,rx-device = <&wcd939x_rx>;
+        qcom,micbias1-microvolt = <1800000>;
+        qcom,micbias2-microvolt = <1800000>;
+        qcom,micbias3-microvolt = <1800000>;
+        qcom,micbias4-microvolt = <1800000>;
+        qcom,hphl-jack-type-normally-closed;
+        qcom,ground-jack-type-normally-closed;
+        qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>;
+        qcom,mbhc-headphone-vthreshold-microvolt = <50000>;
+    };
+
+    /* ... */
+
+    soundwire@3210000 {
+        #address-cells = <2>;
+        #size-cells = <0>;
+        reg = <0x03210000 0x2000>;
+        wcd939x_rx: codec@0,4 {
+            compatible = "sdw20217010e00";
+            reg = <0 4>;
+            qcom,rx-port-mapping = <1 2 3 4 5 6>;
+        };
+    };
+
+    soundwire@3230000 {
+        #address-cells = <2>;
+        #size-cells = <0>;
+        reg = <0x03230000 0x2000>;
+        wcd938x_tx: codec@0,3 {
+            compatible = "sdw20217010e00";
+            reg = <0 3>;
+            qcom,tx-port-mapping = <2 3 4 5>;
+        };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml
new file mode 100644 (file)
index 0000000..f78ba14
--- /dev/null
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wcd93xx-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Common properties for Qualcomm WCD93xx Audio Codec
+
+maintainers:
+  - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+properties:
+  reset-gpios:
+    description: GPIO spec for reset line to use
+    maxItems: 1
+
+  vdd-buck-supply:
+    description: A reference to the 1.8V buck supply
+
+  vdd-rxtx-supply:
+    description: A reference to the 1.8V rx supply
+
+  vdd-io-supply:
+    description: A reference to the 1.8V I/O supply
+
+  vdd-mic-bias-supply:
+    description: A reference to the 3.8V mic bias supply
+
+  qcom,tx-device:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: A reference to Soundwire tx device phandle
+
+  qcom,rx-device:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: A reference to Soundwire rx device phandle
+
+  qcom,micbias1-microvolt:
+    description: micbias1 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,micbias2-microvolt:
+    description: micbias2 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,micbias3-microvolt:
+    description: micbias3 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,micbias4-microvolt:
+    description: micbias4 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,hphl-jack-type-normally-closed:
+    description: Indicates that HPHL jack switch type is normally closed
+    type: boolean
+
+  qcom,ground-jack-type-normally-closed:
+    description: Indicates that Headset Ground switch type is normally closed
+    type: boolean
+
+  qcom,mbhc-headset-vthreshold-microvolt:
+    description: Voltage threshold value for headset detection
+    minimum: 0
+    maximum: 2850000
+
+  qcom,mbhc-headphone-vthreshold-microvolt:
+    description: Voltage threshold value for headphone detection
+    minimum: 0
+    maximum: 2850000
+
+  qcom,mbhc-buttons-vthreshold-microvolt:
+    description:
+      Array of 8 Voltage threshold values corresponding to headset
+      button0 - button7
+    minItems: 8
+    maxItems: 8
+
+  '#sound-dai-cells':
+    const: 1
+
+required:
+  - reset-gpios
+  - qcom,tx-device
+  - qcom,rx-device
+  - qcom,micbias1-microvolt
+  - qcom,micbias2-microvolt
+  - qcom,micbias3-microvolt
+  - qcom,micbias4-microvolt
+  - "#sound-dai-cells"
+
+additionalProperties: true
index d717017b0fdbc68fbabc1b7acd1364de0c4c8d4b..22798d22d981b270602882732dcd1e72d62c3023 100644 (file)
@@ -28,6 +28,10 @@ properties:
     description: Powerdown/Shutdown line to use (pin SD_N)
     maxItems: 1
 
+  reset-gpios:
+    description: Powerdown/Shutdown line to use (pin SD_N)
+    maxItems: 1
+
   '#sound-dai-cells':
     const: 0
 
@@ -37,11 +41,16 @@ properties:
 required:
   - compatible
   - reg
-  - powerdown-gpios
   - '#sound-dai-cells'
   - vdd-1p8-supply
   - vdd-io-supply
 
+oneOf:
+  - required:
+      - powerdown-gpios
+  - required:
+      - reset-gpios
+
 unevaluatedProperties: false
 
 examples:
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml
new file mode 100644 (file)
index 0000000..8801960
--- /dev/null
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt1015.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RT1015 Mono Class D Audio Amplifier
+
+maintainers:
+  - Jack Yu <jack.yu@realtek.com>
+
+properties:
+  compatible:
+    enum:
+      - realtek,rt1015
+
+  reg:
+    maxItems: 1
+
+  realtek,power-up-delay-ms:
+    description: Set a delay time for flush work to be completed,
+      this vlaue is adjustable depending on platform.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@28 {
+            compatible = "realtek,rt1015";
+            reg = <0x28>;
+            realtek,power-up-delay-ms = <50>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/rt1015.txt b/Documentation/devicetree/bindings/sound/rt1015.txt
deleted file mode 100644 (file)
index e498966..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-RT1015 Mono Class D Audio Amplifier
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "realtek,rt1015".
-
-- reg : The I2C address of the device.
-
-Optional properties:
-
-- realtek,power-up-delay-ms
-  Set a delay time for flush work to be completed,
-  this value is adjustable depending on platform.
-
-Example:
-
-rt1015: codec@28 {
-       compatible = "realtek,rt1015";
-       reg = <0x28>;
-       realtek,power-up-delay-ms = <50>;
-};
index 76059259914346996aa81e0301d211b29b048594..cbc7ba37362a935698629c22a0b61f1dc63ca414 100644 (file)
@@ -25,8 +25,11 @@ properties:
     description: Phandles to the codecs.
     $ref: /schemas/types.yaml#/definitions/phandle-array
     items:
-      - description: Phandle to the WM5110 audio codec.
-      - description: Phandle to the HDMI transmitter node.
+      - items:
+          - description: Phandle to the WM5110 audio codec.
+      - items:
+          - description: Phandle to the HDMI transmitter node.
+
 
   samsung,audio-routing:
     description: |
index cd421856409e6579d788fc5089027ed7c2c77915..2d2998faff62b9e7809168cb7a5b94f6a3f03b08 100644 (file)
@@ -3864,14 +3864,16 @@ corresponding destructor.
 
 And next, set suspend/resume callbacks to the pci_driver::
 
-  static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume);
+  static DEFINE_SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume);
 
   static struct pci_driver driver = {
           .name = KBUILD_MODNAME,
           .id_table = snd_my_ids,
           .probe = snd_my_probe,
           .remove = snd_my_remove,
-          .driver.pm = &snd_my_pm_ops,
+          .driver = {
+                  .pm = &snd_my_pm_ops,
+          },
   };
 
 Module Parameters
index 4bce9cfedfbada937e55257f3198cd15742c86f6..98226d076ef6eff011d0a155f6b324461aaa6007 100644 (file)
@@ -5042,6 +5042,7 @@ F:        include/linux/mfd/cs42l43*
 F:     include/sound/cs*
 F:     sound/pci/hda/cirrus*
 F:     sound/pci/hda/cs*
+F:     sound/pci/hda/hda_component*
 F:     sound/pci/hda/hda_cs_dsp_ctl.*
 F:     sound/soc/codecs/cs*
 
@@ -20515,6 +20516,12 @@ F:     include/uapi/sound/compress_*
 F:     sound/core/compress_offload.c
 F:     sound/soc/soc-compress.c
 
+SOUND - CORE KUNIT TEST
+M:     Ivan Orlov <ivan.orlov0322@gmail.com>
+L:     linux-sound@vger.kernel.org
+S:     Supported
+F:     sound/core/sound_kunit.c
+
 SOUND - DMAENGINE HELPERS
 M:     Lars-Peter Clausen <lars@metafoo.de>
 S:     Supported
index 3b722e4c0f0624afa8fd349d300e9a4ec626ed20..7c157bf926956be5cabd6db7c708ff87759c7879 100644 (file)
@@ -1760,7 +1760,9 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
                {"BSG1160", },
                {"BSG2150", },
                {"CSC3551", },
+               {"CSC3554", },
                {"CSC3556", },
+               {"CSC3557", },
                {"INT33FE", },
                {"INT3515", },
                /* Non-conforming _HID for Cirrus Logic already released */
index 79d4254d1f9bc537be6ad3e958928b89d40efba3..9f3d665cfdcf77bc1f405a811c06799104badf7d 100644 (file)
@@ -522,7 +522,7 @@ void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp)
 {
        cs_dsp_debugfs_clear(dsp);
        debugfs_remove_recursive(dsp->debugfs_root);
-       dsp->debugfs_root = NULL;
+       dsp->debugfs_root = ERR_PTR(-ENODEV);
 }
 EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP);
 #else
@@ -2246,6 +2246,11 @@ static int cs_dsp_common_init(struct cs_dsp *dsp)
 
        mutex_init(&dsp->pwr_lock);
 
+#ifdef CONFIG_DEBUG_FS
+       /* Ensure this is invalid if client never provides a debugfs root */
+       dsp->debugfs_root = ERR_PTR(-ENODEV);
+#endif
+
        return 0;
 }
 
index 8158e3cf5d6dec12903c1b8b97f682da451f5df3..97b9c63922303af20175d15ca76dcb93e8e91601 100644 (file)
@@ -329,6 +329,19 @@ static const struct smi_node cs35l41_hda = {
        .bus_type = SMI_AUTO_DETECT,
 };
 
+static const struct smi_node cs35l54_hda = {
+       .instances = {
+               { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+               /* a 5th entry is an alias address, not a real device */
+               { "cs35l54-hda_dummy_dev" },
+               {}
+       },
+       .bus_type = SMI_AUTO_DETECT,
+};
+
 static const struct smi_node cs35l56_hda = {
        .instances = {
                { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },
@@ -342,6 +355,19 @@ static const struct smi_node cs35l56_hda = {
        .bus_type = SMI_AUTO_DETECT,
 };
 
+static const struct smi_node cs35l57_hda = {
+       .instances = {
+               { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+               { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+               /* a 5th entry is an alias address, not a real device */
+               { "cs35l57-hda_dummy_dev" },
+               {}
+       },
+       .bus_type = SMI_AUTO_DETECT,
+};
+
 /*
  * Note new device-ids must also be added to ignore_serial_bus_ids in
  * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -350,7 +376,9 @@ static const struct acpi_device_id smi_acpi_ids[] = {
        { "BSG1160", (unsigned long)&bsg1160_data },
        { "BSG2150", (unsigned long)&bsg2150_data },
        { "CSC3551", (unsigned long)&cs35l41_hda },
+       { "CSC3554", (unsigned long)&cs35l54_hda },
        { "CSC3556", (unsigned long)&cs35l56_hda },
+       { "CSC3557", (unsigned long)&cs35l57_hda },
        { "INT3515", (unsigned long)&int3515_data },
        /* Non-conforming _HID for Cirrus Logic already released */
        { "CLSA0100", (unsigned long)&cs35l41_hda },
index 657f5888a77b0a5913e76dc47904caec94f68a62..e80a2c2cf3e71ff6f333b70a7d4671c3fb80fbef 100644 (file)
@@ -20,7 +20,7 @@ soundwire-bus-y += irq.o
 endif
 
 #AMD driver
-soundwire-amd-y :=     amd_manager.o
+soundwire-amd-y := amd_init.o amd_manager.o
 obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o
 
 #Cadence Objs
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c
new file mode 100644 (file)
index 0000000..e45dc82
--- /dev/null
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * SoundWire AMD Manager Initialize routines
+ *
+ * Initializes and creates SDW devices based on ACPI and Hardware values
+ *
+ * Copyright 2024 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "amd_init.h"
+
+#define ACP_PAD_PULLDOWN_CTRL                          0x0001448
+#define ACP_SW_PAD_KEEPER_EN                           0x0001454
+#define AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK          0x7f9a
+#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK         0x7f9f
+#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK         0x7ffa
+#define AMD_SDW0_PAD_EN_MASK                           1
+#define AMD_SDW1_PAD_EN_MASK                           0x10
+#define AMD_SDW_PAD_EN_MASK    (AMD_SDW0_PAD_EN_MASK | AMD_SDW1_PAD_EN_MASK)
+
+static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev)
+{
+       u32 val;
+       u32 pad_keeper_en_mask, pad_pulldown_ctrl_mask;
+
+       switch (link_mask) {
+       case 1:
+               pad_keeper_en_mask = AMD_SDW0_PAD_EN_MASK;
+               pad_pulldown_ctrl_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+               break;
+       case 2:
+               pad_keeper_en_mask = AMD_SDW1_PAD_EN_MASK;
+               pad_pulldown_ctrl_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+               break;
+       case 3:
+               pad_keeper_en_mask = AMD_SDW_PAD_EN_MASK;
+               pad_pulldown_ctrl_mask = AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+               break;
+       default:
+               dev_err(dev, "No SDW Links are enabled\n");
+               return -ENODEV;
+       }
+
+       val = readl(mmio + ACP_SW_PAD_KEEPER_EN);
+       val |= pad_keeper_en_mask;
+       writel(val, mmio + ACP_SW_PAD_KEEPER_EN);
+       val = readl(mmio + ACP_PAD_PULLDOWN_CTRL);
+       val &= pad_pulldown_ctrl_mask;
+       writel(val, mmio + ACP_PAD_PULLDOWN_CTRL);
+       return 0;
+}
+
+static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx)
+{
+       int i;
+
+       for (i = 0; i < ctx->count; i++) {
+               if (!(ctx->link_mask & BIT(i)))
+                       continue;
+               platform_device_unregister(ctx->pdev[i]);
+       }
+
+       return 0;
+}
+
+static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
+{
+       struct sdw_amd_ctx *ctx;
+       struct acpi_device *adev;
+       struct resource *sdw_res;
+       struct acp_sdw_pdata sdw_pdata[2];
+       struct platform_device_info pdevinfo[2];
+       u32 link_mask;
+       int count, index;
+       int ret;
+
+       if (!res)
+               return NULL;
+
+       adev = acpi_fetch_acpi_dev(res->handle);
+       if (!adev)
+               return NULL;
+
+       if (!res->count)
+               return NULL;
+
+       count = res->count;
+       dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
+       ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent);
+       if (ret)
+               return NULL;
+
+       /*
+        * we need to alloc/free memory manually and can't use devm:
+        * this routine may be called from a workqueue, and not from
+        * the parent .probe.
+        * If devm_ was used, the memory might never be freed on errors.
+        */
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return NULL;
+
+       ctx->count = count;
+       ctx->link_mask = res->link_mask;
+       sdw_res = kzalloc(sizeof(*sdw_res), GFP_KERNEL);
+       if (!sdw_res) {
+               kfree(ctx);
+               return NULL;
+       }
+       sdw_res->flags = IORESOURCE_MEM;
+       sdw_res->start = res->addr;
+       sdw_res->end = res->addr + res->reg_range;
+       memset(&pdevinfo, 0, sizeof(pdevinfo));
+       link_mask = ctx->link_mask;
+       for (index = 0; index < count; index++) {
+               if (!(link_mask & BIT(index)))
+                       continue;
+
+               sdw_pdata[index].instance = index;
+               sdw_pdata[index].acp_sdw_lock = res->acp_lock;
+               pdevinfo[index].name = "amd_sdw_manager";
+               pdevinfo[index].id = index;
+               pdevinfo[index].parent = res->parent;
+               pdevinfo[index].num_res = 1;
+               pdevinfo[index].res = sdw_res;
+               pdevinfo[index].data = &sdw_pdata[index];
+               pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata);
+               pdevinfo[index].fwnode = acpi_fwnode_handle(adev);
+               ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]);
+               if (IS_ERR(ctx->pdev[index]))
+                       goto err;
+       }
+       kfree(sdw_res);
+       return ctx;
+err:
+       while (index--) {
+               if (!(link_mask & BIT(index)))
+                       continue;
+
+               platform_device_unregister(ctx->pdev[index]);
+       }
+
+       kfree(sdw_res);
+       kfree(ctx);
+       return NULL;
+}
+
+static int sdw_amd_startup(struct sdw_amd_ctx *ctx)
+{
+       struct amd_sdw_manager *amd_manager;
+       int i, ret;
+
+       /* Startup SDW Manager devices */
+       for (i = 0; i < ctx->count; i++) {
+               if (!(ctx->link_mask & BIT(i)))
+                       continue;
+               amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev);
+               ret = amd_sdw_manager_start(amd_manager);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx)
+{
+       *sdw_ctx = sdw_amd_probe_controller(res);
+       if (!*sdw_ctx)
+               return -ENODEV;
+
+       return sdw_amd_startup(*sdw_ctx);
+}
+EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT);
+
+void sdw_amd_exit(struct sdw_amd_ctx *ctx)
+{
+       sdw_amd_cleanup(ctx);
+       kfree(ctx->ids);
+       kfree(ctx);
+}
+EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT);
+
+int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx)
+{
+       struct amd_sdw_manager *amd_manager;
+       struct sdw_bus *bus;
+       struct sdw_slave *slave;
+       struct list_head *node;
+       int index;
+       int i = 0;
+       int num_slaves = 0;
+
+       for (index = 0; index < ctx->count; index++) {
+               if (!(ctx->link_mask & BIT(index)))
+                       continue;
+               amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
+               if (!amd_manager)
+                       return -ENODEV;
+               bus = &amd_manager->bus;
+               /* Calculate number of slaves */
+               list_for_each(node, &bus->slaves)
+                       num_slaves++;
+       }
+
+       ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
+       if (!ctx->ids)
+               return -ENOMEM;
+       ctx->num_slaves = num_slaves;
+       for (index = 0; index < ctx->count; index++) {
+               if (!(ctx->link_mask & BIT(index)))
+                       continue;
+               amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
+               if (amd_manager) {
+                       bus = &amd_manager->bus;
+                       list_for_each_entry(slave, &bus->slaves, node) {
+                               ctx->ids[i].id = slave->id;
+                               ctx->ids[i].link_id = bus->link_id;
+                               i++;
+                       }
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD SoundWire Init Library");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/soundwire/amd_init.h b/drivers/soundwire/amd_init.h
new file mode 100644 (file)
index 0000000..928b0c7
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __AMD_INIT_H
+#define __AMD_INIT_H
+
+#include <linux/soundwire/sdw_amd.h>
+
+int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager);
+
+#endif
index f54bb4dd2d1016a5c18abd53356ee02bf101a19a..7cd24bd8e2248ef9c0e5fece95b3fb58e94f4e1e 100644 (file)
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
 /*
  * SoundWire AMD Manager driver
  *
- * Copyright 2023 Advanced Micro Devices, Inc.
+ * Copyright 2023-24 Advanced Micro Devices, Inc.
  */
 
 #include <linux/completion.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "bus.h"
+#include "amd_init.h"
 #include "amd_manager.h"
 
 #define DRV_NAME "amd_sdw_manager"
 
 #define to_amd_sdw(b)  container_of(b, struct amd_sdw_manager, bus)
 
-static void amd_enable_sdw_pads(struct amd_sdw_manager *amd_manager)
-{
-       u32 sw_pad_pulldown_val;
-       u32 val;
-
-       mutex_lock(amd_manager->acp_sdw_lock);
-       val = readl(amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
-       val |= amd_manager->reg_mask->sw_pad_enable_mask;
-       writel(val, amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
-       usleep_range(1000, 1500);
-
-       sw_pad_pulldown_val = readl(amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
-       sw_pad_pulldown_val &= amd_manager->reg_mask->sw_pad_pulldown_mask;
-       writel(sw_pad_pulldown_val, amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
-       mutex_unlock(amd_manager->acp_sdw_lock);
-}
-
 static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager)
 {
        u32 val;
@@ -102,12 +86,11 @@ static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager)
 
 static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
 {
-       struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
        u32 val;
 
        mutex_lock(amd_manager->acp_sdw_lock);
        val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
-       val |= reg_mask->acp_sdw_intr_mask;
+       val |= sdw_manager_reg_mask_array[amd_manager->instance];
        writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
        mutex_unlock(amd_manager->acp_sdw_lock);
 
@@ -120,12 +103,11 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
 
 static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
 {
-       struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
        u32 val;
 
        mutex_lock(amd_manager->acp_sdw_lock);
        val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
-       val &= ~reg_mask->acp_sdw_intr_mask;
+       val &= ~sdw_manager_reg_mask_array[amd_manager->instance];
        writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
        mutex_unlock(amd_manager->acp_sdw_lock);
 
@@ -864,23 +846,20 @@ static void amd_sdw_irq_thread(struct work_struct *work)
        writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
 }
 
-static void amd_sdw_probe_work(struct work_struct *work)
+int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
 {
-       struct amd_sdw_manager *amd_manager = container_of(work, struct amd_sdw_manager,
-                                                          probe_work);
        struct sdw_master_prop *prop;
        int ret;
 
        prop = &amd_manager->bus.prop;
        if (!prop->hw_disabled) {
-               amd_enable_sdw_pads(amd_manager);
                ret = amd_init_sdw_manager(amd_manager);
                if (ret)
-                       return;
+                       return ret;
                amd_enable_sdw_interrupts(amd_manager);
                ret = amd_enable_sdw_manager(amd_manager);
                if (ret)
-                       return;
+                       return ret;
                amd_sdw_set_frameshape(amd_manager);
        }
        /* Enable runtime PM */
@@ -889,6 +868,7 @@ static void amd_sdw_probe_work(struct work_struct *work)
        pm_runtime_mark_last_busy(amd_manager->dev);
        pm_runtime_set_active(amd_manager->dev);
        pm_runtime_enable(amd_manager->dev);
+       return 0;
 }
 
 static int amd_sdw_manager_probe(struct platform_device *pdev)
@@ -948,7 +928,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance];
        params = &amd_manager->bus.params;
 
        params->col = AMD_SDW_DEFAULT_COLUMNS;
@@ -972,11 +951,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
        dev_set_drvdata(dev, amd_manager);
        INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
        INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
-       INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work);
-       /*
-        * Instead of having lengthy probe sequence, use deferred probe.
-        */
-       schedule_work(&amd_manager->probe_work);
        return 0;
 }
 
@@ -986,7 +960,6 @@ static void amd_sdw_manager_remove(struct platform_device *pdev)
        int ret;
 
        pm_runtime_disable(&pdev->dev);
-       cancel_work_sync(&amd_manager->probe_work);
        amd_disable_sdw_interrupts(amd_manager);
        sdw_bus_master_delete(&amd_manager->bus);
        ret = amd_disable_sdw_manager(amd_manager);
@@ -1215,5 +1188,5 @@ module_platform_driver(amd_sdw_driver);
 
 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
 MODULE_DESCRIPTION("AMD SoundWire driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
 MODULE_ALIAS("platform:" DRV_NAME);
index 5f040151a259b8f7eb771981013680d2a8078352..418b679e0b1a684e0a3ac502d94f74d24d24c00d 100644 (file)
@@ -1,6 +1,6 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
 /*
- * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ * Copyright (C) 2023-24 Advanced Micro Devices, Inc. All rights reserved.
  */
 
 #ifndef __AMD_MANAGER_H
@@ -243,16 +243,8 @@ static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] =  {
         ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
 };
 
-static struct sdw_manager_reg_mask sdw_manager_reg_mask_array[2] =  {
-       {
-               AMD_SDW0_PAD_KEEPER_EN_MASK,
-               AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK,
-               AMD_SDW0_EXT_INTR_MASK
-       },
-       {
-               AMD_SDW1_PAD_KEEPER_EN_MASK,
-               AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK,
+static u32 sdw_manager_reg_mask_array[AMD_SDW_MAX_MANAGER_COUNT] =  {
+               AMD_SDW0_EXT_INTR_MASK,
                AMD_SDW1_EXT_INTR_MASK
-       }
 };
 #endif
index 9ebdd0cd0b1cf598b35902541c0997c5934578d3..91ab97a456fa9fe898bcb1f9f496bb0d8152356c 100644 (file)
@@ -130,6 +130,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
                },
                .driver_data = (void *)intel_rooks_county,
        },
+       {
+               /* quirk used for NUC15 LAPRC710 skew */
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"),
+               },
+               .driver_data = (void *)intel_rooks_county,
+       },
        {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
index 29cd11d5a3cfd187a1bfbb6125c10a1940f027ce..23384a54d57562441544c475ae4270e166576824 100644 (file)
@@ -123,7 +123,6 @@ struct cs_dsp_client_ops;
  * @sysclk_mask:       Mask of frequency bits within sysclk register (ADSP1 only)
  * @sysclk_shift:      Shift of frequency bits within sysclk register (ADSP1 only)
  * @alg_regions:       List of currently loaded algorithm regions
- * @fw_file_name:      Filename of the current firmware
  * @fw_name:           Name of the current firmware
  * @fw_id:             ID of the current firmware, obtained from the wmfw
  * @fw_id_version:     Version of the firmware, obtained from the wmfw
index ceecad74aef9ffe4b11540c18096e928a024f53c..28a4eb77717f987c1a389ff722632ed719e63f97 100644 (file)
@@ -1,11 +1,12 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
 /*
- * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ * Copyright (C) 2023-24 Advanced Micro Devices, Inc. All rights reserved.
  */
 
 #ifndef __SDW_AMD_H
 #define __SDW_AMD_H
 
+#include <linux/acpi.h>
 #include <linux/soundwire/sdw.h>
 
 /* AMD pm_runtime quirk definitions */
@@ -25,6 +26,7 @@
 #define AMD_SDW_POWER_OFF_MODE         2
 #define ACP_SDW0       0
 #define ACP_SDW1       1
+#define AMD_SDW_MAX_MANAGER_COUNT      2
 
 struct acp_sdw_pdata {
        u16 instance;
@@ -32,12 +34,6 @@ struct acp_sdw_pdata {
        struct mutex *acp_sdw_lock;
 };
 
-struct sdw_manager_reg_mask {
-       u32 sw_pad_enable_mask;
-       u32 sw_pad_pulldown_mask;
-       u32 acp_sdw_intr_mask;
-};
-
 /**
  * struct sdw_amd_dai_runtime: AMD sdw dai runtime  data
  *
@@ -59,10 +55,8 @@ struct sdw_amd_dai_runtime {
  * @dev: linux device
  * @mmio: SoundWire registers mmio base
  * @acp_mmio: acp registers mmio base
- * @reg_mask: register mask structure per manager instance
  * @amd_sdw_irq_thread: SoundWire manager irq workqueue
  * @amd_sdw_work: peripheral status work queue
- * @probe_work: SoundWire manager probe workqueue
  * @acp_sdw_lock: mutex to protect acp share register access
  * @status: peripheral devices status array
  * @num_din_ports: number of input ports
@@ -83,10 +77,8 @@ struct amd_sdw_manager {
        void __iomem *mmio;
        void __iomem *acp_mmio;
 
-       struct sdw_manager_reg_mask *reg_mask;
        struct work_struct amd_sdw_irq_thread;
        struct work_struct amd_sdw_work;
-       struct work_struct probe_work;
        /* mutex to protect acp common register access */
        struct mutex *acp_sdw_lock;
 
@@ -106,4 +98,71 @@ struct amd_sdw_manager {
 
        struct sdw_amd_dai_runtime **dai_runtime_array;
 };
+
+/**
+ * struct sdw_amd_acpi_info - Soundwire AMD information found in ACPI tables
+ * @handle: ACPI controller handle
+ * @count: maximum no of soundwire manager links supported on AMD platform.
+ * @link_mask: bit-wise mask listing links enabled by BIOS menu
+ */
+struct sdw_amd_acpi_info {
+       acpi_handle handle;
+       int count;
+       u32 link_mask;
+};
+
+/**
+ * struct sdw_amd_ctx - context allocated by the controller driver probe
+ *
+ * @count: link count
+ * @num_slaves: total number of devices exposed across all enabled links
+ * @link_mask: bit-wise mask listing SoundWire links reported by the
+ * Controller
+ * @ids: array of slave_id, representing Slaves exposed across all enabled
+ * links
+ * @pdev: platform device structure
+ */
+struct sdw_amd_ctx {
+       int count;
+       int num_slaves;
+       u32 link_mask;
+       struct sdw_extended_slave_id *ids;
+       struct platform_device *pdev[AMD_SDW_MAX_MANAGER_COUNT];
+};
+
+/**
+ * struct sdw_amd_res - Soundwire AMD global resource structure,
+ * typically populated by the DSP driver/Legacy driver
+ *
+ * @addr: acp pci device resource start address
+ * @reg_range: ACP register range
+ * @link_mask: bit-wise mask listing links selected by the DSP driver/
+ * legacy driver
+ * @count: link count
+ * @mmio_base: mmio base of SoundWire registers
+ * @handle: ACPI parent handle
+ * @parent: parent device
+ * @dev: device implementing hwparams and free callbacks
+ * @acp_lock: mutex protecting acp common registers access
+ */
+struct sdw_amd_res {
+       u32 addr;
+       u32 reg_range;
+       u32 link_mask;
+       int count;
+       void __iomem *mmio_base;
+       acpi_handle handle;
+       struct device *parent;
+       struct device *dev;
+       /* use to protect acp common registers access */
+       struct mutex *acp_lock;
+};
+
+int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **ctx);
+
+void sdw_amd_exit(struct sdw_amd_ctx *ctx);
+
+int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx);
+
+int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info);
 #endif
index b05d5a87c313c4713477b91d41929be491ded6da..c459809efee4fbb01877398da3d7c2f43ba0239d 100644 (file)
@@ -420,8 +420,6 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
  * @bus_lock_spinlock: spinlock for SPI bus locking
  * @bus_lock_mutex: mutex for exclusion of multiple callers
  * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
- * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
- *     more than one chip select at once.
  * @setup: updates the device mode and clocking records used by a
  *     device's SPI controller; protocol code may call this.  This
  *     must fail if an unrecognized or unsupported mode is requested.
index 9a4429970d929eab60e622b9fc3a6cae4cd2ccb3..64402347d7a253dec5e0bef5379bc5480a14d0e2 100644 (file)
@@ -65,6 +65,9 @@ int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
 #ifdef CONFIG_PM
 void snd_ak4531_suspend(struct snd_ak4531 *ak4531);
 void snd_ak4531_resume(struct snd_ak4531 *ak4531);
+#else
+static inline void snd_ak4531_suspend(struct snd_ak4531 *ak4531) {}
+static inline void snd_ak4531_resume(struct snd_ak4531 *ak4531) {}
 #endif
 
 #endif /* __SOUND_AK4531_CODEC_H */
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
new file mode 100644 (file)
index 0000000..f481148
--- /dev/null
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS_AMP_LIB_H
+#define CS_AMP_LIB_H
+
+#include <linux/efi.h>
+#include <linux/types.h>
+
+struct cs_dsp;
+
+struct cirrus_amp_cal_data {
+       u32 calTarget[2];
+       u32 calTime[2];
+       s8  calAmbient;
+       u8  calStatus;
+       u16 calR;
+} __packed;
+
+struct cirrus_amp_efi_data {
+       u32 size;
+       u32 count;
+       struct cirrus_amp_cal_data data[];
+} __packed;
+
+/**
+ * struct cirrus_amp_cal_controls - definition of firmware calibration controls
+ * @alg_id:    ID of algorithm containing the controls.
+ * @mem_region:        DSP memory region containing the controls.
+ * @ambient:   Name of control for calAmbient value.
+ * @calr:      Name of control for calR value.
+ * @status:    Name of control for calStatus value.
+ * @checksum:  Name of control for checksum value.
+ */
+struct cirrus_amp_cal_controls {
+       unsigned int alg_id;
+       int mem_region;
+       const char *ambient;
+       const char *calr;
+       const char *status;
+       const char *checksum;
+};
+
+int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+                           const struct cirrus_amp_cal_controls *controls,
+                           const struct cirrus_amp_cal_data *data);
+int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+                                   struct cirrus_amp_cal_data *out_data);
+
+struct cs_amp_test_hooks {
+       efi_status_t (*get_efi_variable)(efi_char16_t *name,
+                                        efi_guid_t *guid,
+                                        unsigned long *size,
+                                        void *buf);
+
+       int (*write_cal_coeff)(struct cs_dsp *dsp,
+                              const struct cirrus_amp_cal_controls *controls,
+                              const char *ctl_name, u32 val);
+};
+
+extern const struct cs_amp_test_hooks * const cs_amp_test_hooks;
+
+#endif /* CS_AMP_LIB_H */
index b24716ab27504bdfa17f221a39053dd21dd961d9..e0629699b56338a76b69616dcc2a79dece2e51f4 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/firmware/cirrus/cs_dsp.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regmap.h>
+#include <sound/cs-amp-lib.h>
 
 #define CS35L56_DEVID                                  0x0000000
 #define CS35L56_REVID                                  0x0000004
@@ -23,6 +24,9 @@
 #define CS35L56_BLOCK_ENABLES2                         0x000201C
 #define CS35L56_REFCLK_INPUT                           0x0002C04
 #define CS35L56_GLOBAL_SAMPLE_RATE                     0x0002C0C
+#define CS35L56_OTP_MEM_53                             0x00300D4
+#define CS35L56_OTP_MEM_54                             0x00300D8
+#define CS35L56_OTP_MEM_55                             0x00300DC
 #define CS35L56_ASP1_ENABLES1                          0x0004800
 #define CS35L56_ASP1_CONTROL1                          0x0004804
 #define CS35L56_ASP1_CONTROL2                          0x0004808
@@ -257,11 +261,15 @@ struct cs35l56_base {
        struct regmap *regmap;
        int irq;
        struct mutex irq_lock;
+       u8 type;
        u8 rev;
        bool init_done;
        bool fw_patched;
        bool secured;
        bool can_hibernate;
+       bool cal_data_valid;
+       s8 cal_index;
+       struct cirrus_amp_cal_data cal_data;
        struct gpio_desc *reset_gpio;
 };
 
@@ -269,6 +277,8 @@ extern struct regmap_config cs35l56_regmap_i2c;
 extern struct regmap_config cs35l56_regmap_spi;
 extern struct regmap_config cs35l56_regmap_sdw;
 
+extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;
+
 extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
 extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
 
@@ -286,6 +296,7 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
 int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
 int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
 void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
+int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base);
 int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
                             bool *fw_missing, unsigned int *fw_version);
 int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
index 3994e933db195627fce912493f707082fe260c5c..1bd8eee54f6665dbd2ea489c4f093a70fa25a5e5 100644 (file)
 #define CS42L42_PLL_LOCK_TIMEOUT_US    1250
 #define CS42L42_HP_ADC_EN_TIME_US      20000
 #define CS42L42_PDN_DONE_POLL_US       1000
-#define CS42L42_PDN_DONE_TIMEOUT_US    200000
-#define CS42L42_PDN_DONE_TIME_MS       100
-#define CS42L42_FILT_DISCHARGE_TIME_MS 46
+#define CS42L42_PDN_DONE_TIMEOUT_US    235000
+#define CS42L42_PDN_DONE_TIME_MS       65
 
 #endif /* __CS42L42_H */
index 1cc530434b97b4bc1a9996e216f826d1b663dc00..3f7f365ed248cd82c0fd96c0414ccfdc2c4079ae 100644 (file)
@@ -103,7 +103,7 @@ struct snd_emux {
        int ports[SNDRV_EMUX_MAX_PORTS];        /* The ports for this device */
        struct snd_emux_port *portptrs[SNDRV_EMUX_MAX_PORTS];
        int used;       /* use counter */
-       char *name;     /* name of the device (internal) */
+       const char *name;       /* name of the device (internal) */
        struct snd_rawmidi **vmidi;
        struct timer_list tlist;        /* for pending note-offs */
        int timer_active;
index 228114aca4158d1235f8983c7ab5f8ea8657ef0c..d849d9b24f13b541ccd8a1dc8a858cc34c8f609e 100644 (file)
@@ -181,4 +181,4 @@ hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enabl
 {
        return 0;
 }
-#endif /* CONFIG_SND_SOC_SOF_HDA */
+#endif /* CONFIG_SND_SOC_SOF_HDA_MLINK */
index 55958711d6974e87ed08155fef3b47da6d53ba7f..5ff31e6d41c190fac204c5140ba98c501a382e92 100644 (file)
@@ -131,6 +131,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_REG_VS_SDXEFIFOS_XBASE     0x1094
 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
 
+#define AZX_REG_VS_LTRP_GB_MASK                GENMASK(6, 0)
+
 /* PCI space */
 #define AZX_PCIREG_TCSEL               0x44
 
index cc175c623dac71d63062beb2441f5b94ec35d346..210096f124eed5765960b2b512954fb3dfa5c4ec 100644 (file)
@@ -659,6 +659,18 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
                flags = _snd_pcm_stream_lock_irqsave_nested(substream); \
        } while (0)
 
+/* definitions for guard(); use like guard(pcm_stream_lock) */
+DEFINE_LOCK_GUARD_1(pcm_stream_lock, struct snd_pcm_substream,
+                   snd_pcm_stream_lock(_T->lock),
+                   snd_pcm_stream_unlock(_T->lock))
+DEFINE_LOCK_GUARD_1(pcm_stream_lock_irq, struct snd_pcm_substream,
+                   snd_pcm_stream_lock_irq(_T->lock),
+                   snd_pcm_stream_unlock_irq(_T->lock))
+DEFINE_LOCK_GUARD_1(pcm_stream_lock_irqsave, struct snd_pcm_substream,
+                   snd_pcm_stream_lock_irqsave(_T->lock, _T->flags),
+                   snd_pcm_stream_unlock_irqrestore(_T->lock, _T->flags),
+                   unsigned long flags)
+
 /**
  * snd_pcm_group_for_each_entry - iterate over the linked substreams
  * @s: the iterator
index f540339fb0c77497d618482601972672d4b48913..24970f36c38ae9f6eda2bd0a6353a70abaaf4760 100644 (file)
@@ -290,6 +290,9 @@ int snd_sbmixer_new(struct snd_sb *chip);
 #ifdef CONFIG_PM
 void snd_sbmixer_suspend(struct snd_sb *chip);
 void snd_sbmixer_resume(struct snd_sb *chip);
+#else
+static inline void snd_sbmixer_suspend(struct snd_sb *chip) {}
+static inline void snd_sbmixer_resume(struct snd_sb *chip) {}
 #endif
 
 /* sb8_init.c */
index 6defc5547ff905690a4f1fe3fb34952457cb7319..39613b406b1d0e258707b70f424bb90dc4d82de0 100644 (file)
@@ -1401,8 +1401,8 @@ void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
 void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms,
                                     struct snd_soc_dai_link_component *cpus);
 struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev,
-                                             struct of_phandle_args *args);
-struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args);
+                                             const struct of_phandle_args *args);
+struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args);
 struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
                                         struct snd_soc_dai_driver *dai_drv,
                                         bool legacy_dai_naming);
index 9df7ac824efea6c65baa7dc77f0ac8b5bd8dd317..59cd014392c1b18fe919026937e3f336dab43e90 100644 (file)
@@ -26,4 +26,11 @@ struct sof_ipc_dai_acpdmic_params {
        uint32_t pdm_ch;
 } __packed;
 
+/* ACP_SDW Configuration Request - SOF_IPC_DAI_AMD_SDW_CONFIG */
+struct sof_ipc_dai_acp_sdw_params {
+       struct sof_ipc_hdr hdr;
+       u32 rate;
+       u32 channels;
+} __packed;
+
 #endif
index 4773a5f616a413f0438125f75179487e5954d566..0764a80c17a9b5380431237c094d2cf9203d041e 100644 (file)
@@ -89,6 +89,7 @@ enum sof_ipc_dai_type {
        SOF_DAI_AMD_SP_VIRTUAL,         /**< AMD ACP SP VIRTUAL */
        SOF_DAI_AMD_HS_VIRTUAL,         /**< AMD ACP HS VIRTUAL */
        SOF_DAI_IMX_MICFIL,             /** < i.MX MICFIL PDM */
+       SOF_DAI_AMD_SDW,                /**< AMD ACP SDW */
 };
 
 /* general purpose DAI configuration */
@@ -119,6 +120,7 @@ struct sof_ipc_dai_config {
                struct sof_ipc_dai_acp_params acphs;
                struct sof_ipc_dai_mtk_afe_params afe;
                struct sof_ipc_dai_micfil_params micfil;
+               struct sof_ipc_dai_acp_sdw_params acp_sdw;
        };
 } __packed;
 
index 9aff384941de27f925d0491312a7088ebeb6a297..99ca3e401fd1fe36422a77a71eb87c26bbdaeacf 100644 (file)
@@ -103,7 +103,6 @@ struct tasdevice_priv {
        struct tm tm;
 
        enum device_catlog_id catlog_id;
-       const char *acpi_subsystem_id;
        unsigned char cal_binaryname[TASDEVICE_MAX_CHANNELS][64];
        unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
        unsigned char coef_binaryname[64];
index 4d8ef71090af1c758a2804234ea747c68ad50e5b..4eed9028bb1199f144e7dd35dd95814bacebc2ce 100644 (file)
@@ -17,71 +17,76 @@ struct snd_soc_card;
 struct snd_soc_dapm_widget;
 struct snd_soc_dapm_path;
 
-DECLARE_EVENT_CLASS(snd_soc_card,
+DECLARE_EVENT_CLASS(snd_soc_dapm,
 
-       TP_PROTO(struct snd_soc_card *card, int val),
+       TP_PROTO(struct snd_soc_dapm_context *dapm, int val),
 
-       TP_ARGS(card, val),
+       TP_ARGS(dapm, val),
 
        TP_STRUCT__entry(
-               __string(       name,           card->name      )
-               __field(        int,            val             )
+               __string(       card_name,      dapm->card->name)
+               __string(       comp_name,      dapm->component ? dapm->component->name : "(none)")
+               __field(        int,            val)
        ),
 
        TP_fast_assign(
-               __assign_str(name, card->name);
+               __assign_str(card_name, dapm->card->name);
+               __assign_str(comp_name, dapm->component ? dapm->component->name : "(none)");
                __entry->val = val;
        ),
 
-       TP_printk("card=%s val=%d", __get_str(name), (int)__entry->val)
+       TP_printk("card=%s component=%s val=%d",
+                 __get_str(card_name), __get_str(comp_name), (int)__entry->val)
 );
 
-DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_start,
+DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_start,
 
-       TP_PROTO(struct snd_soc_card *card, int val),
+       TP_PROTO(struct snd_soc_dapm_context *dapm, int val),
 
-       TP_ARGS(card, val)
+       TP_ARGS(dapm, val)
 
 );
 
-DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_done,
+DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_done,
 
-       TP_PROTO(struct snd_soc_card *card, int val),
+       TP_PROTO(struct snd_soc_dapm_context *dapm, int val),
 
-       TP_ARGS(card, val)
+       TP_ARGS(dapm, val)
 
 );
 
 DECLARE_EVENT_CLASS(snd_soc_dapm_basic,
 
-       TP_PROTO(struct snd_soc_card *card),
+       TP_PROTO(struct snd_soc_card *card, int event),
 
-       TP_ARGS(card),
+       TP_ARGS(card, event),
 
        TP_STRUCT__entry(
                __string(       name,   card->name      )
+               __field(        int,    event           )
        ),
 
        TP_fast_assign(
                __assign_str(name, card->name);
+               __entry->event = event;
        ),
 
-       TP_printk("card=%s", __get_str(name))
+       TP_printk("card=%s event=%d", __get_str(name), (int)__entry->event)
 );
 
 DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_start,
 
-       TP_PROTO(struct snd_soc_card *card),
+       TP_PROTO(struct snd_soc_card *card, int event),
 
-       TP_ARGS(card)
+       TP_ARGS(card, event)
 
 );
 
 DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_done,
 
-       TP_PROTO(struct snd_soc_card *card),
+       TP_PROTO(struct snd_soc_card *card, int event),
 
-       TP_ARGS(card)
+       TP_ARGS(card, event)
 
 );
 
index dfe49547a7b04d6aa7e9cb474a0f103b2cd2b043..5f4100c2cf04cb44494fec115b70095f530b42c2 100644 (file)
@@ -7,6 +7,14 @@
 
 #include <linux/virtio_types.h>
 
+/*******************************************************************************
+ * FEATURE BITS
+ */
+enum {
+       /* device supports control elements */
+       VIRTIO_SND_F_CTLS = 0
+};
+
 /*******************************************************************************
  * CONFIGURATION SPACE
  */
@@ -17,6 +25,8 @@ struct virtio_snd_config {
        __le32 streams;
        /* # of available channel maps */
        __le32 chmaps;
+       /* # of available control elements */
+       __le32 controls;
 };
 
 enum {
@@ -55,6 +65,15 @@ enum {
        /* channel map control request types */
        VIRTIO_SND_R_CHMAP_INFO = 0x0200,
 
+       /* control element request types */
+       VIRTIO_SND_R_CTL_INFO = 0x0300,
+       VIRTIO_SND_R_CTL_ENUM_ITEMS,
+       VIRTIO_SND_R_CTL_READ,
+       VIRTIO_SND_R_CTL_WRITE,
+       VIRTIO_SND_R_CTL_TLV_READ,
+       VIRTIO_SND_R_CTL_TLV_WRITE,
+       VIRTIO_SND_R_CTL_TLV_COMMAND,
+
        /* jack event types */
        VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000,
        VIRTIO_SND_EVT_JACK_DISCONNECTED,
@@ -63,6 +82,9 @@ enum {
        VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100,
        VIRTIO_SND_EVT_PCM_XRUN,
 
+       /* control element event types */
+       VIRTIO_SND_EVT_CTL_NOTIFY = 0x1200,
+
        /* common status codes */
        VIRTIO_SND_S_OK = 0x8000,
        VIRTIO_SND_S_BAD_MSG,
@@ -331,4 +353,136 @@ struct virtio_snd_chmap_info {
        __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE];
 };
 
+/*******************************************************************************
+ * CONTROL ELEMENTS MESSAGES
+ */
+struct virtio_snd_ctl_hdr {
+       /* VIRTIO_SND_R_CTL_XXX */
+       struct virtio_snd_hdr hdr;
+       /* 0 ... virtio_snd_config::controls - 1 */
+       __le32 control_id;
+};
+
+/* supported roles for control elements */
+enum {
+       VIRTIO_SND_CTL_ROLE_UNDEFINED = 0,
+       VIRTIO_SND_CTL_ROLE_VOLUME,
+       VIRTIO_SND_CTL_ROLE_MUTE,
+       VIRTIO_SND_CTL_ROLE_GAIN
+};
+
+/* supported value types for control elements */
+enum {
+       VIRTIO_SND_CTL_TYPE_BOOLEAN = 0,
+       VIRTIO_SND_CTL_TYPE_INTEGER,
+       VIRTIO_SND_CTL_TYPE_INTEGER64,
+       VIRTIO_SND_CTL_TYPE_ENUMERATED,
+       VIRTIO_SND_CTL_TYPE_BYTES,
+       VIRTIO_SND_CTL_TYPE_IEC958
+};
+
+/* supported access rights for control elements */
+enum {
+       VIRTIO_SND_CTL_ACCESS_READ = 0,
+       VIRTIO_SND_CTL_ACCESS_WRITE,
+       VIRTIO_SND_CTL_ACCESS_VOLATILE,
+       VIRTIO_SND_CTL_ACCESS_INACTIVE,
+       VIRTIO_SND_CTL_ACCESS_TLV_READ,
+       VIRTIO_SND_CTL_ACCESS_TLV_WRITE,
+       VIRTIO_SND_CTL_ACCESS_TLV_COMMAND
+};
+
+struct virtio_snd_ctl_info {
+       /* common header */
+       struct virtio_snd_info hdr;
+       /* element role (VIRTIO_SND_CTL_ROLE_XXX) */
+       __le32 role;
+       /* element value type (VIRTIO_SND_CTL_TYPE_XXX) */
+       __le32 type;
+       /* element access right bit map (1 << VIRTIO_SND_CTL_ACCESS_XXX) */
+       __le32 access;
+       /* # of members in the element value */
+       __le32 count;
+       /* index for an element with a non-unique name */
+       __le32 index;
+       /* name identifier string for the element */
+       __u8 name[44];
+       /* additional information about the element's value */
+       union {
+               /* VIRTIO_SND_CTL_TYPE_INTEGER */
+               struct {
+                       /* minimum supported value */
+                       __le32 min;
+                       /* maximum supported value */
+                       __le32 max;
+                       /* fixed step size for value (0 = variable size) */
+                       __le32 step;
+               } integer;
+               /* VIRTIO_SND_CTL_TYPE_INTEGER64 */
+               struct {
+                       /* minimum supported value */
+                       __le64 min;
+                       /* maximum supported value */
+                       __le64 max;
+                       /* fixed step size for value (0 = variable size) */
+                       __le64 step;
+               } integer64;
+               /* VIRTIO_SND_CTL_TYPE_ENUMERATED */
+               struct {
+                       /* # of options supported for value */
+                       __le32 items;
+               } enumerated;
+       } value;
+};
+
+struct virtio_snd_ctl_enum_item {
+       /* option name */
+       __u8 item[64];
+};
+
+struct virtio_snd_ctl_iec958 {
+       /* AES/IEC958 channel status bits */
+       __u8 status[24];
+       /* AES/IEC958 subcode bits */
+       __u8 subcode[147];
+       /* nothing */
+       __u8 pad;
+       /* AES/IEC958 subframe bits */
+       __u8 dig_subframe[4];
+};
+
+struct virtio_snd_ctl_value {
+       union {
+               /* VIRTIO_SND_CTL_TYPE_BOOLEAN|INTEGER value */
+               __le32 integer[128];
+               /* VIRTIO_SND_CTL_TYPE_INTEGER64 value */
+               __le64 integer64[64];
+               /* VIRTIO_SND_CTL_TYPE_ENUMERATED value (option indexes) */
+               __le32 enumerated[128];
+               /* VIRTIO_SND_CTL_TYPE_BYTES value */
+               __u8 bytes[512];
+               /* VIRTIO_SND_CTL_TYPE_IEC958 value */
+               struct virtio_snd_ctl_iec958 iec958;
+       } value;
+};
+
+/* supported event reason types */
+enum {
+       /* element's value has changed */
+       VIRTIO_SND_CTL_EVT_MASK_VALUE = 0,
+       /* element's information has changed */
+       VIRTIO_SND_CTL_EVT_MASK_INFO,
+       /* element's metadata has changed */
+       VIRTIO_SND_CTL_EVT_MASK_TLV
+};
+
+struct virtio_snd_ctl_event {
+       /* VIRTIO_SND_EVT_CTL_NOTIFY */
+       struct virtio_snd_hdr hdr;
+       /* 0 ... virtio_snd_config::controls - 1 */
+       __le16 control_id;
+       /* event reason bit map (1 << VIRTIO_SND_CTL_EVT_MASK_XXX) */
+       __le16 mask;
+};
+
 #endif /* VIRTIO_SND_IF_H */
index 4ffe546aa409a19f67c123fc7bb1a3ec577ab2de..4beca03405c079d1c6198b3799e2f49d3f06b1bd 100644 (file)
@@ -19,6 +19,8 @@ enum avs_tplg_token {
        AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32            = 6,
        AVS_TKN_MANIFEST_NUM_PPLCFGS_U32                = 7,
        AVS_TKN_MANIFEST_NUM_BINDINGS_U32               = 8,
+       AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32         = 9,
+       AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32           = 10,
 
        /* struct avs_tplg_library */
        AVS_TKN_LIBRARY_ID_U32                          = 101,
@@ -109,6 +111,8 @@ enum avs_tplg_token {
        AVS_TKN_MOD_PROC_DOMAIN_U8                      = 1705,
        AVS_TKN_MOD_MODCFG_EXT_ID_U32                   = 1706,
        AVS_TKN_MOD_KCONTROL_ID_U32                     = 1707,
+       AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32             = 1708,
+       AVS_TKN_MOD_INIT_CONFIG_ID_U32                  = 1709,
 
        /* struct avs_tplg_path_template */
        AVS_TKN_PATH_TMPL_ID_U32                        = 1801,
@@ -125,6 +129,11 @@ enum avs_tplg_token {
 
        /* struct avs_tplg_kcontrol */
        AVS_TKN_KCONTROL_ID_U32                         = 2301,
+
+       /* struct avs_tplg_init_config */
+       AVS_TKN_INIT_CONFIG_ID_U32                      = 2401,
+       AVS_TKN_INIT_CONFIG_PARAM_U8                    = 2402,
+       AVS_TKN_INIT_CONFIG_LENGTH_U32                  = 2403,
 };
 
 #endif
index ee5708934614da6e5706eef52ac84b513f6c6017..6bf00c09d30db0b2c0453ef64d73ded35bfd32c2 100644 (file)
 #define SOF_TKN_IMX_MICFIL_RATE                        2000
 #define SOF_TKN_IMX_MICFIL_CH                  2001
 
+/* ACP SDW */
+#define SOF_TKN_AMD_ACP_SDW_RATE               2100
+#define SOF_TKN_AMD_ACP_SDW_CH                 2101
+
 #endif
index 0cd19a05db1987e222548dbb95b3bf76b71a586e..e68b4cb4df296aa2d01929a32cc765ede53ef879 100644 (file)
@@ -1126,7 +1126,6 @@ static void aoa_fabric_layout_remove(struct soundbus_dev *sdev)
        sdev->pcmname = NULL;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int aoa_fabric_layout_suspend(struct device *dev)
 {
        struct layout_dev *ldev = dev_get_drvdata(dev);
@@ -1147,11 +1146,9 @@ static int aoa_fabric_layout_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
        aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
 
-#endif
-
 static struct soundbus_driver aoa_soundbus_driver = {
        .name = "snd_aoa_soundbus_drv",
        .owner = THIS_MODULE,
@@ -1159,9 +1156,7 @@ static struct soundbus_driver aoa_soundbus_driver = {
        .remove = aoa_fabric_layout_remove,
        .driver = {
                .owner = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
                .pm = &aoa_fabric_layout_pm_ops,
-#endif
        }
 };
 
index 8f24a3eea16bd34f8c890248538749758fab0cb7..2a295f6105942813b9541b9c0a287218c7e6b3a9 100644 (file)
@@ -127,7 +127,7 @@ static void soundbus_device_shutdown(struct device *dev)
 
 /* soundbus_dev_attrs is declared in sysfs.c */
 ATTRIBUTE_GROUPS(soundbus_dev);
-static struct bus_type soundbus_bus_type = {
+static const struct bus_type soundbus_bus_type = {
        .name           = "aoa-soundbus",
        .probe          = soundbus_probe,
        .uevent         = soundbus_uevent,
index 0817ad21af74f4b0e19ac77e60b2a06b488a808e..c3340b8ff3dafe683416f5e34ce122206543b97d 100644 (file)
@@ -737,10 +737,8 @@ static const struct snd_pcm_ops aaci_capture_ops = {
 /*
  * Power Management.
  */
-#ifdef CONFIG_PM
 static int aaci_do_suspend(struct snd_card *card)
 {
-       struct aaci *aaci = card->private_data;
        snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
        return 0;
 }
@@ -763,12 +761,7 @@ static int aaci_resume(struct device *dev)
        return card ? aaci_do_resume(card) : 0;
 }
 
-static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
-#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops)
-#else
-#define AACI_DEV_PM_OPS NULL
-#endif
-
+static DEFINE_SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
 
 static const struct ac97_pcm ac97_defs[] = {
        [0] = { /* Front PCM */
@@ -1081,7 +1074,7 @@ MODULE_DEVICE_TABLE(amba, aaci_ids);
 static struct amba_driver aaci_driver = {
        .drv            = {
                .name   = DRIVER_NAME,
-               .pm     = AACI_DEV_PM_OPS,
+               .pm     = &aaci_dev_pm_ops,
        },
        .probe          = aaci_probe,
        .remove         = aaci_remove,
index 2d83ad91f96800538fe0b4aea9e16d4274d0b03d..4c367e73b2c9b3e35d7a4623935c8266cd92a203 100644 (file)
@@ -111,8 +111,6 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
        return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
 }
 
-#ifdef CONFIG_PM_SLEEP
-
 static int pxa2xx_ac97_do_suspend(struct snd_card *card)
 {
        pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
@@ -164,8 +162,7 @@ static int pxa2xx_ac97_resume(struct device *dev)
        return ret;
 }
 
-static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
-#endif
+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,
@@ -277,9 +274,7 @@ static struct platform_driver pxa2xx_ac97_driver = {
        .remove_new     = pxa2xx_ac97_remove,
        .driver         = {
                .name   = "pxa2xx-ac97",
-#ifdef CONFIG_PM_SLEEP
                .pm     = &pxa2xx_ac97_pm_ops,
-#endif
        },
 };
 
index e41818e59a1585e1bd164e094118e0e9d5ffce2c..8077f481d84fd6baf3be305b2b6936575d937a3a 100644 (file)
@@ -39,6 +39,23 @@ config SND_UMP_LEGACY_RAWMIDI
          legacy MIDI 1.0 byte streams is created for each UMP Endpoint.
          The device contains 16 substreams corresponding to UMP groups.
 
+config SND_CORE_TEST
+       tristate "Sound core KUnit test"
+       depends on KUNIT
+       select SND_PCM
+       default KUNIT_ALL_TESTS
+       help
+         This options enables the sound core functions KUnit test.
+
+         KUnit tests run during boot and output the results to the debug
+         log in TAP format (https://testanything.org/). Only useful for
+         kernel devs running KUnit test harness and are not for inclusion
+         into a production build.
+
+         For more information on KUnit and unit tests in general, refer
+         to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+
 config SND_COMPRESS_OFFLOAD
        tristate
 
index f6526b33713756071c14d4da2c7e051d1ae17bf9..b8aa886198ab084d38ca2fefca8afe88154752c3 100644 (file)
@@ -48,6 +48,8 @@ obj-$(CONFIG_SND_SEQ_DEVICE)  += snd-seq-device.o
 obj-$(CONFIG_SND_RAWMIDI)      += snd-rawmidi.o
 obj-$(CONFIG_SND_UMP)          += snd-ump.o
 
+obj-$(CONFIG_SND_CORE_TEST)    += sound_kunit.o
+
 obj-$(CONFIG_SND_OSSEMUL)      += oss/
 obj-$(CONFIG_SND_SEQUENCER)    += seq/
 
index 619371aa9964dcb23f24bbae1658fb891b11ca20..f0008fa2d8396c8e898829a41360dc11eed19b1d 100644 (file)
@@ -127,9 +127,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
        init_waitqueue_head(&runtime->sleep);
        data->stream.runtime = runtime;
        f->private_data = (void *)data;
-       mutex_lock(&compr->lock);
-       ret = compr->ops->open(&data->stream);
-       mutex_unlock(&compr->lock);
+       scoped_guard(mutex, &compr->lock)
+               ret = compr->ops->open(&data->stream);
        if (ret) {
                kfree(runtime);
                kfree(data);
@@ -288,7 +287,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
                return -EFAULT;
 
        stream = &data->stream;
-       mutex_lock(&stream->device->lock);
+       guard(mutex)(&stream->device->lock);
        /* write is allowed when stream is running or has been steup */
        switch (stream->runtime->state) {
        case SNDRV_PCM_STATE_SETUP:
@@ -296,7 +295,6 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
        case SNDRV_PCM_STATE_RUNNING:
                break;
        default:
-               mutex_unlock(&stream->device->lock);
                return -EBADFD;
        }
 
@@ -322,7 +320,6 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
                pr_debug("stream prepared, Houston we are good to go\n");
        }
 
-       mutex_unlock(&stream->device->lock);
        return retval;
 }
 
@@ -339,7 +336,7 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
                return -EFAULT;
 
        stream = &data->stream;
-       mutex_lock(&stream->device->lock);
+       guard(mutex)(&stream->device->lock);
 
        /* read is allowed when stream is running, paused, draining and setup
         * (yes setup is state which we transition to after stop, so if user
@@ -350,11 +347,9 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
        case SNDRV_PCM_STATE_PREPARED:
        case SNDRV_PCM_STATE_SUSPENDED:
        case SNDRV_PCM_STATE_DISCONNECTED:
-               retval = -EBADFD;
-               goto out;
+               return -EBADFD;
        case SNDRV_PCM_STATE_XRUN:
-               retval = -EPIPE;
-               goto out;
+               return -EPIPE;
        }
 
        avail = snd_compr_get_avail(stream);
@@ -363,17 +358,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
        if (avail > count)
                avail = count;
 
-       if (stream->ops->copy) {
+       if (stream->ops->copy)
                retval = stream->ops->copy(stream, buf, avail);
-       } else {
-               retval = -ENXIO;
-               goto out;
-       }
+       else
+               return -ENXIO;
        if (retval > 0)
                stream->runtime->total_bytes_transferred += retval;
 
-out:
-       mutex_unlock(&stream->device->lock);
        return retval;
 }
 
@@ -402,13 +393,12 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
 
        stream = &data->stream;
 
-       mutex_lock(&stream->device->lock);
+       guard(mutex)(&stream->device->lock);
 
        switch (stream->runtime->state) {
        case SNDRV_PCM_STATE_OPEN:
        case SNDRV_PCM_STATE_XRUN:
-               retval = snd_compr_get_poll(stream) | EPOLLERR;
-               goto out;
+               return snd_compr_get_poll(stream) | EPOLLERR;
        default:
                break;
        }
@@ -433,11 +423,9 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
                        retval = snd_compr_get_poll(stream);
                break;
        default:
-               retval = snd_compr_get_poll(stream) | EPOLLERR;
-               break;
+               return snd_compr_get_poll(stream) | EPOLLERR;
        }
-out:
-       mutex_unlock(&stream->device->lock);
+
        return retval;
 }
 
@@ -465,7 +453,7 @@ static int
 snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
 {
        int retval;
-       struct snd_compr_codec_caps *caps;
+       struct snd_compr_codec_caps *caps __free(kfree) = NULL;
 
        if (!stream->ops->get_codec_caps)
                return -ENXIO;
@@ -476,12 +464,9 @@ snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
 
        retval = stream->ops->get_codec_caps(stream, caps);
        if (retval)
-               goto out;
+               return retval;
        if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
-               retval = -EFAULT;
-
-out:
-       kfree(caps);
+               return -EFAULT;
        return retval;
 }
 #endif /* !COMPR_CODEC_CAPS_OVERFLOW */
@@ -586,7 +571,7 @@ static int snd_compress_check_input(struct snd_compr_params *params)
 static int
 snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
 {
-       struct snd_compr_params *params;
+       struct snd_compr_params *params __free(kfree) = NULL;
        int retval;
 
        if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
@@ -596,24 +581,22 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
                 */
                params = memdup_user((void __user *)arg, sizeof(*params));
                if (IS_ERR(params))
-                       return PTR_ERR(params);
+                       return PTR_ERR(no_free_ptr(params));
 
                retval = snd_compress_check_input(params);
                if (retval)
-                       goto out;
+                       return retval;
 
                retval = snd_compr_allocate_buffer(stream, params);
-               if (retval) {
-                       retval = -ENOMEM;
-                       goto out;
-               }
+               if (retval)
+                       return -ENOMEM;
 
                retval = stream->ops->set_params(stream, params);
                if (retval)
-                       goto out;
+                       return retval;
 
                if (stream->next_track)
-                       goto out;
+                       return retval;
 
                stream->metadata_set = false;
                stream->next_track = false;
@@ -622,15 +605,13 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
        } else {
                return -EPERM;
        }
-out:
-       kfree(params);
        return retval;
 }
 
 static int
 snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
 {
-       struct snd_codec *params;
+       struct snd_codec *params __free(kfree) = NULL;
        int retval;
 
        if (!stream->ops->get_params)
@@ -641,12 +622,9 @@ snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
                return -ENOMEM;
        retval = stream->ops->get_params(stream, params);
        if (retval)
-               goto out;
+               return retval;
        if (copy_to_user((char __user *)arg, params, sizeof(*params)))
-               retval = -EFAULT;
-
-out:
-       kfree(params);
+               return -EFAULT;
        return retval;
 }
 
@@ -805,12 +783,10 @@ static void error_delayed_work(struct work_struct *work)
 
        stream = container_of(work, struct snd_compr_stream, error_work.work);
 
-       mutex_lock(&stream->device->lock);
+       guard(mutex)(&stream->device->lock);
 
        stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
        wake_up(&stream->runtime->sleep);
-
-       mutex_unlock(&stream->device->lock);
 }
 
 /**
@@ -967,70 +943,52 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 {
        struct snd_compr_file *data = f->private_data;
        struct snd_compr_stream *stream;
-       int retval = -ENOTTY;
 
        if (snd_BUG_ON(!data))
                return -EFAULT;
 
        stream = &data->stream;
 
-       mutex_lock(&stream->device->lock);
+       guard(mutex)(&stream->device->lock);
        switch (_IOC_NR(cmd)) {
        case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
-               retval = put_user(SNDRV_COMPRESS_VERSION,
+               return put_user(SNDRV_COMPRESS_VERSION,
                                (int __user *)arg) ? -EFAULT : 0;
-               break;
        case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
-               retval = snd_compr_get_caps(stream, arg);
-               break;
+               return snd_compr_get_caps(stream, arg);
 #ifndef COMPR_CODEC_CAPS_OVERFLOW
        case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
-               retval = snd_compr_get_codec_caps(stream, arg);
-               break;
+               return snd_compr_get_codec_caps(stream, arg);
 #endif
        case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
-               retval = snd_compr_set_params(stream, arg);
-               break;
+               return snd_compr_set_params(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
-               retval = snd_compr_get_params(stream, arg);
-               break;
+               return snd_compr_get_params(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
-               retval = snd_compr_set_metadata(stream, arg);
-               break;
+               return snd_compr_set_metadata(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
-               retval = snd_compr_get_metadata(stream, arg);
-               break;
+               return snd_compr_get_metadata(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
-               retval = snd_compr_tstamp(stream, arg);
-               break;
+               return snd_compr_tstamp(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_AVAIL):
-               retval = snd_compr_ioctl_avail(stream, arg);
-               break;
+               return snd_compr_ioctl_avail(stream, arg);
        case _IOC_NR(SNDRV_COMPRESS_PAUSE):
-               retval = snd_compr_pause(stream);
-               break;
+               return snd_compr_pause(stream);
        case _IOC_NR(SNDRV_COMPRESS_RESUME):
-               retval = snd_compr_resume(stream);
-               break;
+               return snd_compr_resume(stream);
        case _IOC_NR(SNDRV_COMPRESS_START):
-               retval = snd_compr_start(stream);
-               break;
+               return snd_compr_start(stream);
        case _IOC_NR(SNDRV_COMPRESS_STOP):
-               retval = snd_compr_stop(stream);
-               break;
+               return snd_compr_stop(stream);
        case _IOC_NR(SNDRV_COMPRESS_DRAIN):
-               retval = snd_compr_drain(stream);
-               break;
+               return snd_compr_drain(stream);
        case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
-               retval = snd_compr_partial_drain(stream);
-               break;
+               return snd_compr_partial_drain(stream);
        case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
-               retval = snd_compr_next_track(stream);
-               break;
-
+               return snd_compr_next_track(stream);
        }
-       mutex_unlock(&stream->device->lock);
-       return retval;
+
+       return -ENOTTY;
 }
 
 /* support of 32bit userspace on 64bit platforms */
index 59c8658966d4cb37cc571c4d21da8c2dc57a4ee7..8367fd4853716af763cfe648e5619f6364d2ce1d 100644 (file)
@@ -44,7 +44,6 @@ static int snd_ctl_remove_locked(struct snd_card *card,
 
 static int snd_ctl_open(struct inode *inode, struct file *file)
 {
-       unsigned long flags;
        struct snd_card *card;
        struct snd_ctl_file *ctl;
        int i, err;
@@ -80,9 +79,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
                ctl->preferred_subdevice[i] = -1;
        ctl->pid = get_pid(task_pid(current));
        file->private_data = ctl;
-       write_lock_irqsave(&card->ctl_files_rwlock, flags);
-       list_add_tail(&ctl->list, &card->ctl_files);
-       write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+       scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+               list_add_tail(&ctl->list, &card->ctl_files);
        snd_card_unref(card);
        return 0;
 
@@ -98,21 +96,18 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
 
 static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
 {
-       unsigned long flags;
        struct snd_kctl_event *cread;
 
-       spin_lock_irqsave(&ctl->read_lock, flags);
+       guard(spinlock_irqsave)(&ctl->read_lock);
        while (!list_empty(&ctl->events)) {
                cread = snd_kctl_event(ctl->events.next);
                list_del(&cread->list);
                kfree(cread);
        }
-       spin_unlock_irqrestore(&ctl->read_lock, flags);
 }
 
 static int snd_ctl_release(struct inode *inode, struct file *file)
 {
-       unsigned long flags;
        struct snd_card *card;
        struct snd_ctl_file *ctl;
        struct snd_kcontrol *control;
@@ -121,15 +116,17 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
        ctl = file->private_data;
        file->private_data = NULL;
        card = ctl->card;
-       write_lock_irqsave(&card->ctl_files_rwlock, flags);
-       list_del(&ctl->list);
-       write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
-       down_write(&card->controls_rwsem);
-       list_for_each_entry(control, &card->controls, list)
-               for (idx = 0; idx < control->count; idx++)
-                       if (control->vd[idx].owner == ctl)
-                               control->vd[idx].owner = NULL;
-       up_write(&card->controls_rwsem);
+
+       scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+               list_del(&ctl->list);
+
+       scoped_guard(rwsem_write, &card->controls_rwsem) {
+               list_for_each_entry(control, &card->controls, list)
+                       for (idx = 0; idx < control->count; idx++)
+                               if (control->vd[idx].owner == ctl)
+                                       control->vd[idx].owner = NULL;
+       }
+
        snd_fasync_free(ctl->fasync);
        snd_ctl_empty_read_queue(ctl);
        put_pid(ctl->pid);
@@ -152,7 +149,6 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
 void snd_ctl_notify(struct snd_card *card, unsigned int mask,
                    struct snd_ctl_elem_id *id)
 {
-       unsigned long flags;
        struct snd_ctl_file *ctl;
        struct snd_kctl_event *ev;
 
@@ -160,34 +156,34 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
                return;
        if (card->shutdown)
                return;
-       read_lock_irqsave(&card->ctl_files_rwlock, flags);
+
+       guard(read_lock_irqsave)(&card->ctl_files_rwlock);
 #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
        card->mixer_oss_change_count++;
 #endif
        list_for_each_entry(ctl, &card->ctl_files, list) {
                if (!ctl->subscribed)
                        continue;
-               spin_lock(&ctl->read_lock);
-               list_for_each_entry(ev, &ctl->events, list) {
-                       if (ev->id.numid == id->numid) {
-                               ev->mask |= mask;
-                               goto _found;
+               scoped_guard(spinlock, &ctl->read_lock) {
+                       list_for_each_entry(ev, &ctl->events, list) {
+                               if (ev->id.numid == id->numid) {
+                                       ev->mask |= mask;
+                                       goto _found;
+                               }
                        }
+                       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+                       if (ev) {
+                               ev->id = *id;
+                               ev->mask = mask;
+                               list_add_tail(&ev->list, &ctl->events);
+                       } else {
+                               dev_err(card->dev, "No memory available to allocate event\n");
+                       }
+_found:
+                       wake_up(&ctl->change_sleep);
                }
-               ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-               if (ev) {
-                       ev->id = *id;
-                       ev->mask = mask;
-                       list_add_tail(&ev->list, &ctl->events);
-               } else {
-                       dev_err(card->dev, "No memory available to allocate event\n");
-               }
-       _found:
-               wake_up(&ctl->change_sleep);
-               spin_unlock(&ctl->read_lock);
                snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
        }
-       read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
 }
 EXPORT_SYMBOL(snd_ctl_notify);
 
@@ -210,10 +206,9 @@ void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
        id.index += ioff;
        id.numid += ioff;
        snd_ctl_notify(card, mask, &id);
-       down_read(&snd_ctl_layer_rwsem);
+       guard(rwsem_read)(&snd_ctl_layer_rwsem);
        for (lops = snd_ctl_layer; lops; lops = lops->next)
                lops->lnotify(card, mask, kctl, ioff);
-       up_read(&snd_ctl_layer_rwsem);
 }
 EXPORT_SYMBOL(snd_ctl_notify_one);
 
@@ -520,9 +515,9 @@ static int snd_ctl_add_replace(struct snd_card *card,
        if (snd_BUG_ON(!card || !kcontrol->info))
                goto error;
 
-       down_write(&card->controls_rwsem);
-       err = __snd_ctl_add_replace(card, kcontrol, mode);
-       up_write(&card->controls_rwsem);
+       scoped_guard(rwsem_write, &card->controls_rwsem)
+               err = __snd_ctl_add_replace(card, kcontrol, mode);
+
        if (err < 0)
                goto error;
        return 0;
@@ -616,12 +611,8 @@ static inline int snd_ctl_remove_locked(struct snd_card *card,
  */
 int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
 {
-       int ret;
-
-       down_write(&card->controls_rwsem);
-       ret = snd_ctl_remove_locked(card, kcontrol);
-       up_write(&card->controls_rwsem);
-       return ret;
+       guard(rwsem_write)(&card->controls_rwsem);
+       return snd_ctl_remove_locked(card, kcontrol);
 }
 EXPORT_SYMBOL(snd_ctl_remove);
 
@@ -638,17 +629,12 @@ EXPORT_SYMBOL(snd_ctl_remove);
 int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
 {
        struct snd_kcontrol *kctl;
-       int ret;
 
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, id);
-       if (kctl == NULL) {
-               up_write(&card->controls_rwsem);
+       if (kctl == NULL)
                return -ENOENT;
-       }
-       ret = snd_ctl_remove_locked(card, kctl);
-       up_write(&card->controls_rwsem);
-       return ret;
+       return snd_ctl_remove_locked(card, kctl);
 }
 EXPORT_SYMBOL(snd_ctl_remove_id);
 
@@ -667,27 +653,18 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
 {
        struct snd_card *card = file->card;
        struct snd_kcontrol *kctl;
-       int idx, ret;
+       int idx;
 
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, id);
-       if (kctl == NULL) {
-               ret = -ENOENT;
-               goto error;
-       }
-       if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
-               ret = -EINVAL;
-               goto error;
-       }
+       if (kctl == NULL)
+               return -ENOENT;
+       if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER))
+               return -EINVAL;
        for (idx = 0; idx < kctl->count; idx++)
-               if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
-                       ret = -EBUSY;
-                       goto error;
-               }
-       ret = snd_ctl_remove_locked(card, kctl);
-error:
-       up_write(&card->controls_rwsem);
-       return ret;
+               if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file)
+                       return -EBUSY;
+       return snd_ctl_remove_locked(card, kctl);
 }
 
 /**
@@ -764,18 +741,15 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
        struct snd_kcontrol *kctl;
        int saved_numid;
 
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, src_id);
-       if (kctl == NULL) {
-               up_write(&card->controls_rwsem);
+       if (kctl == NULL)
                return -ENOENT;
-       }
        saved_numid = kctl->id.numid;
        remove_hash_entries(card, kctl);
        kctl->id = *dst_id;
        kctl->id.numid = saved_numid;
        add_hash_entries(card, kctl);
-       up_write(&card->controls_rwsem);
        return 0;
 }
 EXPORT_SYMBOL(snd_ctl_rename_id);
@@ -793,7 +767,7 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
 void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
                    const char *name)
 {
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        remove_hash_entries(card, kctl);
 
        if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -801,7 +775,6 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
                        name, kctl->id.name);
 
        add_hash_entries(card, kctl);
-       up_write(&card->controls_rwsem);
 }
 EXPORT_SYMBOL(snd_ctl_rename);
 
@@ -859,12 +832,8 @@ EXPORT_SYMBOL(snd_ctl_find_numid_locked);
 struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
                                        unsigned int numid)
 {
-       struct snd_kcontrol *kctl;
-
-       down_read(&card->controls_rwsem);
-       kctl = snd_ctl_find_numid_locked(card, numid);
-       up_read(&card->controls_rwsem);
-       return kctl;
+       guard(rwsem_read)(&card->controls_rwsem);
+       return snd_ctl_find_numid_locked(card, numid);
 }
 EXPORT_SYMBOL(snd_ctl_find_numid);
 
@@ -920,37 +889,30 @@ EXPORT_SYMBOL(snd_ctl_find_id_locked);
 struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
                                     const struct snd_ctl_elem_id *id)
 {
-       struct snd_kcontrol *kctl;
-
-       down_read(&card->controls_rwsem);
-       kctl = snd_ctl_find_id_locked(card, id);
-       up_read(&card->controls_rwsem);
-       return kctl;
+       guard(rwsem_read)(&card->controls_rwsem);
+       return snd_ctl_find_id_locked(card, id);
 }
 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;
+       struct snd_ctl_card_info *info __free(kfree) = NULL;
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (! info)
                return -ENOMEM;
-       down_read(&snd_ioctl_rwsem);
-       info->card = card->number;
-       strscpy(info->id, card->id, sizeof(info->id));
-       strscpy(info->driver, card->driver, sizeof(info->driver));
-       strscpy(info->name, card->shortname, sizeof(info->name));
-       strscpy(info->longname, card->longname, sizeof(info->longname));
-       strscpy(info->mixername, card->mixername, sizeof(info->mixername));
-       strscpy(info->components, card->components, sizeof(info->components));
-       up_read(&snd_ioctl_rwsem);
-       if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
-               kfree(info);
+       scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+               info->card = card->number;
+               strscpy(info->id, card->id, sizeof(info->id));
+               strscpy(info->driver, card->driver, sizeof(info->driver));
+               strscpy(info->name, card->shortname, sizeof(info->name));
+               strscpy(info->longname, card->longname, sizeof(info->longname));
+               strscpy(info->mixername, card->mixername, sizeof(info->mixername));
+               strscpy(info->components, card->components, sizeof(info->components));
+       }
+       if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
                return -EFAULT;
-       }
-       kfree(info);
        return 0;
 }
 
@@ -960,37 +922,31 @@ static int snd_ctl_elem_list(struct snd_card *card,
        struct snd_kcontrol *kctl;
        struct snd_ctl_elem_id id;
        unsigned int offset, space, jidx;
-       int err = 0;
 
        offset = list->offset;
        space = list->space;
 
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        list->count = card->controls_count;
        list->used = 0;
-       if (space > 0) {
-               list_for_each_entry(kctl, &card->controls, list) {
-                       if (offset >= kctl->count) {
-                               offset -= kctl->count;
-                               continue;
-                       }
-                       for (jidx = offset; jidx < kctl->count; jidx++) {
-                               snd_ctl_build_ioff(&id, kctl, jidx);
-                               if (copy_to_user(list->pids + list->used, &id,
-                                                sizeof(id))) {
-                                       err = -EFAULT;
-                                       goto out;
-                               }
-                               list->used++;
-                               if (!--space)
-                                       goto out;
-                       }
-                       offset = 0;
+       if (!space)
+               return 0;
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (offset >= kctl->count) {
+                       offset -= kctl->count;
+                       continue;
                }
+               for (jidx = offset; jidx < kctl->count; jidx++) {
+                       snd_ctl_build_ioff(&id, kctl, jidx);
+                       if (copy_to_user(list->pids + list->used, &id, sizeof(id)))
+                               return -EFAULT;
+                       list->used++;
+                       if (!--space)
+                               return 0;
+               }
+               offset = 0;
        }
- out:
-       up_read(&card->controls_rwsem);
-       return err;
+       return 0;
 }
 
 static int snd_ctl_elem_list_user(struct snd_card *card,
@@ -1238,16 +1194,12 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
 {
        struct snd_card *card = ctl->card;
        struct snd_kcontrol *kctl;
-       int result;
 
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, &info->id);
-       if (kctl == NULL)
-               result = -ENOENT;
-       else
-               result = __snd_ctl_elem_info(card, kctl, info, ctl);
-       up_read(&card->controls_rwsem);
-       return result;
+       if (!kctl)
+               return -ENOENT;
+       return __snd_ctl_elem_info(card, kctl, info, ctl);
 }
 
 static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
@@ -1279,19 +1231,15 @@ static int snd_ctl_elem_read(struct snd_card *card,
        const u32 pattern = 0xdeadbeef;
        int ret;
 
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, &control->id);
-       if (kctl == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       if (!kctl)
+               return -ENOENT;
 
        index_offset = snd_ctl_get_ioff(kctl, &control->id);
        vd = &kctl->vd[index_offset];
-       if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
-               ret = -EPERM;
-               goto unlock;
-       }
+       if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || !kctl->get)
+               return -EPERM;
 
        snd_ctl_build_ioff(&control->id, kctl, index_offset);
 
@@ -1301,7 +1249,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
        info.id = control->id;
        ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
        if (ret < 0)
-               goto unlock;
+               return ret;
 #endif
 
        if (!snd_ctl_skip_validation(&info))
@@ -1311,7 +1259,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
                ret = kctl->get(kctl, control);
        snd_power_unref(card);
        if (ret < 0)
-               goto unlock;
+               return ret;
        if (!snd_ctl_skip_validation(&info) &&
            sanity_check_elem_value(card, control, &info, pattern) < 0) {
                dev_err(card->dev,
@@ -1319,12 +1267,9 @@ static int snd_ctl_elem_read(struct snd_card *card,
                        control->id.iface, control->id.device,
                        control->id.subdevice, control->id.name,
                        control->id.index);
-               ret = -EINVAL;
-               goto unlock;
+               return -EINVAL;
        }
-unlock:
-       up_read(&card->controls_rwsem);
-       return ret;
+       return 0;
 }
 
 static int snd_ctl_elem_read_user(struct snd_card *card,
@@ -1339,12 +1284,10 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
 
        result = snd_ctl_elem_read(card, control);
        if (result < 0)
-               goto error;
+               return result;
 
        if (copy_to_user(_control, control, sizeof(*control)))
-               result = -EFAULT;
- error:
-       kfree(control);
+               return -EFAULT;
        return result;
 }
 
@@ -1406,23 +1349,21 @@ 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;
+       struct snd_ctl_elem_value *control __free(kfree) = NULL;
        struct snd_card *card;
        int result;
 
        control = memdup_user(_control, sizeof(*control));
        if (IS_ERR(control))
-               return PTR_ERR(control);
+               return PTR_ERR(no_free_ptr(control));
 
        card = file->card;
        result = snd_ctl_elem_write(card, file, control);
        if (result < 0)
-               goto error;
+               return result;
 
        if (copy_to_user(_control, control, sizeof(*control)))
-               result = -EFAULT;
- error:
-       kfree(control);
+               return -EFAULT;
        return result;
 }
 
@@ -1433,25 +1374,18 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
        struct snd_ctl_elem_id id;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_volatile *vd;
-       int result;
 
        if (copy_from_user(&id, _id, sizeof(id)))
                return -EFAULT;
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, &id);
-       if (kctl == NULL) {
-               result = -ENOENT;
-       } else {
-               vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
-               if (vd->owner != NULL)
-                       result = -EBUSY;
-               else {
-                       vd->owner = file;
-                       result = 0;
-               }
-       }
-       up_write(&card->controls_rwsem);
-       return result;
+       if (!kctl)
+               return -ENOENT;
+       vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+       if (vd->owner)
+               return -EBUSY;
+       vd->owner = file;
+       return 0;
 }
 
 static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
@@ -1461,27 +1395,20 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
        struct snd_ctl_elem_id id;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_volatile *vd;
-       int result;
 
        if (copy_from_user(&id, _id, sizeof(id)))
                return -EFAULT;
-       down_write(&card->controls_rwsem);
+       guard(rwsem_write)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, &id);
-       if (kctl == NULL) {
-               result = -ENOENT;
-       } else {
-               vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
-               if (vd->owner == NULL)
-                       result = -EINVAL;
-               else if (vd->owner != file)
-                       result = -EPERM;
-               else {
-                       vd->owner = NULL;
-                       result = 0;
-               }
-       }
-       up_write(&card->controls_rwsem);
-       return result;
+       if (!kctl)
+               return -ENOENT;
+       vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+       if (!vd->owner)
+               return -EINVAL;
+       if (vd->owner != file)
+               return -EPERM;
+       vd->owner = NULL;
+       return 0;
 }
 
 struct user_element {
@@ -1763,11 +1690,9 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
        private_size = value_sizes[info->type] * info->count;
        alloc_size = compute_user_elem_size(private_size, count);
 
-       down_write(&card->controls_rwsem);
-       if (check_user_elem_overflow(card, alloc_size)) {
-               err = -ENOMEM;
-               goto unlock;
-       }
+       guard(rwsem_write)(&card->controls_rwsem);
+       if (check_user_elem_overflow(card, alloc_size))
+               return -ENOMEM;
 
        /*
         * Keep memory object for this userspace control. After passing this
@@ -1777,13 +1702,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
         */
        err = snd_ctl_new(&kctl, count, access, file);
        if (err < 0)
-               goto unlock;
+               return err;
        memcpy(&kctl->id, &info->id, sizeof(kctl->id));
        ue = kzalloc(alloc_size, GFP_KERNEL);
        if (!ue) {
                kfree(kctl);
-               err = -ENOMEM;
-               goto unlock;
+               return -ENOMEM;
        }
        kctl->private_data = ue;
        kctl->private_free = snd_ctl_elem_user_free;
@@ -1801,7 +1725,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
                err = snd_ctl_elem_init_enum_names(ue);
                if (err < 0) {
                        snd_ctl_free_one(kctl);
-                       goto unlock;
+                       return err;
                }
        }
 
@@ -1821,7 +1745,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
        err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
        if (err < 0) {
                snd_ctl_free_one(kctl);
-               goto unlock;
+               return err;
        }
        offset = snd_ctl_get_ioff(kctl, &info->id);
        snd_ctl_build_ioff(&info->id, kctl, offset);
@@ -1832,9 +1756,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
         * applications because the field originally means PID of a process
         * which locks the element.
         */
- unlock:
-       up_write(&card->controls_rwsem);
-       return err;
+       return 0;
 }
 
 static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
@@ -2036,34 +1958,29 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
                return snd_ctl_subscribe_events(ctl, ip);
        case SNDRV_CTL_IOCTL_TLV_READ:
-               down_read(&ctl->card->controls_rwsem);
-               err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
-               up_read(&ctl->card->controls_rwsem);
+               scoped_guard(rwsem_read, &ctl->card->controls_rwsem)
+                       err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
                return err;
        case SNDRV_CTL_IOCTL_TLV_WRITE:
-               down_write(&ctl->card->controls_rwsem);
-               err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
-               up_write(&ctl->card->controls_rwsem);
+               scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+                       err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
                return err;
        case SNDRV_CTL_IOCTL_TLV_COMMAND:
-               down_write(&ctl->card->controls_rwsem);
-               err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
-               up_write(&ctl->card->controls_rwsem);
+               scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+                       err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
                return err;
        case SNDRV_CTL_IOCTL_POWER:
                return -ENOPROTOOPT;
        case SNDRV_CTL_IOCTL_POWER_STATE:
                return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
        }
-       down_read(&snd_ioctl_rwsem);
+
+       guard(rwsem_read)(&snd_ioctl_rwsem);
        list_for_each_entry(p, &snd_control_ioctls, list) {
                err = p->fioctl(card, ctl, cmd, arg);
-               if (err != -ENOIOCTLCMD) {
-                       up_read(&snd_ioctl_rwsem);
+               if (err != -ENOIOCTLCMD)
                        return err;
-               }
        }
-       up_read(&snd_ioctl_rwsem);
        dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
        return -ENOTTY;
 }
@@ -2155,9 +2072,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
        if (pn == NULL)
                return -ENOMEM;
        pn->fioctl = fcn;
-       down_write(&snd_ioctl_rwsem);
+       guard(rwsem_write)(&snd_ioctl_rwsem);
        list_add_tail(&pn->list, lists);
-       up_write(&snd_ioctl_rwsem);
        return 0;
 }
 
@@ -2200,16 +2116,14 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
 
        if (snd_BUG_ON(!fcn))
                return -EINVAL;
-       down_write(&snd_ioctl_rwsem);
+       guard(rwsem_write)(&snd_ioctl_rwsem);
        list_for_each_entry(p, lists, list) {
                if (p->fioctl == fcn) {
                        list_del(&p->list);
-                       up_write(&snd_ioctl_rwsem);
                        kfree(p);
                        return 0;
                }
        }
-       up_write(&snd_ioctl_rwsem);
        snd_BUG();
        return -EINVAL;
 }
@@ -2256,9 +2170,8 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
 {
        struct snd_ctl_file *kctl;
        int subdevice = -1;
-       unsigned long flags;
 
-       read_lock_irqsave(&card->ctl_files_rwlock, flags);
+       guard(read_lock_irqsave)(&card->ctl_files_rwlock);
        list_for_each_entry(kctl, &card->ctl_files, list) {
                if (kctl->pid == task_pid(current)) {
                        subdevice = kctl->preferred_subdevice[type];
@@ -2266,7 +2179,6 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
                                break;
                }
        }
-       read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
        return subdevice;
 }
 EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
@@ -2296,13 +2208,11 @@ int snd_ctl_request_layer(const char *module_name)
 
        if (module_name == NULL)
                return 0;
-       down_read(&snd_ctl_layer_rwsem);
-       for (lops = snd_ctl_layer; lops; lops = lops->next)
-               if (strcmp(lops->module_name, module_name) == 0)
-                       break;
-       up_read(&snd_ctl_layer_rwsem);
-       if (lops)
-               return 0;
+       scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) {
+               for (lops = snd_ctl_layer; lops; lops = lops->next)
+                       if (strcmp(lops->module_name, module_name) == 0)
+                               return 0;
+       }
        return request_module(module_name);
 }
 EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
@@ -2319,16 +2229,15 @@ void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
        struct snd_card *card;
        int card_number;
 
-       down_write(&snd_ctl_layer_rwsem);
-       lops->next = snd_ctl_layer;
-       snd_ctl_layer = lops;
-       up_write(&snd_ctl_layer_rwsem);
+       scoped_guard(rwsem_write, &snd_ctl_layer_rwsem) {
+               lops->next = snd_ctl_layer;
+               snd_ctl_layer = lops;
+       }
        for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
                card = snd_card_ref(card_number);
                if (card) {
-                       down_read(&card->controls_rwsem);
-                       lops->lregister(card);
-                       up_read(&card->controls_rwsem);
+                       scoped_guard(rwsem_read, &card->controls_rwsem)
+                               lops->lregister(card);
                        snd_card_unref(card);
                }
        }
@@ -2347,7 +2256,7 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
 {
        struct snd_ctl_layer_ops *lops2, *prev_lops2;
 
-       down_write(&snd_ctl_layer_rwsem);
+       guard(rwsem_write)(&snd_ctl_layer_rwsem);
        for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) {
                if (lops2 == lops) {
                        if (!prev_lops2)
@@ -2358,7 +2267,6 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
                }
                prev_lops2 = lops2;
        }
-       up_write(&snd_ctl_layer_rwsem);
 }
 EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
 
@@ -2379,25 +2287,29 @@ static const struct file_operations snd_ctl_f_ops =
        .fasync =       snd_ctl_fasync,
 };
 
+/* call lops under rwsems; called from snd_ctl_dev_*() below() */
+#define call_snd_ctl_lops(_card, _op)                              \
+       do {                                                        \
+               struct snd_ctl_layer_ops *lops;                     \
+               guard(rwsem_read)(&(_card)->controls_rwsem);        \
+               guard(rwsem_read)(&snd_ctl_layer_rwsem);            \
+               for (lops = snd_ctl_layer; lops; lops = lops->next) \
+                       lops->_op(_card);                           \
+       } while (0)
+
 /*
  * registration of the control device
  */
 static int snd_ctl_dev_register(struct snd_device *device)
 {
        struct snd_card *card = device->device_data;
-       struct snd_ctl_layer_ops *lops;
        int err;
 
        err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
                                  &snd_ctl_f_ops, card, card->ctl_dev);
        if (err < 0)
                return err;
-       down_read(&card->controls_rwsem);
-       down_read(&snd_ctl_layer_rwsem);
-       for (lops = snd_ctl_layer; lops; lops = lops->next)
-               lops->lregister(card);
-       up_read(&snd_ctl_layer_rwsem);
-       up_read(&card->controls_rwsem);
+       call_snd_ctl_lops(card, lregister);
        return 0;
 }
 
@@ -2408,23 +2320,15 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
 {
        struct snd_card *card = device->device_data;
        struct snd_ctl_file *ctl;
-       struct snd_ctl_layer_ops *lops;
-       unsigned long flags;
 
-       read_lock_irqsave(&card->ctl_files_rwlock, flags);
-       list_for_each_entry(ctl, &card->ctl_files, list) {
-               wake_up(&ctl->change_sleep);
-               snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+       scoped_guard(read_lock_irqsave, &card->ctl_files_rwlock) {
+               list_for_each_entry(ctl, &card->ctl_files, list) {
+                       wake_up(&ctl->change_sleep);
+                       snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+               }
        }
-       read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
-
-       down_read(&card->controls_rwsem);
-       down_read(&snd_ctl_layer_rwsem);
-       for (lops = snd_ctl_layer; lops; lops = lops->next)
-               lops->ldisconnect(card);
-       up_read(&snd_ctl_layer_rwsem);
-       up_read(&card->controls_rwsem);
 
+       call_snd_ctl_lops(card, ldisconnect);
        return snd_unregister_device(card->ctl_dev);
 }
 
@@ -2436,17 +2340,17 @@ static int snd_ctl_dev_free(struct snd_device *device)
        struct snd_card *card = device->device_data;
        struct snd_kcontrol *control;
 
-       down_write(&card->controls_rwsem);
-       while (!list_empty(&card->controls)) {
-               control = snd_kcontrol(card->controls.next);
-               __snd_ctl_remove(card, control, false);
-       }
+       scoped_guard(rwsem_write, &card->controls_rwsem) {
+               while (!list_empty(&card->controls)) {
+                       control = snd_kcontrol(card->controls.next);
+                       __snd_ctl_remove(card, control, false);
+               }
 
 #ifdef CONFIG_SND_CTL_FAST_LOOKUP
-       xa_destroy(&card->ctl_numids);
-       xa_destroy(&card->ctl_hash);
+               xa_destroy(&card->ctl_numids);
+               xa_destroy(&card->ctl_hash);
 #endif
-       up_write(&card->controls_rwsem);
+       }
        put_device(card->ctl_dev);
        return 0;
 }
index 63d787501066c907dd5efcdccea52960e8a7c44b..934bb945e702a2090bb103c6e5e20a222d393dd7 100644 (file)
@@ -79,61 +79,56 @@ struct snd_ctl_elem_info32 {
 static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
                                    struct snd_ctl_elem_info32 __user *data32)
 {
-       struct snd_ctl_elem_info *data;
+       struct snd_ctl_elem_info *data __free(kfree) = NULL;
        int err;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (! data)
                return -ENOMEM;
 
-       err = -EFAULT;
        /* copy id */
        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
-               goto error;
+               return -EFAULT;
        /* we need to copy the item index.
         * hope this doesn't break anything..
         */
        if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
-               goto error;
+               return -EFAULT;
 
        err = snd_ctl_elem_info(ctl, data);
        if (err < 0)
-               goto error;
+               return err;
        /* restore info to 32bit */
-       err = -EFAULT;
        /* id, type, access, count */
        if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
            copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
-               goto error;
+               return -EFAULT;
        if (put_user(data->owner, &data32->owner))
-               goto error;
+               return -EFAULT;
        switch (data->type) {
        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
        case SNDRV_CTL_ELEM_TYPE_INTEGER:
                if (put_user(data->value.integer.min, &data32->value.integer.min) ||
                    put_user(data->value.integer.max, &data32->value.integer.max) ||
                    put_user(data->value.integer.step, &data32->value.integer.step))
-                       goto error;
+                       return -EFAULT;
                break;
        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
                if (copy_to_user(&data32->value.integer64,
                                 &data->value.integer64,
                                 sizeof(data->value.integer64)))
-                       goto error;
+                       return -EFAULT;
                break;
        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
                if (copy_to_user(&data32->value.enumerated,
                                 &data->value.enumerated,
                                 sizeof(data->value.enumerated)))
-                       goto error;
+                       return -EFAULT;
                break;
        default:
                break;
        }
-       err = 0;
- error:
-       kfree(data);
-       return err;
+       return 0;
 }
 
 /* read / write */
@@ -169,31 +164,25 @@ 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;
+       struct snd_ctl_elem_info *info __free(kfree) = NULL;
        int err;
 
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_id_locked(card, id);
-       if (! kctl) {
-               up_read(&card->controls_rwsem);
+       if (!kctl)
                return -ENOENT;
-       }
        info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (info == NULL) {
-               up_read(&card->controls_rwsem);
+       if (info == NULL)
                return -ENOMEM;
-       }
        info->id = *id;
        err = snd_power_ref_and_wait(card);
        if (!err)
                err = kctl->info(kctl, info);
        snd_power_unref(card);
-       up_read(&card->controls_rwsem);
        if (err >= 0) {
                err = info->type;
                *countp = info->count;
        }
-       kfree(info);
        return err;
 }
 
@@ -289,7 +278,7 @@ 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;
+       struct snd_ctl_elem_value *data __free(kfree) = NULL;
        int err, type, count;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -299,21 +288,18 @@ static int ctl_elem_read_user(struct snd_card *card,
        err = copy_ctl_value_from_user(card, data, userdata, valuep,
                                       &type, &count);
        if (err < 0)
-               goto error;
+               return err;
 
        err = snd_ctl_elem_read(card, data);
        if (err < 0)
-               goto error;
-       err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
- error:
-       kfree(data);
-       return err;
+               return err;
+       return copy_ctl_value_to_user(userdata, valuep, data, type, count);
 }
 
 static int ctl_elem_write_user(struct snd_ctl_file *file,
                               void __user *userdata, void __user *valuep)
 {
-       struct snd_ctl_elem_value *data;
+       struct snd_ctl_elem_value *data __free(kfree) = NULL;
        struct snd_card *card = file->card;
        int err, type, count;
 
@@ -324,15 +310,12 @@ static int ctl_elem_write_user(struct snd_ctl_file *file,
        err = copy_ctl_value_from_user(card, data, userdata, valuep,
                                       &type, &count);
        if (err < 0)
-               goto error;
+               return err;
 
        err = snd_ctl_elem_write(card, file, data);
        if (err < 0)
-               goto error;
-       err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
- error:
-       kfree(data);
-       return err;
+               return err;
+       return copy_ctl_value_to_user(userdata, valuep, data, type, count);
 }
 
 static int snd_ctl_elem_read_user_compat(struct snd_card *card,
@@ -366,49 +349,44 @@ 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;
-       int err;
+       struct snd_ctl_elem_info *data __free(kfree) = NULL;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (! data)
                return -ENOMEM;
 
-       err = -EFAULT;
        /* id, type, access, count */ \
        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
-               goto error;
+               return -EFAULT;
        if (get_user(data->owner, &data32->owner))
-               goto error;
+               return -EFAULT;
        switch (data->type) {
        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
        case SNDRV_CTL_ELEM_TYPE_INTEGER:
                if (get_user(data->value.integer.min, &data32->value.integer.min) ||
                    get_user(data->value.integer.max, &data32->value.integer.max) ||
                    get_user(data->value.integer.step, &data32->value.integer.step))
-                       goto error;
+                       return -EFAULT;
                break;
        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
                if (copy_from_user(&data->value.integer64,
                                   &data32->value.integer64,
                                   sizeof(data->value.integer64)))
-                       goto error;
+                       return -EFAULT;
                break;
        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
                if (copy_from_user(&data->value.enumerated,
                                   &data32->value.enumerated,
                                   sizeof(data->value.enumerated)))
-                       goto error;
+                       return -EFAULT;
                data->value.enumerated.names_ptr =
                        (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
                break;
        default:
                break;
        }
-       err = snd_ctl_elem_add(file, data, replace);
- error:
-       kfree(data);
-       return err;
+       return snd_ctl_elem_add(file, data, replace);
 }  
 
 enum {
@@ -468,16 +446,13 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
 #endif /* CONFIG_X86_X32_ABI */
        }
 
-       down_read(&snd_ioctl_rwsem);
+       guard(rwsem_read)(&snd_ioctl_rwsem);
        list_for_each_entry(p, &snd_control_compat_ioctls, list) {
                if (p->fioctl) {
                        err = p->fioctl(ctl->card, ctl, cmd, arg);
-                       if (err != -ENOIOCTLCMD) {
-                               up_read(&snd_ioctl_rwsem);
+                       if (err != -ENOIOCTLCMD)
                                return err;
-                       }
                }
        }
-       up_read(&snd_ioctl_rwsem);
        return -ENOIOCTLCMD;
 }
index a78eb48927c7bfa68354a6fa80dcb740f71c9ed5..3d37e9fa7b9c207d9b24fa3bcd4507550d727316 100644 (file)
@@ -147,29 +147,27 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
                return;
        route = -1;
        found = false;
-       mutex_lock(&snd_ctl_led_mutex);
-       /* the card may not be registered (active) at this point */
-       if (card && !snd_ctl_led_card_valid[card->number]) {
-               mutex_unlock(&snd_ctl_led_mutex);
-               return;
-       }
-       list_for_each_entry(lctl, &led->controls, list) {
-               if (lctl->kctl == kctl && lctl->index_offset == ioff)
-                       found = true;
-               UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
-       }
-       if (!found && kctl && card) {
-               lctl = kzalloc(sizeof(*lctl), GFP_KERNEL);
-               if (lctl) {
-                       lctl->card = card;
-                       lctl->access = access;
-                       lctl->kctl = kctl;
-                       lctl->index_offset = ioff;
-                       list_add(&lctl->list, &led->controls);
+       scoped_guard(mutex, &snd_ctl_led_mutex) {
+               /* the card may not be registered (active) at this point */
+               if (card && !snd_ctl_led_card_valid[card->number])
+                       return;
+               list_for_each_entry(lctl, &led->controls, list) {
+                       if (lctl->kctl == kctl && lctl->index_offset == ioff)
+                               found = true;
                        UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
                }
+               if (!found && kctl && card) {
+                       lctl = kzalloc(sizeof(*lctl), GFP_KERNEL);
+                       if (lctl) {
+                               lctl->card = card;
+                               lctl->access = access;
+                               lctl->kctl = kctl;
+                               lctl->index_offset = ioff;
+                               list_add(&lctl->list, &led->controls);
+                               UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
+                       }
+               }
        }
-       mutex_unlock(&snd_ctl_led_mutex);
        switch (led->mode) {
        case MODE_OFF:          route = 1; break;
        case MODE_ON:           route = 0; break;
@@ -201,14 +199,13 @@ static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int i
        struct snd_ctl_led_ctl *lctl;
        unsigned int ret = 0;
 
-       mutex_lock(&snd_ctl_led_mutex);
+       guard(mutex)(&snd_ctl_led_mutex);
        lctl = snd_ctl_led_find(kctl, ioff);
        if (lctl && (access == 0 || access != lctl->access)) {
                ret = lctl->access;
                list_del(&lctl->list);
                kfree(lctl);
        }
-       mutex_unlock(&snd_ctl_led_mutex);
        return ret;
 }
 
@@ -239,44 +236,36 @@ static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask,
        }
 }
 
+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;
+       struct snd_card *card __free(snd_card_unref) = NULL;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_volatile *vd;
        unsigned int ioff, access, new_access;
-       int err = 0;
 
        card = snd_card_ref(card_number);
-       if (card) {
-               down_write(&card->controls_rwsem);
-               kctl = snd_ctl_find_id_locked(card, id);
-               if (kctl) {
-                       ioff = snd_ctl_get_ioff(kctl, id);
-                       vd = &kctl->vd[ioff];
-                       access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
-                       if (access != 0 && access != group_to_access(group)) {
-                               err = -EXDEV;
-                               goto unlock;
-                       }
-                       new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
-                       if (set)
-                               new_access |= group_to_access(group);
-                       if (new_access != vd->access) {
-                               vd->access = new_access;
-                               snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff);
-                       }
-               } else {
-                       err = -ENOENT;
-               }
-unlock:
-               up_write(&card->controls_rwsem);
-               snd_card_unref(card);
-       } else {
-               err = -ENXIO;
+       if (!card)
+               return -ENXIO;
+       guard(rwsem_write)(&card->controls_rwsem);
+       kctl = snd_ctl_find_id_locked(card, id);
+       if (!kctl)
+               return -ENOENT;
+       ioff = snd_ctl_get_ioff(kctl, id);
+       vd = &kctl->vd[ioff];
+       access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+       if (access != 0 && access != group_to_access(group))
+               return -EXDEV;
+       new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+       if (set)
+               new_access |= group_to_access(group);
+       if (new_access != vd->access) {
+               vd->access = new_access;
+               snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff);
        }
-       return err;
+       return 0;
 }
 
 static void snd_ctl_led_refresh(void)
@@ -312,7 +301,7 @@ repeat:
 
 static int snd_ctl_led_reset(int card_number, unsigned int group)
 {
-       struct snd_card *card;
+       struct snd_card *card __free(snd_card_unref) = NULL;
        struct snd_ctl_led *led;
        struct snd_ctl_led_ctl *lctl;
        struct snd_kcontrol_volatile *vd;
@@ -322,26 +311,22 @@ static int snd_ctl_led_reset(int card_number, unsigned int group)
        if (!card)
                return -ENXIO;
 
-       mutex_lock(&snd_ctl_led_mutex);
-       if (!snd_ctl_led_card_valid[card_number]) {
-               mutex_unlock(&snd_ctl_led_mutex);
-               snd_card_unref(card);
-               return -ENXIO;
-       }
-       led = &snd_ctl_leds[group];
+       scoped_guard(mutex, &snd_ctl_led_mutex) {
+               if (!snd_ctl_led_card_valid[card_number])
+                       return -ENXIO;
+               led = &snd_ctl_leds[group];
 repeat:
-       list_for_each_entry(lctl, &led->controls, list)
-               if (lctl->card == card) {
-                       vd = &lctl->kctl->vd[lctl->index_offset];
-                       vd->access &= ~group_to_access(group);
-                       snd_ctl_led_ctl_destroy(lctl);
-                       change = true;
-                       goto repeat;
-               }
-       mutex_unlock(&snd_ctl_led_mutex);
+               list_for_each_entry(lctl, &led->controls, list)
+                       if (lctl->card == card) {
+                               vd = &lctl->kctl->vd[lctl->index_offset];
+                               vd->access &= ~group_to_access(group);
+                               snd_ctl_led_ctl_destroy(lctl);
+                               change = true;
+                               goto repeat;
+                       }
+       }
        if (change)
                snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
-       snd_card_unref(card);
        return 0;
 }
 
@@ -353,9 +338,8 @@ static void snd_ctl_led_register(struct snd_card *card)
        if (snd_BUG_ON(card->number < 0 ||
                       card->number >= ARRAY_SIZE(snd_ctl_led_card_valid)))
                return;
-       mutex_lock(&snd_ctl_led_mutex);
-       snd_ctl_led_card_valid[card->number] = true;
-       mutex_unlock(&snd_ctl_led_mutex);
+       scoped_guard(mutex, &snd_ctl_led_mutex)
+               snd_ctl_led_card_valid[card->number] = true;
        /* the register callback is already called with held card->controls_rwsem */
        list_for_each_entry(kctl, &card->controls, list)
                for (ioff = 0; ioff < kctl->count; ioff++)
@@ -367,10 +351,10 @@ static void snd_ctl_led_register(struct snd_card *card)
 static void snd_ctl_led_disconnect(struct snd_card *card)
 {
        snd_ctl_led_sysfs_remove(card);
-       mutex_lock(&snd_ctl_led_mutex);
-       snd_ctl_led_card_valid[card->number] = false;
-       snd_ctl_led_clean(card);
-       mutex_unlock(&snd_ctl_led_mutex);
+       scoped_guard(mutex, &snd_ctl_led_mutex) {
+               snd_ctl_led_card_valid[card->number] = false;
+               snd_ctl_led_clean(card);
+       }
        snd_ctl_led_refresh();
 }
 
@@ -430,9 +414,8 @@ static ssize_t mode_store(struct device *dev,
        else
                return count;
 
-       mutex_lock(&snd_ctl_led_mutex);
-       led->mode = mode;
-       mutex_unlock(&snd_ctl_led_mutex);
+       scoped_guard(mutex, &snd_ctl_led_mutex)
+               led->mode = mode;
 
        snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
        return count;
@@ -615,15 +598,15 @@ 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;
+       struct snd_card *card __free(snd_card_unref) = NULL;
        struct snd_ctl_led_ctl *lctl;
        size_t l = 0;
 
        card = snd_card_ref(led_card->number);
        if (!card)
                return -ENXIO;
-       down_read(&card->controls_rwsem);
-       mutex_lock(&snd_ctl_led_mutex);
+       guard(rwsem_read)(&card->controls_rwsem);
+       guard(mutex)(&snd_ctl_led_mutex);
        if (snd_ctl_led_card_valid[led_card->number]) {
                list_for_each_entry(lctl, &led_card->led->controls, list) {
                        if (lctl->card != card)
@@ -634,9 +617,6 @@ static ssize_t list_show(struct device *dev,
                                           lctl->kctl->id.numid + lctl->index_offset);
                }
        }
-       mutex_unlock(&snd_ctl_led_mutex);
-       up_read(&card->controls_rwsem);
-       snd_card_unref(card);
        return l;
 }
 
index e97ff8cccb643359cae97162040f516a7b0c43b1..147c1fea4708efe3bb3d1f7faed8923633f49e9c 100644 (file)
@@ -35,12 +35,12 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
        unsigned long ticks;
        enum hrtimer_restart ret = HRTIMER_NORESTART;
 
-       spin_lock(&t->lock);
-       if (!t->running)
-               goto out; /* fast path */
-       stime->in_callback = true;
-       ticks = t->sticks;
-       spin_unlock(&t->lock);
+       scoped_guard(spinlock, &t->lock) {
+               if (!t->running)
+                       return HRTIMER_NORESTART; /* fast path */
+               stime->in_callback = true;
+               ticks = t->sticks;
+       }
 
        /* calculate the drift */
        delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
@@ -49,15 +49,13 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
 
        snd_timer_interrupt(stime->timer, ticks);
 
-       spin_lock(&t->lock);
+       guard(spinlock)(&t->lock);
        if (t->running) {
                hrtimer_add_expires_ns(hrt, t->sticks * resolution);
                ret = HRTIMER_RESTART;
        }
 
        stime->in_callback = false;
- out:
-       spin_unlock(&t->lock);
        return ret;
 }
 
@@ -80,10 +78,10 @@ static int snd_hrtimer_close(struct snd_timer *t)
        struct snd_hrtimer *stime = t->private_data;
 
        if (stime) {
-               spin_lock_irq(&t->lock);
-               t->running = 0; /* just to be sure */
-               stime->in_callback = 1; /* skip start/stop */
-               spin_unlock_irq(&t->lock);
+               scoped_guard(spinlock_irq, &t->lock) {
+                       t->running = 0; /* just to be sure */
+                       stime->in_callback = 1; /* skip start/stop */
+               }
 
                hrtimer_cancel(&stime->hrt);
                kfree(stime);
index de7476034f2cf5f495c634a2a1649a543aab2811..09200df2932c793104acf91fedf005cf51fac30b 100644 (file)
@@ -149,12 +149,12 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
        struct snd_hwdep *hw = file->private_data;
        struct module *mod = hw->card->module;
 
-       mutex_lock(&hw->open_mutex);
-       if (hw->ops.release)
-               err = hw->ops.release(hw, file);
-       if (hw->used > 0)
-               hw->used--;
-       mutex_unlock(&hw->open_mutex);
+       scoped_guard(mutex, &hw->open_mutex) {
+               if (hw->ops.release)
+                       err = hw->ops.release(hw, file);
+               if (hw->used > 0)
+                       hw->used--;
+       }
        wake_up(&hw->open_wait);
 
        snd_card_file_remove(hw->card, file);
@@ -272,23 +272,23 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
 
                        if (get_user(device, (int __user *)arg))
                                return -EFAULT;
-                       mutex_lock(&register_mutex);
-
-                       if (device < 0)
-                               device = 0;
-                       else if (device < SNDRV_MINOR_HWDEPS)
-                               device++;
-                       else
-                               device = SNDRV_MINOR_HWDEPS;
-
-                       while (device < SNDRV_MINOR_HWDEPS) {
-                               if (snd_hwdep_search(card, device))
-                                       break;
-                               device++;
+
+                       scoped_guard(mutex, &register_mutex) {
+                               if (device < 0)
+                                       device = 0;
+                               else if (device < SNDRV_MINOR_HWDEPS)
+                                       device++;
+                               else
+                                       device = SNDRV_MINOR_HWDEPS;
+
+                               while (device < SNDRV_MINOR_HWDEPS) {
+                                       if (snd_hwdep_search(card, device))
+                                               break;
+                                       device++;
+                               }
+                               if (device >= SNDRV_MINOR_HWDEPS)
+                                       device = -1;
                        }
-                       if (device >= SNDRV_MINOR_HWDEPS)
-                               device = -1;
-                       mutex_unlock(&register_mutex);
                        if (put_user(device, (int __user *)arg))
                                return -EFAULT;
                        return 0;
@@ -296,19 +296,18 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
        case SNDRV_CTL_IOCTL_HWDEP_INFO:
                {
                        struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
-                       int device, err;
+                       int device;
                        struct snd_hwdep *hwdep;
 
                        if (get_user(device, &info->device))
                                return -EFAULT;
-                       mutex_lock(&register_mutex);
-                       hwdep = snd_hwdep_search(card, device);
-                       if (hwdep)
-                               err = snd_hwdep_info(hwdep, info);
-                       else
-                               err = -ENXIO;
-                       mutex_unlock(&register_mutex);
-                       return err;
+                       scoped_guard(mutex, &register_mutex) {
+                               hwdep = snd_hwdep_search(card, device);
+                               if (!hwdep)
+                                       return -ENXIO;
+                               return snd_hwdep_info(hwdep, info);
+                       }
+                       break;
                }
        }
        return -ENOIOCTLCMD;
@@ -422,11 +421,9 @@ static int snd_hwdep_dev_register(struct snd_device *device)
        struct snd_card *card = hwdep->card;
        int err;
 
-       mutex_lock(&register_mutex);
-       if (snd_hwdep_search(card, hwdep->device)) {
-               mutex_unlock(&register_mutex);
+       guard(mutex)(&register_mutex);
+       if (snd_hwdep_search(card, hwdep->device))
                return -EBUSY;
-       }
        list_add_tail(&hwdep->list, &snd_hwdep_devices);
        err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
                                  hwdep->card, hwdep->device,
@@ -434,7 +431,6 @@ static int snd_hwdep_dev_register(struct snd_device *device)
        if (err < 0) {
                dev_err(hwdep->dev, "unable to register\n");
                list_del(&hwdep->list);
-               mutex_unlock(&register_mutex);
                return err;
        }
 
@@ -454,7 +450,6 @@ static int snd_hwdep_dev_register(struct snd_device *device)
                        hwdep->ossreg = 1;
        }
 #endif
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -464,12 +459,10 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
 
        if (snd_BUG_ON(!hwdep))
                return -ENXIO;
-       mutex_lock(&register_mutex);
-       if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
-               mutex_unlock(&register_mutex);
+       guard(mutex)(&register_mutex);
+       if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep)
                return -EINVAL;
-       }
-       mutex_lock(&hwdep->open_mutex);
+       guard(mutex)(&hwdep->open_mutex);
        wake_up(&hwdep->open_wait);
 #ifdef CONFIG_SND_OSSEMUL
        if (hwdep->ossreg)
@@ -477,8 +470,6 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
 #endif
        snd_unregister_device(hwdep->dev);
        list_del_init(&hwdep->list);
-       mutex_unlock(&hwdep->open_mutex);
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -492,11 +483,10 @@ static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 {
        struct snd_hwdep *hwdep;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
                snd_iprintf(buffer, "%02i-%02i: %s\n",
                            hwdep->card->number, hwdep->device, hwdep->name);
-       mutex_unlock(&register_mutex);
 }
 
 static struct snd_info_entry *snd_hwdep_proc_entry;
index e2f302e55bbb20de0ee95593a34d26c4745d0ca3..1f5b8a3d9e3b62a5e3796e9394c1de4e98441a1f 100644 (file)
@@ -105,17 +105,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
 {
        struct snd_info_private_data *data;
        struct snd_info_entry *entry;
-       loff_t ret = -EINVAL, size;
+       loff_t size;
 
        data = file->private_data;
        entry = data->entry;
-       mutex_lock(&entry->access);
-       if (entry->c.ops->llseek) {
-               ret = entry->c.ops->llseek(entry,
-                                          data->file_private_data,
-                                          file, offset, orig);
-               goto out;
-       }
+       guard(mutex)(&entry->access);
+       if (entry->c.ops->llseek)
+               return entry->c.ops->llseek(entry,
+                                           data->file_private_data,
+                                           file, offset, orig);
 
        size = entry->size;
        switch (orig) {
@@ -126,21 +124,18 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
                break;
        case SEEK_END:
                if (!size)
-                       goto out;
+                       return -EINVAL;
                offset += size;
                break;
        default:
-               goto out;
+               return -EINVAL;
        }
        if (offset < 0)
-               goto out;
+               return -EINVAL;
        if (size && offset > size)
                offset = size;
        file->f_pos = offset;
-       ret = offset;
- out:
-       mutex_unlock(&entry->access);
-       return ret;
+       return offset;
 }
 
 static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
@@ -238,10 +233,10 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
        struct snd_info_private_data *data;
        int mode, err;
 
-       mutex_lock(&info_mutex);
+       guard(mutex)(&info_mutex);
        err = alloc_info_private(entry, &data);
        if (err < 0)
-               goto unlock;
+               return err;
 
        mode = file->f_flags & O_ACCMODE;
        if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
@@ -257,14 +252,11 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
        }
 
        file->private_data = data;
-       mutex_unlock(&info_mutex);
        return 0;
 
  error:
        kfree(data);
        module_put(entry->module);
- unlock:
-       mutex_unlock(&info_mutex);
        return err;
 }
 
@@ -306,7 +298,6 @@ static ssize_t snd_info_text_entry_write(struct file *file,
        struct snd_info_buffer *buf;
        loff_t pos;
        size_t next;
-       int err = 0;
 
        if (!entry->c.text.write)
                return -EIO;
@@ -317,34 +308,24 @@ static ssize_t snd_info_text_entry_write(struct file *file,
        /* don't handle too large text inputs */
        if (next > 16 * 1024)
                return -EIO;
-       mutex_lock(&entry->access);
+       guard(mutex)(&entry->access);
        buf = data->wbuffer;
        if (!buf) {
                data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
-               if (!buf) {
-                       err = -ENOMEM;
-                       goto error;
-               }
+               if (!buf)
+                       return -ENOMEM;
        }
        if (next > buf->len) {
                char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
-               if (!nbuf) {
-                       err = -ENOMEM;
-                       goto error;
-               }
+               if (!nbuf)
+                       return -ENOMEM;
                kvfree(buf->buffer);
                buf->buffer = nbuf;
                buf->len = PAGE_ALIGN(next);
        }
-       if (copy_from_user(buf->buffer + pos, buffer, count)) {
-               err = -EFAULT;
-               goto error;
-       }
+       if (copy_from_user(buf->buffer + pos, buffer, count))
+               return -EFAULT;
        buf->size = next;
- error:
-       mutex_unlock(&entry->access);
-       if (err < 0)
-               return err;
        *offset = next;
        return count;
 }
@@ -369,10 +350,10 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file)
        struct snd_info_private_data *data;
        int err;
 
-       mutex_lock(&info_mutex);
+       guard(mutex)(&info_mutex);
        err = alloc_info_private(entry, &data);
        if (err < 0)
-               goto unlock;
+               return err;
 
        data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
        if (!data->rbuffer) {
@@ -386,15 +367,12 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file)
                err = single_open(file, snd_info_seq_show, data);
        if (err < 0)
                goto error;
-       mutex_unlock(&info_mutex);
        return 0;
 
  error:
        kfree(data->rbuffer);
        kfree(data);
        module_put(entry->module);
- unlock:
-       mutex_unlock(&info_mutex);
        return err;
 }
 
@@ -549,7 +527,7 @@ int snd_info_card_register(struct snd_card *card)
  */
 void snd_info_card_id_change(struct snd_card *card)
 {
-       mutex_lock(&info_mutex);
+       guard(mutex)(&info_mutex);
        if (card->proc_root_link) {
                proc_remove(card->proc_root_link);
                card->proc_root_link = NULL;
@@ -558,7 +536,6 @@ void snd_info_card_id_change(struct snd_card *card)
                card->proc_root_link = proc_symlink(card->id,
                                                    snd_proc_root->p,
                                                    card->proc_root->name);
-       mutex_unlock(&info_mutex);
 }
 
 /*
@@ -574,12 +551,11 @@ void snd_info_card_disconnect(struct snd_card *card)
        if (card->proc_root)
                proc_remove(card->proc_root->p);
 
-       mutex_lock(&info_mutex);
+       guard(mutex)(&info_mutex);
        if (card->proc_root)
                snd_info_clear_entries(card->proc_root);
        card->proc_root_link = NULL;
        card->proc_root = NULL;
-       mutex_unlock(&info_mutex);
 }
 
 /*
@@ -703,9 +679,8 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent,
        entry->parent = parent;
        entry->module = module;
        if (parent) {
-               mutex_lock(&parent->access);
+               guard(mutex)(&parent->access);
                list_add_tail(&entry->list, &parent->children);
-               mutex_unlock(&parent->access);
        }
        return entry;
 }
@@ -775,9 +750,8 @@ void snd_info_free_entry(struct snd_info_entry * entry)
                return;
        if (entry->p) {
                proc_remove(entry->p);
-               mutex_lock(&info_mutex);
+               guard(mutex)(&info_mutex);
                snd_info_clear_entries(entry);
-               mutex_unlock(&info_mutex);
        }
 
        /* free all children at first */
@@ -786,9 +760,8 @@ void snd_info_free_entry(struct snd_info_entry * entry)
 
        p = entry->parent;
        if (p) {
-               mutex_lock(&p->access);
+               guard(mutex)(&p->access);
                list_del(&entry->list);
-               mutex_unlock(&p->access);
        }
        kfree(entry->name);
        if (entry->private_free)
@@ -804,15 +777,13 @@ static int __snd_info_register(struct snd_info_entry *entry)
        if (snd_BUG_ON(!entry))
                return -ENXIO;
        root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
-       mutex_lock(&info_mutex);
+       guard(mutex)(&info_mutex);
        if (entry->p || !root)
-               goto unlock;
+               return 0;
        if (S_ISDIR(entry->mode)) {
                p = proc_mkdir_mode(entry->name, entry->mode, root);
-               if (!p) {
-                       mutex_unlock(&info_mutex);
+               if (!p)
                        return -ENOMEM;
-               }
        } else {
                const struct proc_ops *ops;
                if (entry->content == SNDRV_INFO_CONTENT_DATA)
@@ -821,15 +792,11 @@ static int __snd_info_register(struct snd_info_entry *entry)
                        ops = &snd_info_text_entry_ops;
                p = proc_create_data(entry->name, entry->mode, root,
                                     ops, entry);
-               if (!p) {
-                       mutex_unlock(&info_mutex);
+               if (!p)
                        return -ENOMEM;
-               }
                proc_set_size(p, entry->size);
        }
        entry->p = p;
- unlock:
-       mutex_unlock(&info_mutex);
        return 0;
 }
 
index ebc714b2f46b3faca2db1f0f8b9fee5fd8ba0520..0dbbb80055700ed3509f5f7f991e226a3b256b68 100644 (file)
@@ -29,20 +29,17 @@ int snd_oss_info_register(int dev, int num, char *string)
                return -ENXIO;
        if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
                return -ENXIO;
-       mutex_lock(&strings);
+       guard(mutex)(&strings);
        if (string == NULL) {
                x = snd_sndstat_strings[num][dev];
                kfree(x);
                x = NULL;
        } else {
                x = kstrdup(string, GFP_KERNEL);
-               if (x == NULL) {
-                       mutex_unlock(&strings);
+               if (x == NULL)
                        return -ENOMEM;
-               }
        }
        snd_sndstat_strings[num][dev] = x;
-       mutex_unlock(&strings);
        return 0;
 }
 EXPORT_SYMBOL(snd_oss_info_register);
@@ -53,7 +50,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
        char *str;
 
        snd_iprintf(buf, "\n%s:", id);
-       mutex_lock(&strings);
+       guard(mutex)(&strings);
        for (idx = 0; idx < SNDRV_CARDS; idx++) {
                str = snd_sndstat_strings[idx][dev];
                if (str) {
@@ -64,7 +61,6 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
                        snd_iprintf(buf, "%i: %s\n", idx, str);
                }
        }
-       mutex_unlock(&strings);
        if (ok < 0)
                snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
        return ok;
index 22c0d217b8608f04b4c9c1803379b9546f12ed14..4ed5037d8693b3ab3472818b82ce2286b99b942c 100644 (file)
@@ -284,30 +284,31 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
        if (xid)
                strscpy(card->id, xid, sizeof(card->id));
        err = 0;
-       mutex_lock(&snd_card_mutex);
-       if (idx < 0) /* first check the matching module-name slot */
-               idx = get_slot_from_bitmask(idx, module_slot_match, module);
-       if (idx < 0) /* if not matched, assign an empty slot */
-               idx = get_slot_from_bitmask(idx, check_empty_slot, module);
-       if (idx < 0)
-               err = -ENODEV;
-       else if (idx < snd_ecards_limit) {
-               if (test_bit(idx, snd_cards_lock))
-                       err = -EBUSY;   /* invalid */
-       } else if (idx >= SNDRV_CARDS)
-               err = -ENODEV;
+       scoped_guard(mutex, &snd_card_mutex) {
+               if (idx < 0) /* first check the matching module-name slot */
+                       idx = get_slot_from_bitmask(idx, module_slot_match, module);
+               if (idx < 0) /* if not matched, assign an empty slot */
+                       idx = get_slot_from_bitmask(idx, check_empty_slot, module);
+               if (idx < 0)
+                       err = -ENODEV;
+               else if (idx < snd_ecards_limit) {
+                       if (test_bit(idx, snd_cards_lock))
+                               err = -EBUSY;   /* invalid */
+               } else if (idx >= SNDRV_CARDS)
+                       err = -ENODEV;
+               if (!err) {
+                       set_bit(idx, snd_cards_lock);           /* lock it */
+                       if (idx >= snd_ecards_limit)
+                               snd_ecards_limit = idx + 1; /* increase the limit */
+               }
+       }
        if (err < 0) {
-               mutex_unlock(&snd_card_mutex);
                dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
-                        idx, snd_ecards_limit - 1, err);
+                       idx, snd_ecards_limit - 1, err);
                if (!card->managed)
                        kfree(card); /* manually free here, as no destructor called */
                return err;
        }
-       set_bit(idx, snd_cards_lock);           /* lock it */
-       if (idx >= snd_ecards_limit)
-               snd_ecards_limit = idx + 1; /* increase the limit */
-       mutex_unlock(&snd_card_mutex);
        card->dev = parent;
        card->number = idx;
 #ifdef MODULE
@@ -386,11 +387,10 @@ struct snd_card *snd_card_ref(int idx)
 {
        struct snd_card *card;
 
-       mutex_lock(&snd_card_mutex);
+       guard(mutex)(&snd_card_mutex);
        card = snd_cards[idx];
        if (card)
                get_device(&card->card_dev);
-       mutex_unlock(&snd_card_mutex);
        return card;
 }
 EXPORT_SYMBOL_GPL(snd_card_ref);
@@ -398,12 +398,8 @@ EXPORT_SYMBOL_GPL(snd_card_ref);
 /* return non-zero if a card is already locked */
 int snd_card_locked(int card)
 {
-       int locked;
-
-       mutex_lock(&snd_card_mutex);
-       locked = test_bit(card, snd_cards_lock);
-       mutex_unlock(&snd_card_mutex);
-       return locked;
+       guard(mutex)(&snd_card_mutex);
+       return test_bit(card, snd_cards_lock);
 }
 
 static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
@@ -427,15 +423,15 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
 {
        struct snd_monitor_file *df = NULL, *_df;
 
-       spin_lock(&shutdown_lock);
-       list_for_each_entry(_df, &shutdown_files, shutdown_list) {
-               if (_df->file == file) {
-                       df = _df;
-                       list_del_init(&df->shutdown_list);
-                       break;
+       scoped_guard(spinlock, &shutdown_lock) {
+               list_for_each_entry(_df, &shutdown_files, shutdown_list) {
+                       if (_df->file == file) {
+                               df = _df;
+                               list_del_init(&df->shutdown_list);
+                               break;
+                       }
                }
        }
-       spin_unlock(&shutdown_lock);
 
        if (likely(df)) {
                if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
@@ -501,27 +497,24 @@ void snd_card_disconnect(struct snd_card *card)
        if (!card)
                return;
 
-       spin_lock(&card->files_lock);
-       if (card->shutdown) {
-               spin_unlock(&card->files_lock);
-               return;
-       }
-       card->shutdown = 1;
+       scoped_guard(spinlock, &card->files_lock) {
+               if (card->shutdown)
+                       return;
+               card->shutdown = 1;
 
-       /* replace file->f_op with special dummy operations */
-       list_for_each_entry(mfile, &card->files_list, list) {
-               /* it's critical part, use endless loop */
-               /* we have no room to fail */
-               mfile->disconnected_f_op = mfile->file->f_op;
+               /* replace file->f_op with special dummy operations */
+               list_for_each_entry(mfile, &card->files_list, list) {
+                       /* it's critical part, use endless loop */
+                       /* we have no room to fail */
+                       mfile->disconnected_f_op = mfile->file->f_op;
 
-               spin_lock(&shutdown_lock);
-               list_add(&mfile->shutdown_list, &shutdown_files);
-               spin_unlock(&shutdown_lock);
+                       scoped_guard(spinlock, &shutdown_lock)
+                               list_add(&mfile->shutdown_list, &shutdown_files);
 
-               mfile->file->f_op = &snd_shutdown_f_ops;
-               fops_get(mfile->file->f_op);
+                       mfile->file->f_op = &snd_shutdown_f_ops;
+                       fops_get(mfile->file->f_op);
+               }
        }
-       spin_unlock(&card->files_lock); 
 
        /* notify all connected devices about disconnection */
        /* at this point, they cannot respond to any calls except release() */
@@ -544,10 +537,10 @@ void snd_card_disconnect(struct snd_card *card)
        }
 
        /* disable fops (user space) operations for ALSA API */
-       mutex_lock(&snd_card_mutex);
-       snd_cards[card->number] = NULL;
-       clear_bit(card->number, snd_cards_lock);
-       mutex_unlock(&snd_card_mutex);
+       scoped_guard(mutex, &snd_card_mutex) {
+               snd_cards[card->number] = NULL;
+               clear_bit(card->number, snd_cards_lock);
+       }
 
 #ifdef CONFIG_PM
        wake_up(&card->power_sleep);
@@ -569,11 +562,10 @@ void snd_card_disconnect_sync(struct snd_card *card)
 {
        snd_card_disconnect(card);
 
-       spin_lock_irq(&card->files_lock);
+       guard(spinlock_irq)(&card->files_lock);
        wait_event_lock_irq(card->remove_sleep,
                            list_empty(&card->files_list),
                            card->files_lock);
-       spin_unlock_irq(&card->files_lock);
 }
 EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
 
@@ -767,9 +759,8 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
        /* check if user specified own card->id */
        if (card->id[0] != '\0')
                return;
-       mutex_lock(&snd_card_mutex);
+       guard(mutex)(&snd_card_mutex);
        snd_card_set_id_no_lock(card, nid, nid);
-       mutex_unlock(&snd_card_mutex);
 }
 EXPORT_SYMBOL(snd_card_set_id);
 
@@ -797,14 +788,11 @@ static ssize_t id_store(struct device *dev, struct device_attribute *attr,
        }
        memcpy(buf1, buf, copy);
        buf1[copy] = '\0';
-       mutex_lock(&snd_card_mutex);
-       if (!card_id_ok(NULL, buf1)) {
-               mutex_unlock(&snd_card_mutex);
+       guard(mutex)(&snd_card_mutex);
+       if (!card_id_ok(NULL, buf1))
                return -EEXIST;
-       }
        strcpy(card->id, buf1);
        snd_info_card_id_change(card);
-       mutex_unlock(&snd_card_mutex);
 
        return count;
 }
@@ -897,26 +885,27 @@ int snd_card_register(struct snd_card *card)
        err = snd_device_register_all(card);
        if (err < 0)
                return err;
-       mutex_lock(&snd_card_mutex);
-       if (snd_cards[card->number]) {
-               /* already registered */
-               mutex_unlock(&snd_card_mutex);
-               return snd_info_card_register(card); /* register pending info */
-       }
-       if (*card->id) {
-               /* make a unique id name from the given string */
-               char tmpid[sizeof(card->id)];
-               memcpy(tmpid, card->id, sizeof(card->id));
-               snd_card_set_id_no_lock(card, tmpid, tmpid);
-       } else {
-               /* create an id from either shortname or longname */
-               const char *src;
-               src = *card->shortname ? card->shortname : card->longname;
-               snd_card_set_id_no_lock(card, src,
-                                       retrieve_id_from_card_name(src));
+       scoped_guard(mutex, &snd_card_mutex) {
+               if (snd_cards[card->number]) {
+                       /* already registered */
+                       return snd_info_card_register(card); /* register pending info */
+               }
+               if (*card->id) {
+                       /* make a unique id name from the given string */
+                       char tmpid[sizeof(card->id)];
+
+                       memcpy(tmpid, card->id, sizeof(card->id));
+                       snd_card_set_id_no_lock(card, tmpid, tmpid);
+               } else {
+                       /* create an id from either shortname or longname */
+                       const char *src;
+
+                       src = *card->shortname ? card->shortname : card->longname;
+                       snd_card_set_id_no_lock(card, src,
+                                               retrieve_id_from_card_name(src));
+               }
+               snd_cards[card->number] = card;
        }
-       snd_cards[card->number] = card;
-       mutex_unlock(&snd_card_mutex);
        err = snd_info_card_register(card);
        if (err < 0)
                return err;
@@ -937,7 +926,7 @@ static void snd_card_info_read(struct snd_info_entry *entry,
        struct snd_card *card;
 
        for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
-               mutex_lock(&snd_card_mutex);
+               guard(mutex)(&snd_card_mutex);
                card = snd_cards[idx];
                if (card) {
                        count++;
@@ -949,7 +938,6 @@ static void snd_card_info_read(struct snd_info_entry *entry,
                        snd_iprintf(buffer, "                      %s\n",
                                        card->longname);
                }
-               mutex_unlock(&snd_card_mutex);
        }
        if (!count)
                snd_iprintf(buffer, "--- no soundcards ---\n");
@@ -962,13 +950,12 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
        struct snd_card *card;
 
        for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
-               mutex_lock(&snd_card_mutex);
+               guard(mutex)(&snd_card_mutex);
                card = snd_cards[idx];
                if (card) {
                        count++;
                        snd_iprintf(buffer, "%s\n", card->longname);
                }
-               mutex_unlock(&snd_card_mutex);
        }
        if (!count) {
                snd_iprintf(buffer, "--- no soundcards ---\n");
@@ -985,12 +972,11 @@ static void snd_card_module_info_read(struct snd_info_entry *entry,
        struct snd_card *card;
 
        for (idx = 0; idx < SNDRV_CARDS; idx++) {
-               mutex_lock(&snd_card_mutex);
+               guard(mutex)(&snd_card_mutex);
                card = snd_cards[idx];
                if (card)
                        snd_iprintf(buffer, "%2i %s\n",
                                    idx, card->module->name);
-               mutex_unlock(&snd_card_mutex);
        }
 }
 #endif
@@ -1072,15 +1058,13 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
        mfile->file = file;
        mfile->disconnected_f_op = NULL;
        INIT_LIST_HEAD(&mfile->shutdown_list);
-       spin_lock(&card->files_lock);
+       guard(spinlock)(&card->files_lock);
        if (card->shutdown) {
-               spin_unlock(&card->files_lock);
                kfree(mfile);
                return -ENODEV;
        }
        list_add(&mfile->list, &card->files_list);
        get_device(&card->card_dev);
-       spin_unlock(&card->files_lock);
        return 0;
 }
 EXPORT_SYMBOL(snd_card_file_add);
@@ -1102,22 +1086,21 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
 {
        struct snd_monitor_file *mfile, *found = NULL;
 
-       spin_lock(&card->files_lock);
-       list_for_each_entry(mfile, &card->files_list, list) {
-               if (mfile->file == file) {
-                       list_del(&mfile->list);
-                       spin_lock(&shutdown_lock);
-                       list_del(&mfile->shutdown_list);
-                       spin_unlock(&shutdown_lock);
-                       if (mfile->disconnected_f_op)
-                               fops_put(mfile->disconnected_f_op);
-                       found = mfile;
-                       break;
+       scoped_guard(spinlock, &card->files_lock) {
+               list_for_each_entry(mfile, &card->files_list, list) {
+                       if (mfile->file == file) {
+                               list_del(&mfile->list);
+                               scoped_guard(spinlock, &shutdown_lock)
+                                       list_del(&mfile->shutdown_list);
+                               if (mfile->disconnected_f_op)
+                                       fops_put(mfile->disconnected_f_op);
+                               found = mfile;
+                               break;
+                       }
                }
+               if (list_empty(&card->files_list))
+                       wake_up_all(&card->remove_sleep);
        }
-       if (list_empty(&card->files_list))
-               wake_up_all(&card->remove_sleep);
-       spin_unlock(&card->files_lock);
        if (!found) {
                dev_err(card->dev, "card file remove problem (%p)\n", file);
                return -ENOENT;
index e0f034e7275cd569fa9dee70f7bf4d2dbe882714..e08b2c4fbd1a577380d31781d28b222b404c5c2c 100644 (file)
@@ -42,11 +42,9 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
 #ifdef CONFIG_SND_JACK_INPUT_DEV
        struct snd_jack *jack = device->device_data;
 
-       mutex_lock(&jack->input_dev_lock);
-       if (!jack->input_dev) {
-               mutex_unlock(&jack->input_dev_lock);
+       guard(mutex)(&jack->input_dev_lock);
+       if (!jack->input_dev)
                return 0;
-       }
 
        /* If the input device is registered with the input subsystem
         * then we need to use a different deallocator. */
@@ -55,7 +53,6 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
        else
                input_free_device(jack->input_dev);
        jack->input_dev = NULL;
-       mutex_unlock(&jack->input_dev_lock);
 #endif /* CONFIG_SND_JACK_INPUT_DEV */
        return 0;
 }
@@ -92,11 +89,9 @@ static int snd_jack_dev_register(struct snd_device *device)
        snprintf(jack->name, sizeof(jack->name), "%s %s",
                 card->shortname, jack->id);
 
-       mutex_lock(&jack->input_dev_lock);
-       if (!jack->input_dev) {
-               mutex_unlock(&jack->input_dev_lock);
+       guard(mutex)(&jack->input_dev_lock);
+       if (!jack->input_dev)
                return 0;
-       }
 
        jack->input_dev->name = jack->name;
 
@@ -121,7 +116,6 @@ static int snd_jack_dev_register(struct snd_device *device)
        if (err == 0)
                jack->registered = 1;
 
-       mutex_unlock(&jack->input_dev_lock);
        return err;
 }
 #endif /* CONFIG_SND_JACK_INPUT_DEV */
@@ -586,14 +580,9 @@ EXPORT_SYMBOL(snd_jack_new);
 void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
 {
        WARN_ON(jack->registered);
-       mutex_lock(&jack->input_dev_lock);
-       if (!jack->input_dev) {
-               mutex_unlock(&jack->input_dev_lock);
-               return;
-       }
-
-       jack->input_dev->dev.parent = parent;
-       mutex_unlock(&jack->input_dev_lock);
+       guard(mutex)(&jack->input_dev_lock);
+       if (jack->input_dev)
+               jack->input_dev->dev.parent = parent;
 }
 EXPORT_SYMBOL(snd_jack_set_parent);
 
index dae2da3808351bf2178c3b0cda484479014ff6ea..6a0508093ea6889f888a810641d7ae21b71c2ee0 100644 (file)
@@ -130,13 +130,12 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        for (chn = 0; chn < 31; chn++) {
                pslot = &mixer->slots[chn];
                if (pslot->put_volume || pslot->put_recsrc)
                        result |= 1 << chn;
        }
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -148,13 +147,12 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        for (chn = 0; chn < 31; chn++) {
                pslot = &mixer->slots[chn];
                if (pslot->put_volume && pslot->stereo)
                        result |= 1 << chn;
        }
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -165,7 +163,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
                result = mixer->mask_recsrc;
        } else {
@@ -177,7 +175,6 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
                                result |= 1 << chn;
                }
        }
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -188,12 +185,12 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
                unsigned int index;
                result = mixer->get_recsrc(fmixer, &index);
                if (result < 0)
-                       goto unlock;
+                       return result;
                result = 1 << index;
        } else {
                struct snd_mixer_oss_slot *pslot;
@@ -209,8 +206,6 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
                }
        }
        mixer->oss_recsrc = result;
- unlock:
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -224,7 +219,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
 
        if (mixer == NULL)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        if (mixer->get_recsrc && mixer->put_recsrc) {   /* exclusive input */
                if (recsrc & ~mixer->oss_recsrc)
                        recsrc &= ~mixer->oss_recsrc;
@@ -250,7 +245,6 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
                        }
                }
        }
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -262,7 +256,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
 
        if (mixer == NULL || slot > 30)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        pslot = &mixer->slots[slot];
        left = pslot->volume[0];
        right = pslot->volume[1];
@@ -270,21 +264,15 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
                result = pslot->get_volume(fmixer, pslot, &left, &right);
        if (!pslot->stereo)
                right = left;
-       if (snd_BUG_ON(left < 0 || left > 100)) {
-               result = -EIO;
-               goto unlock;
-       }
-       if (snd_BUG_ON(right < 0 || right > 100)) {
-               result = -EIO;
-               goto unlock;
-       }
+       if (snd_BUG_ON(left < 0 || left > 100))
+               return -EIO;
+       if (snd_BUG_ON(right < 0 || right > 100))
+               return -EIO;
        if (result >= 0) {
                pslot->volume[0] = left;
                pslot->volume[1] = right;
                result = (left & 0xff) | ((right & 0xff) << 8);
        }
- unlock:
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -297,7 +285,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
 
        if (mixer == NULL || slot > 30)
                return -EIO;
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        pslot = &mixer->slots[slot];
        if (left > 100)
                left = 100;
@@ -308,12 +296,10 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
        if (pslot->put_volume)
                result = pslot->put_volume(fmixer, pslot, left, right);
        if (result < 0)
-               goto unlock;
+               return result;
        pslot->volume[0] = left;
        pslot->volume[1] = right;
        result = (left & 0xff) | ((right & 0xff) << 8);
- unlock:
-       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -532,37 +518,31 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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;
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_numid_locked(card, numid);
-       if (!kctl) {
-               up_read(&card->controls_rwsem);
+       if (!kctl)
                return;
-       }
        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
        if (uinfo == NULL || uctl == NULL)
-               goto __unalloc;
+               return;
        if (kctl->info(kctl, uinfo))
-               goto __unalloc;
+               return;
        if (kctl->get(kctl, uctl))
-               goto __unalloc;
+               return;
        if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
            uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
-               goto __unalloc;
+               return;
        *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
        if (uinfo->count > 1)
                *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
-      __unalloc:
-       up_read(&card->controls_rwsem);
-       kfree(uctl);
-       kfree(uinfo);
 }
 
 static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -571,27 +551,25 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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;
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_numid_locked(card, numid);
-       if (!kctl) {
-               up_read(&card->controls_rwsem);
+       if (!kctl)
                return;
-       }
        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
        if (uinfo == NULL || uctl == NULL)
-               goto __unalloc;
+               return;
        if (kctl->info(kctl, uinfo))
-               goto __unalloc;
+               return;
        if (kctl->get(kctl, uctl))
-               goto __unalloc;
+               return;
        if (!uctl->value.integer.value[0]) {
                *left = 0;
                if (uinfo->count == 1)
@@ -599,10 +577,6 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
        }
        if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
                *right = 0;
-      __unalloc:
-       up_read(&card->controls_rwsem);
-       kfree(uctl);
-       kfree(uinfo);
 }
 
 static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
@@ -636,41 +610,35 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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;
 
        if (numid == ID_UNKNOWN)
                return;
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_numid_locked(card, numid);
-       if (!kctl) {
-               up_read(&card->controls_rwsem);
+       if (!kctl)
                return;
-       }
        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
        if (uinfo == NULL || uctl == NULL)
-               goto __unalloc;
+               return;
        if (kctl->info(kctl, uinfo))
-               goto __unalloc;
+               return;
        if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
            uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
-               goto __unalloc;
+               return;
        uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
        if (uinfo->count > 1)
                uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
        res = kctl->put(kctl, uctl);
        if (res < 0)
-               goto __unalloc;
+               return;
        if (res > 0)
                snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
-      __unalloc:
-       up_read(&card->controls_rwsem);
-       kfree(uctl);
-       kfree(uinfo);
 }
 
 static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -679,26 +647,24 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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;
 
        if (numid == ID_UNKNOWN)
                return;
-       down_read(&card->controls_rwsem);
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_ctl_find_numid_locked(card, numid);
-       if (!kctl) {
-               up_read(&card->controls_rwsem);
+       if (!kctl)
                return;
-       }
        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
        if (uinfo == NULL || uctl == NULL)
-               goto __unalloc;
+               return;
        if (kctl->info(kctl, uinfo))
-               goto __unalloc;
+               return;
        if (uinfo->count > 1) {
                uctl->value.integer.value[0] = left > 0 ? 1 : 0;
                uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
@@ -711,13 +677,9 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
        }
        res = kctl->put(kctl, uctl);
        if (res < 0)
-               goto __unalloc;
+               return;
        if (res > 0)
                snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
-      __unalloc:
-       up_read(&card->controls_rwsem);
-       kfree(uctl);
-       kfree(uinfo);
 }
 
 static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
@@ -822,28 +784,24 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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);
-       if (uinfo == NULL || uctl == NULL) {
-               err = -ENOMEM;
-               goto __free_only;
-       }
-       down_read(&card->controls_rwsem);
+       if (uinfo == NULL || uctl == NULL)
+               return -ENOMEM;
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
-       if (! kctl) {
-               err = -ENOENT;
-               goto __unlock;
-       }
+       if (!kctl)
+               return -ENOENT;
        err = kctl->info(kctl, uinfo);
        if (err < 0)
-               goto __unlock;
+               return err;
        err = kctl->get(kctl, uctl);
        if (err < 0)
-               goto __unlock;
+               return err;
        for (idx = 0; idx < 32; idx++) {
                if (!(mixer->mask_recsrc & (1 << idx)))
                        continue;
@@ -858,13 +816,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
                        break;
                }
        }
-       err = 0;
-      __unlock:
-       up_read(&card->controls_rwsem);
-      __free_only:
-       kfree(uctl);
-       kfree(uinfo);
-       return err;
+       return 0;
 }
 
 static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)
@@ -874,26 +826,22 @@ 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;
-       struct snd_ctl_elem_value *uctl;
+       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);
-       if (uinfo == NULL || uctl == NULL) {
-               err = -ENOMEM;
-               goto __free_only;
-       }
-       down_read(&card->controls_rwsem);
+       if (uinfo == NULL || uctl == NULL)
+               return -ENOMEM;
+       guard(rwsem_read)(&card->controls_rwsem);
        kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
-       if (! kctl) {
-               err = -ENOENT;
-               goto __unlock;
-       }
+       if (!kctl)
+               return -ENOENT;
        err = kctl->info(kctl, uinfo);
        if (err < 0)
-               goto __unlock;
+               return err;
        for (idx = 0; idx < 32; idx++) {
                if (!(mixer->mask_recsrc & (1 << idx)))
                        continue;
@@ -907,20 +855,14 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
                        break;
                slot = NULL;
        }
-       if (! slot)
-               goto __unlock;
+       if (!slot)
+               return 0;
        for (idx = 0; idx < uinfo->count; idx++)
                uctl->value.enumerated.item[idx] = slot->capture_item;
        err = kctl->put(kctl, uctl);
        if (err > 0)
                snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
-       err = 0;
-      __unlock:
-       up_read(&card->controls_rwsem);
-      __free_only:
-       kfree(uctl);
-       kfree(uinfo);
-       return err;
+       return 0;
 }
 
 struct snd_mixer_oss_assign_table {
@@ -931,34 +873,26 @@ 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;
+       struct snd_ctl_elem_info *info __free(kfree) = NULL;
        struct snd_kcontrol *kcontrol;
        struct snd_card *card = mixer->card;
        int err;
 
-       down_read(&card->controls_rwsem);
-       kcontrol = snd_mixer_oss_test_id(mixer, name, index);
-       if (kcontrol == NULL) {
-               up_read(&card->controls_rwsem);
-               return 0;
-       }
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (! info) {
-               up_read(&card->controls_rwsem);
-               return -ENOMEM;
-       }
-       err = kcontrol->info(kcontrol, info);
-       if (err < 0) {
-               up_read(&card->controls_rwsem);
-               kfree(info);
-               return err;
+       scoped_guard(rwsem_read, &card->controls_rwsem) {
+               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;
+               slot->numid[item] = kcontrol->id.numid;
        }
-       slot->numid[item] = kcontrol->id.numid;
-       up_read(&card->controls_rwsem);
        if (info->count > slot->channels)
                slot->channels = info->count;
        slot->present |= 1 << item;
-       kfree(info);
        return 0;
 }
 
@@ -1068,24 +1002,19 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
        memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
        if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
                return 0;
-       down_read(&mixer->card->controls_rwsem);
+       guard(rwsem_read)(&mixer->card->controls_rwsem);
        kctl = NULL;
        if (!ptr->index)
                kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
        if (kctl) {
-               struct snd_ctl_elem_info *uinfo;
+               struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
 
                uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
-               if (! uinfo) {
-                       up_read(&mixer->card->controls_rwsem);
+               if (!uinfo)
                        return -ENOMEM;
-               }
                        
-               if (kctl->info(kctl, uinfo)) {
-                       up_read(&mixer->card->controls_rwsem);
-                       kfree(uinfo);
+               if (kctl->info(kctl, uinfo))
                        return 0;
-               }
                strcpy(str, ptr->name);
                if (!strcmp(str, "Master"))
                        strcpy(str, "Mix");
@@ -1097,20 +1026,15 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
                } else {
                        for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
                                uinfo->value.enumerated.item = slot.capture_item;
-                               if (kctl->info(kctl, uinfo)) {
-                                       up_read(&mixer->card->controls_rwsem);
-                                       kfree(uinfo);
+                               if (kctl->info(kctl, uinfo))
                                        return 0;
-                               }
                                if (!strcmp(uinfo->value.enumerated.name, str)) {
                                        slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
                                        break;
                                }
                        }
                }
-               kfree(uinfo);
        }
-       up_read(&mixer->card->controls_rwsem);
        if (slot.present != 0) {
                pslot = kmalloc(sizeof(slot), GFP_KERNEL);
                if (! pslot)
@@ -1183,7 +1107,7 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
        struct snd_mixer_oss *mixer = entry->private_data;
        int i;
 
-       mutex_lock(&mixer->reg_mutex);
+       guard(mutex)(&mixer->reg_mutex);
        for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
                struct slot *p;
 
@@ -1198,7 +1122,6 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
                else
                        snd_iprintf(buffer, "\"\" 0\n");
        }
-       mutex_unlock(&mixer->reg_mutex);
 }
 
 static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
@@ -1225,9 +1148,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
                cptr = snd_info_get_str(str, cptr, sizeof(str));
                if (! *str) {
                        /* remove the entry */
-                       mutex_lock(&mixer->reg_mutex);
-                       mixer_slot_clear(&mixer->slots[ch]);
-                       mutex_unlock(&mixer->reg_mutex);
+                       scoped_guard(mutex, &mixer->reg_mutex)
+                               mixer_slot_clear(&mixer->slots[ch]);
                        continue;
                }
                snd_info_get_str(idxstr, cptr, sizeof(idxstr));
@@ -1236,28 +1158,27 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
                        pr_err("ALSA: mixer_oss: invalid index %d\n", idx);
                        continue;
                }
-               mutex_lock(&mixer->reg_mutex);
-               slot = (struct slot *)mixer->slots[ch].private_data;
-               if (slot && slot->assigned &&
-                   slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
-                       /* not changed */
-                       goto __unlock;
-               tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
-               if (!tbl)
-                       goto __unlock;
-               tbl->oss_id = ch;
-               tbl->name = kstrdup(str, GFP_KERNEL);
-               if (! tbl->name) {
-                       kfree(tbl);
-                       goto __unlock;
-               }
-               tbl->index = idx;
-               if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
-                       kfree(tbl->name);
-                       kfree(tbl);
+               scoped_guard(mutex, &mixer->reg_mutex) {
+                       slot = (struct slot *)mixer->slots[ch].private_data;
+                       if (slot && slot->assigned &&
+                           slot->assigned->index == idx && !strcmp(slot->assigned->name, str))
+                               /* not changed */
+                               break;
+                       tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+                       if (!tbl)
+                               break;
+                       tbl->oss_id = ch;
+                       tbl->name = kstrdup(str, GFP_KERNEL);
+                       if (!tbl->name) {
+                               kfree(tbl);
+                               break;
+                       }
+                       tbl->index = idx;
+                       if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
+                               kfree(tbl->name);
+                               kfree(tbl);
+                       }
                }
-       __unlock:
-               mutex_unlock(&mixer->reg_mutex);
        }
 }
 
index 728c211142d145df7143856d3a0ea1912f173760..7386982cf40edcbaf6c5832301d0556f90a1e2c0 100644 (file)
@@ -377,7 +377,7 @@ 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 = NULL;
+       struct snd_pcm_hw_params *save __free(kfree) = NULL;
        int v;
        unsigned int saved_min;
        int last = 0;
@@ -404,38 +404,30 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
        saved_min = min;
        min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
        if (min >= 0) {
-               struct snd_pcm_hw_params *params1;
+               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);
-               if (params1 == NULL) {
-                       kfree(save);
+               if (params1 == NULL)
                        return -ENOMEM;
-               }
                *params1 = *save;
                max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
-               if (max < 0) {
-                       kfree(params1);
+               if (max < 0)
                        goto _end;
-               }
                if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
                        *params = *params1;
                        last = 1;
                }
-               kfree(params1);
        } else {
                *params = *save;
                max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
-               if (max < 0) {
-                       kfree(save);
+               if (max < 0)
                        return max;
-               }
                last = 1;
        }
  _end:
-       kfree(save);
        if (last)
                v = snd_pcm_hw_param_last(pcm, params, var, dir);
        else
@@ -789,7 +781,7 @@ 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;
+       struct snd_pcm_hw_params *save __free(kfree) = NULL;
        unsigned int rate, prev;
 
        save = kmalloc(sizeof(*save), GFP_KERNEL);
@@ -808,10 +800,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
                        ret = snd_pcm_hw_param_set(substream, params,
                                                   SNDRV_PCM_HW_PARAM_RATE,
                                                   rate, 0);
-                       if (ret == (int)rate) {
-                               kfree(save);
+                       if (ret == (int)rate)
                                return rate;
-                       }
                        *params = *save;
                }
                prev = rate;
@@ -821,7 +811,6 @@ static int choose_rate(struct snd_pcm_substream *substream,
        }
 
        /* not found, use the nearest rate */
-       kfree(save);
        return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
 }
 
@@ -1634,9 +1623,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
                        break;
                result = 0;
                set_current_state(TASK_INTERRUPTIBLE);
-               snd_pcm_stream_lock_irq(substream);
-               state = runtime->state;
-               snd_pcm_stream_unlock_irq(substream);
+               scoped_guard(pcm_stream_lock_irq, substream)
+                       state = runtime->state;
                if (state != SNDRV_PCM_STATE_RUNNING) {
                        set_current_state(TASK_RUNNING);
                        break;
@@ -1847,7 +1835,7 @@ 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;
+       struct snd_pcm_hw_params *params __free(kfree) = NULL;
        unsigned int formats = 0;
        const struct snd_mask *format_mask;
        int fmt;
@@ -1873,7 +1861,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
        _snd_pcm_hw_params_any(params);
        err = snd_pcm_hw_refine(substream, params);
        if (err < 0)
-               goto error;
+               return err;
        format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
        for (fmt = 0; fmt < 32; ++fmt) {
                if (snd_mask_test(format_mask, fmt)) {
@@ -1883,9 +1871,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
                }
        }
 
- error:
-       kfree(params);
-       return err < 0 ? err : formats;
+       return formats;
 }
 
 static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
@@ -2347,7 +2333,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
 {
        struct snd_pcm_oss_setup *setup;
 
-       mutex_lock(&pcm->streams[stream].oss.setup_mutex);
+       guard(mutex)(&pcm->streams[stream].oss.setup_mutex);
        do {
                for (setup = pcm->streams[stream].oss.setup_list; setup;
                     setup = setup->next) {
@@ -2358,7 +2344,6 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
  out:
        if (setup)
                *rsetup = *setup;
-       mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
 }
 
 static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
@@ -2853,23 +2838,23 @@ static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)
        if (psubstream != NULL) {
                struct snd_pcm_runtime *runtime = psubstream->runtime;
                poll_wait(file, &runtime->sleep, wait);
-               snd_pcm_stream_lock_irq(psubstream);
-               if (runtime->state != SNDRV_PCM_STATE_DRAINING &&
-                   (runtime->state != SNDRV_PCM_STATE_RUNNING ||
-                    snd_pcm_oss_playback_ready(psubstream)))
-                       mask |= EPOLLOUT | EPOLLWRNORM;
-               snd_pcm_stream_unlock_irq(psubstream);
+               scoped_guard(pcm_stream_lock_irq, psubstream) {
+                       if (runtime->state != SNDRV_PCM_STATE_DRAINING &&
+                           (runtime->state != SNDRV_PCM_STATE_RUNNING ||
+                            snd_pcm_oss_playback_ready(psubstream)))
+                               mask |= EPOLLOUT | EPOLLWRNORM;
+               }
        }
        if (csubstream != NULL) {
                struct snd_pcm_runtime *runtime = csubstream->runtime;
                snd_pcm_state_t ostate;
                poll_wait(file, &runtime->sleep, wait);
-               snd_pcm_stream_lock_irq(csubstream);
-               ostate = runtime->state;
-               if (ostate != SNDRV_PCM_STATE_RUNNING ||
-                   snd_pcm_oss_capture_ready(csubstream))
-                       mask |= EPOLLIN | EPOLLRDNORM;
-               snd_pcm_stream_unlock_irq(csubstream);
+               scoped_guard(pcm_stream_lock_irq, csubstream) {
+                       ostate = runtime->state;
+                       if (ostate != SNDRV_PCM_STATE_RUNNING ||
+                           snd_pcm_oss_capture_ready(csubstream))
+                               mask |= EPOLLIN | EPOLLRDNORM;
+               }
                if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
                        struct snd_pcm_oss_file ofile;
                        memset(&ofile, 0, sizeof(ofile));
@@ -2964,7 +2949,7 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
 {
        struct snd_pcm_str *pstr = entry->private_data;
        struct snd_pcm_oss_setup *setup = pstr->oss.setup_list;
-       mutex_lock(&pstr->oss.setup_mutex);
+       guard(mutex)(&pstr->oss.setup_mutex);
        while (setup) {
                snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
                            setup->task_name,
@@ -2978,7 +2963,6 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
                            setup->nosilence ? " no-silence" : "");
                setup = setup->next;
        }
-       mutex_unlock(&pstr->oss.setup_mutex);
 }
 
 static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)
@@ -3004,12 +2988,11 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
        struct snd_pcm_oss_setup *setup, *setup1, template;
 
        while (!snd_info_get_line(buffer, line, sizeof(line))) {
-               mutex_lock(&pstr->oss.setup_mutex);
+               guard(mutex)(&pstr->oss.setup_mutex);
                memset(&template, 0, sizeof(template));
                ptr = snd_info_get_str(task_name, line, sizeof(task_name));
                if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
                        snd_pcm_oss_proc_free_setup_list(pstr);
-                       mutex_unlock(&pstr->oss.setup_mutex);
                        continue;
                }
                for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
@@ -3049,7 +3032,6 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
                        setup = kmalloc(sizeof(*setup), GFP_KERNEL);
                        if (! setup) {
                                buffer->error = -ENOMEM;
-                               mutex_unlock(&pstr->oss.setup_mutex);
                                return;
                        }
                        if (pstr->oss.setup_list == NULL)
@@ -3063,12 +3045,10 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
                        if (! template.task_name) {
                                kfree(setup);
                                buffer->error = -ENOMEM;
-                               mutex_unlock(&pstr->oss.setup_mutex);
                                return;
                        }
                }
                *setup = template;
-               mutex_unlock(&pstr->oss.setup_mutex);
        }
 }
 
index d0788126cbab10a2ef8daaab9201f366f27d8c63..dc37f3508dc7a04c33d7471293cd9e22ffba66c4 100644 (file)
@@ -91,9 +91,8 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
 
                        if (get_user(device, (int __user *)arg))
                                return -EFAULT;
-                       mutex_lock(&register_mutex);
-                       device = snd_pcm_next(card, device);
-                       mutex_unlock(&register_mutex);
+                       scoped_guard(mutex, &register_mutex)
+                               device = snd_pcm_next(card, device);
                        if (put_user(device, (int __user *)arg))
                                return -EFAULT;
                        return 0;
@@ -106,7 +105,6 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
                        struct snd_pcm *pcm;
                        struct snd_pcm_str *pstr;
                        struct snd_pcm_substream *substream;
-                       int err;
 
                        info = (struct snd_pcm_info __user *)arg;
                        if (get_user(device, &info->device))
@@ -118,35 +116,23 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
                        stream = array_index_nospec(stream, 2);
                        if (get_user(subdevice, &info->subdevice))
                                return -EFAULT;
-                       mutex_lock(&register_mutex);
+                       guard(mutex)(&register_mutex);
                        pcm = snd_pcm_get(card, device);
-                       if (pcm == NULL) {
-                               err = -ENXIO;
-                               goto _error;
-                       }
+                       if (pcm == NULL)
+                               return -ENXIO;
                        pstr = &pcm->streams[stream];
-                       if (pstr->substream_count == 0) {
-                               err = -ENOENT;
-                               goto _error;
-                       }
-                       if (subdevice >= pstr->substream_count) {
-                               err = -ENXIO;
-                               goto _error;
-                       }
+                       if (pstr->substream_count == 0)
+                               return -ENOENT;
+                       if (subdevice >= pstr->substream_count)
+                               return -ENXIO;
                        for (substream = pstr->substream; substream;
                             substream = substream->next)
                                if (substream->number == (int)subdevice)
                                        break;
-                       if (substream == NULL) {
-                               err = -ENXIO;
-                               goto _error;
-                       }
-                       mutex_lock(&pcm->open_mutex);
-                       err = snd_pcm_info_user(substream, info);
-                       mutex_unlock(&pcm->open_mutex);
-               _error:
-                       mutex_unlock(&register_mutex);
-                       return err;
+                       if (substream == NULL)
+                               return -ENXIO;
+                       guard(mutex)(&pcm->open_mutex);
+                       return snd_pcm_info_user(substream, info);
                }
        case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
                {
@@ -225,9 +211,11 @@ static const char * const snd_pcm_format_names[] = {
  */
 const char *snd_pcm_format_name(snd_pcm_format_t format)
 {
-       if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
+       unsigned int format_num = (__force unsigned int)format;
+
+       if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num])
                return "Unknown";
-       return snd_pcm_format_names[(__force unsigned int)format];
+       return snd_pcm_format_names[format_num];
 }
 EXPORT_SYMBOL_GPL(snd_pcm_format_name);
 
@@ -340,7 +328,7 @@ 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;
+       struct snd_pcm_info *info __free(kfree) = NULL;
        int err;
 
        if (! substream)
@@ -353,7 +341,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
        err = snd_pcm_info(substream, info);
        if (err < 0) {
                snd_iprintf(buffer, "error %d\n", err);
-               kfree(info);
                return;
        }
        snd_iprintf(buffer, "card: %d\n", info->card);
@@ -367,7 +354,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
        snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass);
        snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count);
        snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail);
-       kfree(info);
 }
 
 static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
@@ -389,15 +375,15 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
        struct snd_pcm_substream *substream = entry->private_data;
        struct snd_pcm_runtime *runtime;
 
-       mutex_lock(&substream->pcm->open_mutex);
+       guard(mutex)(&substream->pcm->open_mutex);
        runtime = substream->runtime;
        if (!runtime) {
                snd_iprintf(buffer, "closed\n");
-               goto unlock;
+               return;
        }
        if (runtime->state == SNDRV_PCM_STATE_OPEN) {
                snd_iprintf(buffer, "no setup\n");
-               goto unlock;
+               return;
        }
        snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
        snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
@@ -416,8 +402,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
                snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
        }
 #endif
- unlock:
-       mutex_unlock(&substream->pcm->open_mutex);
 }
 
 static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
@@ -426,15 +410,15 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
        struct snd_pcm_substream *substream = entry->private_data;
        struct snd_pcm_runtime *runtime;
 
-       mutex_lock(&substream->pcm->open_mutex);
+       guard(mutex)(&substream->pcm->open_mutex);
        runtime = substream->runtime;
        if (!runtime) {
                snd_iprintf(buffer, "closed\n");
-               goto unlock;
+               return;
        }
        if (runtime->state == SNDRV_PCM_STATE_OPEN) {
                snd_iprintf(buffer, "no setup\n");
-               goto unlock;
+               return;
        }
        snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
        snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
@@ -444,8 +428,6 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
        snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
        snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
        snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
- unlock:
-       mutex_unlock(&substream->pcm->open_mutex);
 }
 
 static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
@@ -456,17 +438,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
        struct snd_pcm_status64 status;
        int err;
 
-       mutex_lock(&substream->pcm->open_mutex);
+       guard(mutex)(&substream->pcm->open_mutex);
        runtime = substream->runtime;
        if (!runtime) {
                snd_iprintf(buffer, "closed\n");
-               goto unlock;
+               return;
        }
        memset(&status, 0, sizeof(status));
        err = snd_pcm_status64(substream, &status);
        if (err < 0) {
                snd_iprintf(buffer, "error %d\n", err);
-               goto unlock;
+               return;
        }
        snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
        snd_iprintf(buffer, "owner_pid   : %d\n", pid_vnr(substream->pid));
@@ -480,8 +462,6 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
        snd_iprintf(buffer, "-----\n");
        snd_iprintf(buffer, "hw_ptr      : %ld\n", runtime->status->hw_ptr);
        snd_iprintf(buffer, "appl_ptr    : %ld\n", runtime->control->appl_ptr);
- unlock:
-       mutex_unlock(&substream->pcm->open_mutex);
 }
 
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -1009,9 +989,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        kfree(runtime->hw_constraints.rules);
        /* Avoid concurrent access to runtime via PCM timer interface */
        if (substream->timer) {
-               spin_lock_irq(&substream->timer->lock);
-               substream->runtime = NULL;
-               spin_unlock_irq(&substream->timer->lock);
+               scoped_guard(spinlock_irq, &substream->timer->lock)
+                       substream->runtime = NULL;
        } else {
                substream->runtime = NULL;
        }
@@ -1068,10 +1047,10 @@ static int snd_pcm_dev_register(struct snd_device *device)
                return -ENXIO;
        pcm = device->device_data;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        err = snd_pcm_add(pcm);
        if (err)
-               goto unlock;
+               return err;
        for (cidx = 0; cidx < 2; cidx++) {
                int devtype = -1;
                if (pcm->streams[cidx].substream == NULL)
@@ -1090,7 +1069,7 @@ static int snd_pcm_dev_register(struct snd_device *device)
                                          pcm->streams[cidx].dev);
                if (err < 0) {
                        list_del_init(&pcm->list);
-                       goto unlock;
+                       return err;
                }
 
                for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
@@ -1098,9 +1077,6 @@ static int snd_pcm_dev_register(struct snd_device *device)
        }
 
        pcm_call_notify(pcm, n_register);
-
- unlock:
-       mutex_unlock(&register_mutex);
        return err;
 }
 
@@ -1110,8 +1086,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
        struct snd_pcm_substream *substream;
        int cidx;
 
-       mutex_lock(&register_mutex);
-       mutex_lock(&pcm->open_mutex);
+       guard(mutex)(&register_mutex);
+       guard(mutex)(&pcm->open_mutex);
        wake_up(&pcm->open_wait);
        list_del_init(&pcm->list);
 
@@ -1138,8 +1114,6 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
                        snd_unregister_device(pcm->streams[cidx].dev);
                free_chmap(&pcm->streams[cidx]);
        }
-       mutex_unlock(&pcm->open_mutex);
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -1164,7 +1138,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
                       !notify->n_unregister ||
                       !notify->n_disconnect))
                return -EINVAL;
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        if (nfree) {
                list_del(&notify->list);
                list_for_each_entry(pcm, &snd_pcm_devices, list)
@@ -1174,7 +1148,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
                list_for_each_entry(pcm, &snd_pcm_devices, list)
                        notify->n_register(pcm);
        }
-       mutex_unlock(&register_mutex);
        return 0;
 }
 EXPORT_SYMBOL(snd_pcm_notify);
@@ -1190,7 +1163,7 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
 {
        struct snd_pcm *pcm;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        list_for_each_entry(pcm, &snd_pcm_devices, list) {
                snd_iprintf(buffer, "%02i-%02i: %s : %s",
                            pcm->card->number, pcm->device, pcm->id, pcm->name);
@@ -1202,7 +1175,6 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
                                    pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
                snd_iprintf(buffer, "\n");
        }
-       mutex_unlock(&register_mutex);
 }
 
 static struct snd_info_entry *snd_pcm_proc_entry;
index c96483091f30aa3ff63c3b55bb466b8b63f5df4d..a42ec7f5a1daf0688b758e56d5fcf09959cec8de 100644 (file)
@@ -235,7 +235,7 @@ 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;
+       struct snd_pcm_hw_params *data __free(kfree) = NULL;
        struct snd_pcm_runtime *runtime;
        int err;
 
@@ -248,34 +248,28 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
                return -ENOMEM;
 
        /* only fifo_size (RO from userspace) is different, so just copy all */
-       if (copy_from_user(data, data32, sizeof(*data32))) {
-               err = -EFAULT;
-               goto error;
-       }
+       if (copy_from_user(data, data32, sizeof(*data32)))
+               return -EFAULT;
 
        if (refine) {
                err = snd_pcm_hw_refine(substream, data);
                if (err < 0)
-                       goto error;
+                       return err;
                err = fixup_unreferenced_params(substream, data);
        } else {
                err = snd_pcm_hw_params(substream, data);
        }
        if (err < 0)
-               goto error;
+               return err;
        if (copy_to_user(data32, data, sizeof(*data32)) ||
-           put_user(data->fifo_size, &data32->fifo_size)) {
-               err = -EFAULT;
-               goto error;
-       }
+           put_user(data->fifo_size, &data32->fifo_size))
+               return -EFAULT;
 
        if (! refine) {
                unsigned int new_boundary = recalculate_boundary(runtime);
                if (new_boundary)
                        runtime->boundary = new_boundary;
        }
- error:
-       kfree(data);
        return err;
 }
 
@@ -338,7 +332,7 @@ 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;
+       void __user **bufs __free(kfree) = NULL;
        int err, ch, i;
 
        if (! substream->runtime)
@@ -360,10 +354,8 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
                return -ENOMEM;
        for (i = 0; i < ch; i++) {
                u32 ptr;
-               if (get_user(ptr, bufptr)) {
-                       kfree(bufs);
+               if (get_user(ptr, bufptr))
                        return -EFAULT;
-               }
                bufs[i] = compat_ptr(ptr);
                bufptr++;
        }
@@ -373,9 +365,8 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
                err = snd_pcm_lib_readv(substream, bufs, frames);
        if (err >= 0) {
                if (put_user(err, &data32->result))
-                       err = -EFAULT;
+                       return -EFAULT;
        }
-       kfree(bufs);
        return err;
 }
 
@@ -441,22 +432,22 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
        boundary = recalculate_boundary(runtime);
        if (!boundary)
                boundary = 0x7fffffff;
-       snd_pcm_stream_lock_irq(substream);
-       /* FIXME: we should consider the boundary for the sync from app */
-       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
-               control->appl_ptr = scontrol.appl_ptr;
-       else
-               scontrol.appl_ptr = control->appl_ptr % boundary;
-       if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
-               control->avail_min = scontrol.avail_min;
-       else
-               scontrol.avail_min = control->avail_min;
-       sstatus.state = status->state;
-       sstatus.hw_ptr = status->hw_ptr % boundary;
-       sstatus.tstamp = status->tstamp;
-       sstatus.suspended_state = status->suspended_state;
-       sstatus.audio_tstamp = status->audio_tstamp;
-       snd_pcm_stream_unlock_irq(substream);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               /* FIXME: we should consider the boundary for the sync from app */
+               if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+                       control->appl_ptr = scontrol.appl_ptr;
+               else
+                       scontrol.appl_ptr = control->appl_ptr % boundary;
+               if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+                       control->avail_min = scontrol.avail_min;
+               else
+                       scontrol.avail_min = control->avail_min;
+               sstatus.state = status->state;
+               sstatus.hw_ptr = status->hw_ptr % boundary;
+               sstatus.tstamp = status->tstamp;
+               sstatus.suspended_state = status->suspended_state;
+               sstatus.audio_tstamp = status->audio_tstamp;
+       }
        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (put_user(sstatus.state, &src->s.status.state) ||
@@ -519,26 +510,24 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream,
                if (err < 0)
                        return err;
        }
-       snd_pcm_stream_lock_irq(substream);
-       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
-               err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr);
-               if (err < 0) {
-                       snd_pcm_stream_unlock_irq(substream);
-                       return err;
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
+                       err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr);
+                       if (err < 0)
+                               return err;
+               } else {
+                       sync_cp->appl_ptr = control->appl_ptr;
                }
-       } else {
-               sync_cp->appl_ptr = control->appl_ptr;
+               if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+                       control->avail_min = sync_cp->avail_min;
+               else
+                       sync_cp->avail_min = control->avail_min;
+               sync_ptr.s.status.state = status->state;
+               sync_ptr.s.status.hw_ptr = status->hw_ptr;
+               sync_ptr.s.status.tstamp = status->tstamp;
+               sync_ptr.s.status.suspended_state = status->suspended_state;
+               sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
        }
-       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
-               control->avail_min = sync_cp->avail_min;
-       else
-               sync_cp->avail_min = control->avail_min;
-       sync_ptr.s.status.state = status->state;
-       sync_ptr.s.status.hw_ptr = status->hw_ptr;
-       sync_ptr.s.status.tstamp = status->tstamp;
-       sync_ptr.s.status.suspended_state = status->suspended_state;
-       sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
-       snd_pcm_stream_unlock_irq(substream);
        if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
index 41103e5c43ce5bf852908a3b72ed73468f21695a..6f73b3c2c205ee201552fb4fb1b2efbc6a63b1d0 100644 (file)
@@ -1744,8 +1744,8 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
                                   void *arg)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned long flags;
-       snd_pcm_stream_lock_irqsave(substream, flags);
+
+       guard(pcm_stream_lock_irqsave)(substream);
        if (snd_pcm_running(substream) &&
            snd_pcm_update_hw_ptr(substream) >= 0)
                runtime->status->hw_ptr %= runtime->buffer_size;
@@ -1753,7 +1753,6 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
                runtime->status->hw_ptr = 0;
                runtime->hw_ptr_wrap = 0;
        }
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
        return 0;
 }
 
@@ -1899,14 +1898,11 @@ EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
  */
 void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!substream))
                return;
 
-       snd_pcm_stream_lock_irqsave(substream, flags);
+       guard(pcm_stream_lock_irqsave)(substream);
        snd_pcm_period_elapsed_under_stream_lock(substream);
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
 }
 EXPORT_SYMBOL(snd_pcm_period_elapsed);
 
index a0b9514716995fb65c31ca0fef62a8368d359416..506386959f0848f789de5042ea9a4c15bc4dc7f9 100644 (file)
@@ -38,17 +38,15 @@ static void __update_allocated_size(struct snd_card *card, ssize_t bytes)
 
 static void update_allocated_size(struct snd_card *card, ssize_t bytes)
 {
-       mutex_lock(&card->memory_mutex);
+       guard(mutex)(&card->memory_mutex);
        __update_allocated_size(card, bytes);
-       mutex_unlock(&card->memory_mutex);
 }
 
 static void decrease_allocated_size(struct snd_card *card, size_t bytes)
 {
-       mutex_lock(&card->memory_mutex);
+       guard(mutex)(&card->memory_mutex);
        WARN_ON(card->total_pcm_alloc_bytes < bytes);
        __update_allocated_size(card, -(ssize_t)bytes);
-       mutex_unlock(&card->memory_mutex);
 }
 
 static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
@@ -58,14 +56,12 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
        int err;
 
        /* check and reserve the requested size */
-       mutex_lock(&card->memory_mutex);
-       if (max_alloc_per_card &&
-           card->total_pcm_alloc_bytes + size > max_alloc_per_card) {
-               mutex_unlock(&card->memory_mutex);
-               return -ENOMEM;
+       scoped_guard(mutex, &card->memory_mutex) {
+               if (max_alloc_per_card &&
+                   card->total_pcm_alloc_bytes + size > max_alloc_per_card)
+                       return -ENOMEM;
+               __update_allocated_size(card, size);
        }
-       __update_allocated_size(card, size);
-       mutex_unlock(&card->memory_mutex);
 
        if (str == SNDRV_PCM_STREAM_PLAYBACK)
                dir = DMA_TO_DEVICE;
@@ -191,20 +187,20 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
        size_t size;
        struct snd_dma_buffer new_dmab;
 
-       mutex_lock(&substream->pcm->open_mutex);
+       guard(mutex)(&substream->pcm->open_mutex);
        if (substream->runtime) {
                buffer->error = -EBUSY;
-               goto unlock;
+               return;
        }
        if (!snd_info_get_line(buffer, line, sizeof(line))) {
                snd_info_get_str(str, line, sizeof(str));
                size = simple_strtoul(str, NULL, 10) * 1024;
                if ((size != 0 && size < 8192) || size > substream->dma_max) {
                        buffer->error = -EINVAL;
-                       goto unlock;
+                       return;
                }
                if (substream->dma_buffer.bytes == size)
-                       goto unlock;
+                       return;
                memset(&new_dmab, 0, sizeof(new_dmab));
                new_dmab.dev = substream->dma_buffer.dev;
                if (size > 0) {
@@ -218,7 +214,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
                                         substream->pcm->card->number, substream->pcm->device,
                                         substream->stream ? 'c' : 'p', substream->number,
                                         substream->pcm->name, size);
-                               goto unlock;
+                               return;
                        }
                        substream->buffer_bytes_max = size;
                } else {
@@ -230,8 +226,6 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
        } else {
                buffer->error = -EINVAL;
        }
- unlock:
-       mutex_unlock(&substream->pcm->open_mutex);
 }
 
 static inline void preallocate_info_init(struct snd_pcm_substream *substream)
index 21baf6bf7e25a048e64d37331b0109f2ad7ec354..0b76e76823d288d92f6e3355eda2a08cf15ddf9c 100644 (file)
@@ -236,7 +236,7 @@ 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;
+       struct snd_pcm_info *info __free(kfree) = NULL;
        int err;
 
        info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -247,7 +247,6 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
                if (copy_to_user(_info, info, sizeof(*info)))
                        err = -EFAULT;
        }
-       kfree(info);
        return err;
 }
 
@@ -359,7 +358,7 @@ 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;
+       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;
@@ -435,10 +434,8 @@ retry:
                }
 
                changed = r->func(params, r);
-               if (changed < 0) {
-                       err = changed;
-                       goto out;
-               }
+               if (changed < 0)
+                       return changed;
 
                /*
                 * When the parameter is changed, notify it to the caller
@@ -469,8 +466,6 @@ retry:
        if (again)
                goto retry;
 
- out:
-       kfree(rstamps);
        return err;
 }
 
@@ -576,26 +571,24 @@ 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;
+       struct snd_pcm_hw_params *params __free(kfree) = NULL;
        int err;
 
        params = memdup_user(_params, sizeof(*params));
        if (IS_ERR(params))
-               return PTR_ERR(params);
+               return PTR_ERR(no_free_ptr(params));
 
        err = snd_pcm_hw_refine(substream, params);
        if (err < 0)
-               goto end;
+               return err;
 
        err = fixup_unreferenced_params(substream, params);
        if (err < 0)
-               goto end;
+               return err;
 
        if (copy_to_user(_params, params, sizeof(*params)))
-               err = -EFAULT;
-end:
-       kfree(params);
-       return err;
+               return -EFAULT;
+       return 0;
 }
 
 static int period_to_usecs(struct snd_pcm_runtime *runtime)
@@ -616,10 +609,9 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
 static void snd_pcm_set_state(struct snd_pcm_substream *substream,
                              snd_pcm_state_t state)
 {
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED)
                __snd_pcm_set_state(substream->runtime, state);
-       snd_pcm_stream_unlock_irq(substream);
 }
 
 static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
@@ -745,20 +737,20 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        err = snd_pcm_buffer_access_lock(runtime);
        if (err < 0)
                return err;
-       snd_pcm_stream_lock_irq(substream);
-       switch (runtime->state) {
-       case SNDRV_PCM_STATE_OPEN:
-       case SNDRV_PCM_STATE_SETUP:
-       case SNDRV_PCM_STATE_PREPARED:
-               if (!is_oss_stream(substream) &&
-                   atomic_read(&substream->mmap_count))
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               switch (runtime->state) {
+               case SNDRV_PCM_STATE_OPEN:
+               case SNDRV_PCM_STATE_SETUP:
+               case SNDRV_PCM_STATE_PREPARED:
+                       if (!is_oss_stream(substream) &&
+                           atomic_read(&substream->mmap_count))
+                               err = -EBADFD;
+                       break;
+               default:
                        err = -EBADFD;
-               break;
-       default:
-               err = -EBADFD;
-               break;
+                       break;
+               }
        }
-       snd_pcm_stream_unlock_irq(substream);
        if (err)
                goto unlock;
 
@@ -869,21 +861,19 @@ 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;
+       struct snd_pcm_hw_params *params __free(kfree) = NULL;
        int err;
 
        params = memdup_user(_params, sizeof(*params));
        if (IS_ERR(params))
-               return PTR_ERR(params);
+               return PTR_ERR(no_free_ptr(params));
 
        err = snd_pcm_hw_params(substream, params);
        if (err < 0)
-               goto end;
+               return err;
 
        if (copy_to_user(_params, params, sizeof(*params)))
-               err = -EFAULT;
-end:
-       kfree(params);
+               return -EFAULT;
        return err;
 }
 
@@ -910,18 +900,18 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
        result = snd_pcm_buffer_access_lock(runtime);
        if (result < 0)
                return result;
-       snd_pcm_stream_lock_irq(substream);
-       switch (runtime->state) {
-       case SNDRV_PCM_STATE_SETUP:
-       case SNDRV_PCM_STATE_PREPARED:
-               if (atomic_read(&substream->mmap_count))
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               switch (runtime->state) {
+               case SNDRV_PCM_STATE_SETUP:
+               case SNDRV_PCM_STATE_PREPARED:
+                       if (atomic_read(&substream->mmap_count))
+                               result = -EBADFD;
+                       break;
+               default:
                        result = -EBADFD;
-               break;
-       default:
-               result = -EBADFD;
-               break;
+                       break;
+               }
        }
-       snd_pcm_stream_unlock_irq(substream);
        if (result)
                goto unlock;
        result = do_hw_free(substream);
@@ -941,12 +931,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
-       snd_pcm_stream_lock_irq(substream);
-       if (runtime->state == SNDRV_PCM_STATE_OPEN) {
-               snd_pcm_stream_unlock_irq(substream);
-               return -EBADFD;
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               if (runtime->state == SNDRV_PCM_STATE_OPEN)
+                       return -EBADFD;
        }
-       snd_pcm_stream_unlock_irq(substream);
 
        if (params->tstamp_mode < 0 ||
            params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
@@ -966,24 +954,24 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
                        return -EINVAL;
        }
        err = 0;
-       snd_pcm_stream_lock_irq(substream);
-       runtime->tstamp_mode = params->tstamp_mode;
-       if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12))
-               runtime->tstamp_type = params->tstamp_type;
-       runtime->period_step = params->period_step;
-       runtime->control->avail_min = params->avail_min;
-       runtime->start_threshold = params->start_threshold;
-       runtime->stop_threshold = params->stop_threshold;
-       runtime->silence_threshold = params->silence_threshold;
-       runtime->silence_size = params->silence_size;
-        params->boundary = runtime->boundary;
-       if (snd_pcm_running(substream)) {
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-                   runtime->silence_size > 0)
-                       snd_pcm_playback_silence(substream, ULONG_MAX);
-               err = snd_pcm_update_state(substream, runtime);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               runtime->tstamp_mode = params->tstamp_mode;
+               if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12))
+                       runtime->tstamp_type = params->tstamp_type;
+               runtime->period_step = params->period_step;
+               runtime->control->avail_min = params->avail_min;
+               runtime->start_threshold = params->start_threshold;
+               runtime->stop_threshold = params->stop_threshold;
+               runtime->silence_threshold = params->silence_threshold;
+               runtime->silence_size = params->silence_size;
+               params->boundary = runtime->boundary;
+               if (snd_pcm_running(substream)) {
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+                           runtime->silence_size > 0)
+                               snd_pcm_playback_silence(substream, ULONG_MAX);
+                       err = snd_pcm_update_state(substream, runtime);
+               }
        }
-       snd_pcm_stream_unlock_irq(substream);
        return err;
 }
 
@@ -1017,7 +1005,7 @@ int snd_pcm_status64(struct snd_pcm_substream *substream,
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
 
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
 
        snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
                                        &runtime->audio_tstamp_config);
@@ -1038,7 +1026,7 @@ int snd_pcm_status64(struct snd_pcm_substream *substream,
        status->state = runtime->state;
        status->suspended_state = runtime->suspended_state;
        if (status->state == SNDRV_PCM_STATE_OPEN)
-               goto _end;
+               return 0;
        status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec;
        status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec;
        if (snd_pcm_running(substream)) {
@@ -1083,8 +1071,6 @@ int snd_pcm_status64(struct snd_pcm_substream *substream,
        status->overrange = runtime->overrange;
        runtime->avail_max = 0;
        runtime->overrange = 0;
- _end:
-       snd_pcm_stream_unlock_irq(substream);
        return 0;
 }
 
@@ -1169,12 +1155,10 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
        
        channel = info->channel;
        runtime = substream->runtime;
-       snd_pcm_stream_lock_irq(substream);
-       if (runtime->state == SNDRV_PCM_STATE_OPEN) {
-               snd_pcm_stream_unlock_irq(substream);
-               return -EBADFD;
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               if (runtime->state == SNDRV_PCM_STATE_OPEN)
+                       return -EBADFD;
        }
-       snd_pcm_stream_unlock_irq(substream);
        if (channel >= runtime->channels)
                return -EINVAL;
        memset(info, 0, sizeof(*info));
@@ -1395,12 +1379,8 @@ static int snd_pcm_action_lock_irq(const struct action_ops *ops,
                                   struct snd_pcm_substream *substream,
                                   snd_pcm_state_t state)
 {
-       int res;
-
-       snd_pcm_stream_lock_irq(substream);
-       res = snd_pcm_action(ops, substream, state);
-       snd_pcm_stream_unlock_irq(substream);
-       return res;
+       guard(pcm_stream_lock_irq)(substream);
+       return snd_pcm_action(ops, substream, state);
 }
 
 /*
@@ -1412,17 +1392,15 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
        int res;
 
        /* Guarantee the group members won't change during non-atomic action */
-       down_read(&snd_pcm_link_rwsem);
+       guard(rwsem_read)(&snd_pcm_link_rwsem);
        res = snd_pcm_buffer_access_lock(substream->runtime);
        if (res < 0)
-               goto unlock;
+               return res;
        if (snd_pcm_stream_linked(substream))
                res = snd_pcm_action_group(ops, substream, state, false);
        else
                res = snd_pcm_action_single(ops, substream, state);
        snd_pcm_buffer_access_unlock(substream->runtime);
- unlock:
-       up_read(&snd_pcm_link_rwsem);
        return res;
 }
 
@@ -1592,12 +1570,9 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
  */
 int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
 {
-       unsigned long flags;
-
-       snd_pcm_stream_lock_irqsave(substream, flags);
+       guard(pcm_stream_lock_irqsave)(substream);
        if (substream->runtime && snd_pcm_running(substream))
                __snd_pcm_xrun(substream);
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
@@ -1749,14 +1724,9 @@ static const struct action_ops snd_pcm_action_suspend = {
  */
 static int snd_pcm_suspend(struct snd_pcm_substream *substream)
 {
-       int err;
-       unsigned long flags;
-
-       snd_pcm_stream_lock_irqsave(substream, flags);
-       err = snd_pcm_action(&snd_pcm_action_suspend, substream,
-                            ACTION_ARG_IGNORE);
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
-       return err;
+       guard(pcm_stream_lock_irqsave)(substream);
+       return snd_pcm_action(&snd_pcm_action_suspend, substream,
+                             ACTION_ARG_IGNORE);
 }
 
 /**
@@ -1872,22 +1842,17 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
 static int snd_pcm_xrun(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int result;
 
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        switch (runtime->state) {
        case SNDRV_PCM_STATE_XRUN:
-               result = 0;     /* already there */
-               break;
+               return 0;       /* already there */
        case SNDRV_PCM_STATE_RUNNING:
                __snd_pcm_xrun(substream);
-               result = 0;
-               break;
+               return 0;
        default:
-               result = -EBADFD;
+               return -EBADFD;
        }
-       snd_pcm_stream_unlock_irq(substream);
-       return result;
 }
 
 /*
@@ -1916,13 +1881,12 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream,
        int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
        if (err < 0)
                return err;
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        runtime->hw_ptr_base = 0;
        runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
                runtime->status->hw_ptr % runtime->period_size;
        runtime->silence_start = runtime->status->hw_ptr;
        runtime->silence_filled = 0;
-       snd_pcm_stream_unlock_irq(substream);
        return 0;
 }
 
@@ -1930,12 +1894,11 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream,
                               snd_pcm_state_t state)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        runtime->control->appl_ptr = runtime->status->hw_ptr;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, ULONG_MAX);
-       snd_pcm_stream_unlock_irq(substream);
 }
 
 static const struct action_ops snd_pcm_action_reset = {
@@ -2011,16 +1974,16 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
        else
                f_flags = substream->f_flags;
 
-       snd_pcm_stream_lock_irq(substream);
-       switch (substream->runtime->state) {
-       case SNDRV_PCM_STATE_PAUSED:
-               snd_pcm_pause(substream, false);
-               fallthrough;
-       case SNDRV_PCM_STATE_SUSPENDED:
-               snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-               break;
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               switch (substream->runtime->state) {
+               case SNDRV_PCM_STATE_PAUSED:
+                       snd_pcm_pause(substream, false);
+                       fallthrough;
+               case SNDRV_PCM_STATE_SUSPENDED:
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+                       break;
+               }
        }
-       snd_pcm_stream_unlock_irq(substream);
 
        return snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
                                        substream,
@@ -2237,14 +2200,13 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
            runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
                return -EBADFD;
 
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        /* resume pause */
        if (runtime->state == SNDRV_PCM_STATE_PAUSED)
                snd_pcm_pause(substream, false);
 
        snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
        /* runtime->control->appl_ptr = runtime->status->hw_ptr; */
-       snd_pcm_stream_unlock_irq(substream);
 
        return result;
 }
@@ -2273,53 +2235,44 @@ static bool is_pcm_file(struct file *file)
  */
 static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
 {
-       int res = 0;
        struct snd_pcm_file *pcm_file;
        struct snd_pcm_substream *substream1;
-       struct snd_pcm_group *group, *target_group;
+       struct snd_pcm_group *group __free(kfree) = NULL;
+       struct snd_pcm_group *target_group;
        bool nonatomic = substream->pcm->nonatomic;
-       struct fd f = fdget(fd);
+       CLASS(fd, f)(fd);
 
        if (!f.file)
                return -EBADFD;
-       if (!is_pcm_file(f.file)) {
-               res = -EBADFD;
-               goto _badf;
-       }
+       if (!is_pcm_file(f.file))
+               return -EBADFD;
+
        pcm_file = f.file->private_data;
        substream1 = pcm_file->substream;
 
-       if (substream == substream1) {
-               res = -EINVAL;
-               goto _badf;
-       }
+       if (substream == substream1)
+               return -EINVAL;
 
        group = kzalloc(sizeof(*group), GFP_KERNEL);
-       if (!group) {
-               res = -ENOMEM;
-               goto _nolock;
-       }
+       if (!group)
+               return -ENOMEM;
        snd_pcm_group_init(group);
 
-       down_write(&snd_pcm_link_rwsem);
+       guard(rwsem_write)(&snd_pcm_link_rwsem);
        if (substream->runtime->state == SNDRV_PCM_STATE_OPEN ||
            substream->runtime->state != substream1->runtime->state ||
-           substream->pcm->nonatomic != substream1->pcm->nonatomic) {
-               res = -EBADFD;
-               goto _end;
-       }
-       if (snd_pcm_stream_linked(substream1)) {
-               res = -EALREADY;
-               goto _end;
-       }
+           substream->pcm->nonatomic != substream1->pcm->nonatomic)
+               return -EBADFD;
+       if (snd_pcm_stream_linked(substream1))
+               return -EALREADY;
 
-       snd_pcm_stream_lock_irq(substream);
-       if (!snd_pcm_stream_linked(substream)) {
-               snd_pcm_group_assign(substream, group);
-               group = NULL; /* assigned, don't free this one below */
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               if (!snd_pcm_stream_linked(substream)) {
+                       snd_pcm_group_assign(substream, group);
+                       group = NULL; /* assigned, don't free this one below */
+               }
+               target_group = substream->group;
        }
-       target_group = substream->group;
-       snd_pcm_stream_unlock_irq(substream);
 
        snd_pcm_group_lock_irq(target_group, nonatomic);
        snd_pcm_stream_lock_nested(substream1);
@@ -2327,13 +2280,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
        refcount_inc(&target_group->refs);
        snd_pcm_stream_unlock(substream1);
        snd_pcm_group_unlock_irq(target_group, nonatomic);
- _end:
-       up_write(&snd_pcm_link_rwsem);
- _nolock:
-       kfree(group);
- _badf:
-       fdput(f);
-       return res;
+       return 0;
 }
 
 static void relink_to_local(struct snd_pcm_substream *substream)
@@ -2348,14 +2295,11 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
        struct snd_pcm_group *group;
        bool nonatomic = substream->pcm->nonatomic;
        bool do_free = false;
-       int res = 0;
 
-       down_write(&snd_pcm_link_rwsem);
+       guard(rwsem_write)(&snd_pcm_link_rwsem);
 
-       if (!snd_pcm_stream_linked(substream)) {
-               res = -EALREADY;
-               goto _end;
-       }
+       if (!snd_pcm_stream_linked(substream))
+               return -EALREADY;
 
        group = substream->group;
        snd_pcm_group_lock_irq(group, nonatomic);
@@ -2374,10 +2318,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
        snd_pcm_group_unlock_irq(group, nonatomic);
        if (do_free)
                kfree(group);
-
-       _end:
-       up_write(&snd_pcm_link_rwsem);
-       return res;
+       return 0;
 }
 
 /*
@@ -2950,10 +2891,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
        /* block until the device gets woken up as it may touch the hardware */
        snd_power_wait(pcm->card);
 
-       mutex_lock(&pcm->open_mutex);
-       snd_pcm_release_substream(substream);
-       kfree(pcm_file);
-       mutex_unlock(&pcm->open_mutex);
+       scoped_guard(mutex, &pcm->open_mutex) {
+               snd_pcm_release_substream(substream);
+               kfree(pcm_file);
+       }
        wake_up(&pcm->open_wait);
        module_put(pcm->card->module);
        snd_card_file_remove(pcm->card, file);
@@ -3037,12 +2978,12 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
        if (frames == 0)
                return 0;
 
-       snd_pcm_stream_lock_irq(substream);
-       ret = do_pcm_hwsync(substream);
-       if (!ret)
-               ret = rewind_appl_ptr(substream, frames,
-                                     snd_pcm_hw_avail(substream));
-       snd_pcm_stream_unlock_irq(substream);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               ret = do_pcm_hwsync(substream);
+               if (!ret)
+                       ret = rewind_appl_ptr(substream, frames,
+                                             snd_pcm_hw_avail(substream));
+       }
        if (ret >= 0)
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        return ret;
@@ -3056,12 +2997,12 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
        if (frames == 0)
                return 0;
 
-       snd_pcm_stream_lock_irq(substream);
-       ret = do_pcm_hwsync(substream);
-       if (!ret)
-               ret = forward_appl_ptr(substream, frames,
-                                      snd_pcm_avail(substream));
-       snd_pcm_stream_unlock_irq(substream);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               ret = do_pcm_hwsync(substream);
+               if (!ret)
+                       ret = forward_appl_ptr(substream, frames,
+                                              snd_pcm_avail(substream));
+       }
        if (ret >= 0)
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        return ret;
@@ -3072,11 +3013,11 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
 {
        int err;
 
-       snd_pcm_stream_lock_irq(substream);
-       err = do_pcm_hwsync(substream);
-       if (delay && !err)
-               *delay = snd_pcm_calc_delay(substream);
-       snd_pcm_stream_unlock_irq(substream);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               err = do_pcm_hwsync(substream);
+               if (delay && !err)
+                       *delay = snd_pcm_calc_delay(substream);
+       }
        snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
 
        return err;
@@ -3108,27 +3049,25 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
                if (err < 0)
                        return err;
        }
-       snd_pcm_stream_lock_irq(substream);
-       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
-               err = pcm_lib_apply_appl_ptr(substream,
-                                            sync_ptr.c.control.appl_ptr);
-               if (err < 0) {
-                       snd_pcm_stream_unlock_irq(substream);
-                       return err;
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
+                       err = pcm_lib_apply_appl_ptr(substream,
+                                                    sync_ptr.c.control.appl_ptr);
+                       if (err < 0)
+                               return err;
+               } else {
+                       sync_ptr.c.control.appl_ptr = control->appl_ptr;
                }
-       } else {
-               sync_ptr.c.control.appl_ptr = control->appl_ptr;
+               if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+                       control->avail_min = sync_ptr.c.control.avail_min;
+               else
+                       sync_ptr.c.control.avail_min = control->avail_min;
+               sync_ptr.s.status.state = status->state;
+               sync_ptr.s.status.hw_ptr = status->hw_ptr;
+               sync_ptr.s.status.tstamp = status->tstamp;
+               sync_ptr.s.status.suspended_state = status->suspended_state;
+               sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
        }
-       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
-               control->avail_min = sync_ptr.c.control.avail_min;
-       else
-               sync_ptr.c.control.avail_min = control->avail_min;
-       sync_ptr.s.status.state = status->state;
-       sync_ptr.s.status.hw_ptr = status->hw_ptr;
-       sync_ptr.s.status.tstamp = status->tstamp;
-       sync_ptr.s.status.suspended_state = status->suspended_state;
-       sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
-       snd_pcm_stream_unlock_irq(substream);
        if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
@@ -3206,27 +3145,25 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
        boundary = recalculate_boundary(runtime);
        if (! boundary)
                boundary = 0x7fffffff;
-       snd_pcm_stream_lock_irq(substream);
-       /* FIXME: we should consider the boundary for the sync from app */
-       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
-               err = pcm_lib_apply_appl_ptr(substream,
-                               scontrol.appl_ptr);
-               if (err < 0) {
-                       snd_pcm_stream_unlock_irq(substream);
-                       return err;
-               }
-       } else
-               scontrol.appl_ptr = control->appl_ptr % boundary;
-       if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
-               control->avail_min = scontrol.avail_min;
-       else
-               scontrol.avail_min = control->avail_min;
-       sstatus.state = status->state;
-       sstatus.hw_ptr = status->hw_ptr % boundary;
-       sstatus.tstamp = status->tstamp;
-       sstatus.suspended_state = status->suspended_state;
-       sstatus.audio_tstamp = status->audio_tstamp;
-       snd_pcm_stream_unlock_irq(substream);
+       scoped_guard(pcm_stream_lock_irq, substream) {
+               /* FIXME: we should consider the boundary for the sync from app */
+               if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+                       err = pcm_lib_apply_appl_ptr(substream,
+                                                    scontrol.appl_ptr);
+                       if (err < 0)
+                               return err;
+               } else
+                       scontrol.appl_ptr = control->appl_ptr % boundary;
+               if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+                       control->avail_min = scontrol.avail_min;
+               else
+                       scontrol.avail_min = control->avail_min;
+               sstatus.state = status->state;
+               sstatus.hw_ptr = status->hw_ptr % boundary;
+               sstatus.tstamp = status->tstamp;
+               sstatus.suspended_state = status->suspended_state;
+               sstatus.audio_tstamp = status->audio_tstamp;
+       }
        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
                snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (put_user(sstatus.state, &src->s.status.state) ||
@@ -3284,7 +3221,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
 {
        struct snd_xfern xfern;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       void *bufs;
+       void *bufs __free(kfree) = NULL;
        snd_pcm_sframes_t result;
 
        if (runtime->state == SNDRV_PCM_STATE_OPEN)
@@ -3298,12 +3235,11 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
 
        bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels);
        if (IS_ERR(bufs))
-               return PTR_ERR(bufs);
+               return PTR_ERR(no_free_ptr(bufs));
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
        else
                result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
-       kfree(bufs);
        if (put_user(result, &_xfern->result))
                return -EFAULT;
        return result < 0 ? result : 0;
@@ -3571,7 +3507,7 @@ 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;
+       void __user **bufs __free(kfree) = NULL;
        snd_pcm_uframes_t frames;
        const struct iovec *iov = iter_iov(to);
 
@@ -3600,7 +3536,6 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
        result = snd_pcm_lib_readv(substream, bufs, frames);
        if (result > 0)
                result = frames_to_bytes(runtime, result);
-       kfree(bufs);
        return result;
 }
 
@@ -3611,7 +3546,7 @@ 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;
+       void __user **bufs __free(kfree) = NULL;
        snd_pcm_uframes_t frames;
        const struct iovec *iov = iter_iov(from);
 
@@ -3639,7 +3574,6 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
        result = snd_pcm_lib_writev(substream, bufs, frames);
        if (result > 0)
                result = frames_to_bytes(runtime, result);
-       kfree(bufs);
        return result;
 }
 
@@ -3668,7 +3602,7 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
        poll_wait(file, &runtime->sleep, wait);
 
        mask = 0;
-       snd_pcm_stream_lock_irq(substream);
+       guard(pcm_stream_lock_irq)(substream);
        avail = snd_pcm_avail(substream);
        switch (runtime->state) {
        case SNDRV_PCM_STATE_RUNNING:
@@ -3688,7 +3622,6 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
                mask = ok | EPOLLERR;
                break;
        }
-       snd_pcm_stream_unlock_irq(substream);
        return mask;
 }
 
@@ -4081,8 +4014,8 @@ 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;
-       struct snd_pcm_hw_params_old *oparams = NULL;
+       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);
@@ -4090,34 +4023,28 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
                return -ENOMEM;
 
        oparams = memdup_user(_oparams, sizeof(*oparams));
-       if (IS_ERR(oparams)) {
-               err = PTR_ERR(oparams);
-               goto out;
-       }
+       if (IS_ERR(oparams))
+               return PTR_ERR(no_free_ptr(oparams));
        snd_pcm_hw_convert_from_old_params(params, oparams);
        err = snd_pcm_hw_refine(substream, params);
        if (err < 0)
-               goto out_old;
+               return err;
 
        err = fixup_unreferenced_params(substream, params);
        if (err < 0)
-               goto out_old;
+               return err;
 
        snd_pcm_hw_convert_to_old_params(oparams, params);
        if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
-               err = -EFAULT;
-out_old:
-       kfree(oparams);
-out:
-       kfree(params);
-       return err;
+               return -EFAULT;
+       return 0;
 }
 
 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;
-       struct snd_pcm_hw_params_old *oparams = NULL;
+       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);
@@ -4125,24 +4052,18 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
                return -ENOMEM;
 
        oparams = memdup_user(_oparams, sizeof(*oparams));
-       if (IS_ERR(oparams)) {
-               err = PTR_ERR(oparams);
-               goto out;
-       }
+       if (IS_ERR(oparams))
+               return PTR_ERR(no_free_ptr(oparams));
 
        snd_pcm_hw_convert_from_old_params(params, oparams);
        err = snd_pcm_hw_params(substream, params);
        if (err < 0)
-               goto out_old;
+               return err;
 
        snd_pcm_hw_convert_to_old_params(oparams, params);
        if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
-               err = -EFAULT;
-out_old:
-       kfree(oparams);
-out:
-       kfree(params);
-       return err;
+               return -EFAULT;
+       return 0;
 }
 #endif /* CONFIG_SND_SUPPORT_OLD_API */
 
index 1431cb997808d071d44efadba2b8a1d49117ee25..7accf9a1ddf4c625255fd47794d6ff1d5cbf068c 100644 (file)
@@ -105,13 +105,8 @@ static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
 
 static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
 {
-       unsigned long flags;
-       bool ready;
-
-       spin_lock_irqsave(&substream->lock, flags);
-       ready = __snd_rawmidi_ready(substream->runtime);
-       spin_unlock_irqrestore(&substream->lock, flags);
-       return ready;
+       guard(spinlock_irqsave)(&substream->lock);
+       return __snd_rawmidi_ready(substream->runtime);
 }
 
 static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
@@ -238,12 +233,9 @@ static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
 static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
                               bool is_input)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        if (substream->opened && substream->runtime)
                __reset_runtime_ptrs(substream->runtime, is_input);
-       spin_unlock_irqrestore(&substream->lock, flags);
 }
 
 int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
@@ -260,33 +252,29 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
        long timeout;
        struct snd_rawmidi_runtime *runtime;
 
-       spin_lock_irq(&substream->lock);
-       runtime = substream->runtime;
-       if (!substream->opened || !runtime || !runtime->buffer) {
-               err = -EINVAL;
-       } else {
+       scoped_guard(spinlock_irq, &substream->lock) {
+               runtime = substream->runtime;
+               if (!substream->opened || !runtime || !runtime->buffer)
+                       return -EINVAL;
                snd_rawmidi_buffer_ref(runtime);
                runtime->drain = 1;
        }
-       spin_unlock_irq(&substream->lock);
-       if (err < 0)
-               return err;
 
        timeout = wait_event_interruptible_timeout(runtime->sleep,
                                (runtime->avail >= runtime->buffer_size),
                                10*HZ);
 
-       spin_lock_irq(&substream->lock);
-       if (signal_pending(current))
-               err = -ERESTARTSYS;
-       if (runtime->avail < runtime->buffer_size && !timeout) {
-               rmidi_warn(substream->rmidi,
-                          "rawmidi drain error (avail = %li, buffer_size = %li)\n",
-                          (long)runtime->avail, (long)runtime->buffer_size);
-               err = -EIO;
+       scoped_guard(spinlock_irq, &substream->lock) {
+               if (signal_pending(current))
+                       err = -ERESTARTSYS;
+               if (runtime->avail < runtime->buffer_size && !timeout) {
+                       rmidi_warn(substream->rmidi,
+                                  "rawmidi drain error (avail = %li, buffer_size = %li)\n",
+                                  (long)runtime->avail, (long)runtime->buffer_size);
+                       err = -EIO;
+               }
+               runtime->drain = 0;
        }
-       runtime->drain = 0;
-       spin_unlock_irq(&substream->lock);
 
        if (err != -ERESTARTSYS) {
                /* we need wait a while to make sure that Tx FIFOs are empty */
@@ -297,9 +285,8 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
                snd_rawmidi_drop_output(substream);
        }
 
-       spin_lock_irq(&substream->lock);
-       snd_rawmidi_buffer_unref(runtime);
-       spin_unlock_irq(&substream->lock);
+       scoped_guard(spinlock_irq, &substream->lock)
+               snd_rawmidi_buffer_unref(runtime);
 
        return err;
 }
@@ -363,14 +350,13 @@ static int open_substream(struct snd_rawmidi *rmidi,
                        snd_rawmidi_runtime_free(substream);
                        return err;
                }
-               spin_lock_irq(&substream->lock);
+               guard(spinlock_irq)(&substream->lock);
                substream->opened = 1;
                substream->active_sensing = 0;
                if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
                        substream->append = 1;
                substream->pid = get_pid(task_pid(current));
                rmidi->streams[substream->stream].substream_opened++;
-               spin_unlock_irq(&substream->lock);
        }
        substream->use_count++;
        return 0;
@@ -433,9 +419,8 @@ int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
        if (!try_module_get(rmidi->card->module))
                return -ENXIO;
 
-       mutex_lock(&rmidi->open_mutex);
+       guard(mutex)(&rmidi->open_mutex);
        err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
-       mutex_unlock(&rmidi->open_mutex);
        if (err < 0)
                module_put(rmidi->card->module);
        return err;
@@ -568,10 +553,10 @@ static void close_substream(struct snd_rawmidi *rmidi,
                }
                snd_rawmidi_buffer_ref_sync(substream);
        }
-       spin_lock_irq(&substream->lock);
-       substream->opened = 0;
-       substream->append = 0;
-       spin_unlock_irq(&substream->lock);
+       scoped_guard(spinlock_irq, &substream->lock) {
+               substream->opened = 0;
+               substream->append = 0;
+       }
        substream->ops->close(substream);
        if (substream->runtime->private_free)
                substream->runtime->private_free(substream);
@@ -586,7 +571,7 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
        struct snd_rawmidi *rmidi;
 
        rmidi = rfile->rmidi;
-       mutex_lock(&rmidi->open_mutex);
+       guard(mutex)(&rmidi->open_mutex);
        if (rfile->input) {
                close_substream(rmidi, rfile->input, 1);
                rfile->input = NULL;
@@ -596,7 +581,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
                rfile->output = NULL;
        }
        rfile->rmidi = NULL;
-       mutex_unlock(&rmidi->open_mutex);
        wake_up(&rmidi->open_wait);
 }
 
@@ -695,12 +679,8 @@ static int __snd_rawmidi_info_select(struct snd_card *card,
 
 int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
 {
-       int ret;
-
-       mutex_lock(&register_mutex);
-       ret = __snd_rawmidi_info_select(card, info);
-       mutex_unlock(&register_mutex);
-       return ret;
+       guard(mutex)(&register_mutex);
+       return __snd_rawmidi_info_select(card, info);
 }
 EXPORT_SYMBOL(snd_rawmidi_info_select);
 
@@ -744,9 +724,8 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
                newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
                if (!newbuf)
                        return -ENOMEM;
-               spin_lock_irq(&substream->lock);
+               guard(spinlock_irq)(&substream->lock);
                if (runtime->buffer_ref) {
-                       spin_unlock_irq(&substream->lock);
                        kvfree(newbuf);
                        return -EBUSY;
                }
@@ -754,7 +733,6 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
                runtime->buffer = newbuf;
                runtime->buffer_size = params->buffer_size;
                __reset_runtime_ptrs(runtime, is_input);
-               spin_unlock_irq(&substream->lock);
                kvfree(oldbuf);
        }
        runtime->avail_min = params->avail_min;
@@ -767,15 +745,12 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
        int err;
 
        snd_rawmidi_drain_output(substream);
-       mutex_lock(&substream->rmidi->open_mutex);
+       guard(mutex)(&substream->rmidi->open_mutex);
        if (substream->append && substream->use_count > 1)
-               err = -EBUSY;
-       else
-               err = resize_runtime_buffer(substream, params, false);
-
+               return -EBUSY;
+       err = resize_runtime_buffer(substream, params, false);
        if (!err)
                substream->active_sensing = !params->no_active_sensing;
-       mutex_unlock(&substream->rmidi->open_mutex);
        return err;
 }
 EXPORT_SYMBOL(snd_rawmidi_output_params);
@@ -788,7 +763,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
        int err;
 
        snd_rawmidi_drain_input(substream);
-       mutex_lock(&substream->rmidi->open_mutex);
+       guard(mutex)(&substream->rmidi->open_mutex);
        if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
                err = -EINVAL;
        else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
@@ -802,7 +777,6 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
                substream->framing = framing;
                substream->clock_type = clock_type;
        }
-       mutex_unlock(&substream->rmidi->open_mutex);
        return 0;
 }
 EXPORT_SYMBOL(snd_rawmidi_input_params);
@@ -814,9 +788,8 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
 
        memset(status, 0, sizeof(*status));
        status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
-       spin_lock_irq(&substream->lock);
+       guard(spinlock_irq)(&substream->lock);
        status->avail = runtime->avail;
-       spin_unlock_irq(&substream->lock);
        return 0;
 }
 
@@ -827,11 +800,10 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
 
        memset(status, 0, sizeof(*status));
        status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
-       spin_lock_irq(&substream->lock);
+       guard(spinlock_irq)(&substream->lock);
        status->avail = runtime->avail;
        status->xruns = runtime->xruns;
        runtime->xruns = 0;
-       spin_unlock_irq(&substream->lock);
        return 0;
 }
 
@@ -1025,19 +997,19 @@ static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
                return -EFAULT;
        if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
                device = SNDRV_RAWMIDI_DEVICES - 1;
-       mutex_lock(&register_mutex);
-       device = device < 0 ? 0 : device + 1;
-       for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
-               rmidi = snd_rawmidi_search(card, device);
-               if (!rmidi)
-                       continue;
-               is_ump = rawmidi_is_ump(rmidi);
-               if (find_ump == is_ump)
-                       break;
+       scoped_guard(mutex, &register_mutex) {
+               device = device < 0 ? 0 : device + 1;
+               for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
+                       rmidi = snd_rawmidi_search(card, device);
+                       if (!rmidi)
+                               continue;
+                       is_ump = rawmidi_is_ump(rmidi);
+                       if (find_ump == is_ump)
+                               break;
+               }
+               if (device == SNDRV_RAWMIDI_DEVICES)
+                       device = -1;
        }
-       if (device == SNDRV_RAWMIDI_DEVICES)
-               device = -1;
-       mutex_unlock(&register_mutex);
        if (put_user(device, argp))
                return -EFAULT;
        return 0;
@@ -1050,18 +1022,16 @@ static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
 {
        struct snd_ump_endpoint_info __user *info = argp;
        struct snd_rawmidi *rmidi;
-       int device, ret;
+       int device;
 
        if (get_user(device, &info->device))
                return -EFAULT;
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        rmidi = snd_rawmidi_search(card, device);
        if (rmidi && rmidi->ops && rmidi->ops->ioctl)
-               ret = rmidi->ops->ioctl(rmidi, cmd, argp);
+               return rmidi->ops->ioctl(rmidi, cmd, argp);
        else
-               ret = -ENXIO;
-       mutex_unlock(&register_mutex);
-       return ret;
+               return -ENXIO;
 }
 #endif
 
@@ -1168,27 +1138,23 @@ static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substr
 int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
                        const unsigned char *buffer, int count)
 {
-       unsigned long flags;
        struct timespec64 ts64 = get_framing_tstamp(substream);
        int result = 0, count1;
        struct snd_rawmidi_runtime *runtime;
 
-       spin_lock_irqsave(&substream->lock, flags);
-       if (!substream->opened) {
-               result = -EBADFD;
-               goto unlock;
-       }
+       guard(spinlock_irqsave)(&substream->lock);
+       if (!substream->opened)
+               return -EBADFD;
        runtime = substream->runtime;
        if (!runtime || !runtime->buffer) {
                rmidi_dbg(substream->rmidi,
                          "snd_rawmidi_receive: input is not active!!!\n");
-               result = -EINVAL;
-               goto unlock;
+               return -EINVAL;
        }
 
        count = get_aligned_size(runtime, count);
        if (!count)
-               goto unlock;
+               return result;
 
        if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
                result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
@@ -1211,7 +1177,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
                        count1 = runtime->buffer_size - runtime->avail;
                count1 = get_aligned_size(runtime, count1);
                if (!count1)
-                       goto unlock;
+                       return result;
                memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
                runtime->hw_ptr += count1;
                runtime->hw_ptr %= runtime->buffer_size;
@@ -1239,8 +1205,6 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
                else if (__snd_rawmidi_ready(runtime))
                        wake_up(&runtime->sleep);
        }
- unlock:
-       spin_unlock_irqrestore(&substream->lock, flags);
        return result;
 }
 EXPORT_SYMBOL(snd_rawmidi_receive);
@@ -1362,20 +1326,15 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
 int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
 {
        struct snd_rawmidi_runtime *runtime;
-       int result;
-       unsigned long flags;
 
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        runtime = substream->runtime;
        if (!substream->opened || !runtime || !runtime->buffer) {
                rmidi_dbg(substream->rmidi,
                          "snd_rawmidi_transmit_empty: output is not active!!!\n");
-               result = 1;
-       } else {
-               result = runtime->avail >= runtime->buffer_size;
+               return 1;
        }
-       spin_unlock_irqrestore(&substream->lock, flags);
-       return result;
+       return (runtime->avail >= runtime->buffer_size);
 }
 EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
 
@@ -1449,16 +1408,10 @@ static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
 int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
                              unsigned char *buffer, int count)
 {
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        if (!substream->opened || !substream->runtime)
-               result = -EBADFD;
-       else
-               result = __snd_rawmidi_transmit_peek(substream, buffer, count);
-       spin_unlock_irqrestore(&substream->lock, flags);
-       return result;
+               return -EBADFD;
+       return __snd_rawmidi_transmit_peek(substream, buffer, count);
 }
 EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
 
@@ -1505,16 +1458,10 @@ static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
  */
 int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
 {
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        if (!substream->opened || !substream->runtime)
-               result = -EBADFD;
-       else
-               result = __snd_rawmidi_transmit_ack(substream, count);
-       spin_unlock_irqrestore(&substream->lock, flags);
-       return result;
+               return -EBADFD;
+       return __snd_rawmidi_transmit_ack(substream, count);
 }
 EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
 
@@ -1531,21 +1478,13 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
 int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
                         unsigned char *buffer, int count)
 {
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        if (!substream->opened)
-               result = -EBADFD;
-       else {
-               count = __snd_rawmidi_transmit_peek(substream, buffer, count);
-               if (count <= 0)
-                       result = count;
-               else
-                       result = __snd_rawmidi_transmit_ack(substream, count);
-       }
-       spin_unlock_irqrestore(&substream->lock, flags);
-       return result;
+               return -EBADFD;
+       count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+       if (count <= 0)
+               return count;
+       return __snd_rawmidi_transmit_ack(substream, count);
 }
 EXPORT_SYMBOL(snd_rawmidi_transmit);
 
@@ -1558,17 +1497,15 @@ EXPORT_SYMBOL(snd_rawmidi_transmit);
 int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
 {
        struct snd_rawmidi_runtime *runtime;
-       unsigned long flags;
        int count = 0;
 
-       spin_lock_irqsave(&substream->lock, flags);
+       guard(spinlock_irqsave)(&substream->lock);
        runtime = substream->runtime;
        if (substream->opened && runtime &&
            runtime->avail < runtime->buffer_size) {
                count = runtime->buffer_size - runtime->avail;
                __snd_rawmidi_transmit_ack(substream, count);
        }
-       spin_unlock_irqrestore(&substream->lock, flags);
        return count;
 }
 EXPORT_SYMBOL(snd_rawmidi_proceed);
@@ -1772,7 +1709,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                            rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
        if (rmidi->ops && rmidi->ops->proc_read)
                rmidi->ops->proc_read(entry, buffer);
-       mutex_lock(&rmidi->open_mutex);
+       guard(mutex)(&rmidi->open_mutex);
        if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
                list_for_each_entry(substream,
                                    &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
@@ -1787,10 +1724,10 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                                    "  Owner PID    : %d\n",
                                    pid_vnr(substream->pid));
                                runtime = substream->runtime;
-                               spin_lock_irq(&substream->lock);
-                               buffer_size = runtime->buffer_size;
-                               avail = runtime->avail;
-                               spin_unlock_irq(&substream->lock);
+                               scoped_guard(spinlock_irq, &substream->lock) {
+                                       buffer_size = runtime->buffer_size;
+                                       avail = runtime->avail;
+                               }
                                snd_iprintf(buffer,
                                    "  Mode         : %s\n"
                                    "  Buffer size  : %lu\n"
@@ -1814,11 +1751,11 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                                            "  Owner PID    : %d\n",
                                            pid_vnr(substream->pid));
                                runtime = substream->runtime;
-                               spin_lock_irq(&substream->lock);
-                               buffer_size = runtime->buffer_size;
-                               avail = runtime->avail;
-                               xruns = runtime->xruns;
-                               spin_unlock_irq(&substream->lock);
+                               scoped_guard(spinlock_irq, &substream->lock) {
+                                       buffer_size = runtime->buffer_size;
+                                       avail = runtime->avail;
+                                       xruns = runtime->xruns;
+                               }
                                snd_iprintf(buffer,
                                            "  Buffer size  : %lu\n"
                                            "  Avail        : %lu\n"
@@ -1835,7 +1772,6 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                        }
                }
        }
-       mutex_unlock(&rmidi->open_mutex);
 }
 
 /*
@@ -2024,12 +1960,12 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
        if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
                return -ENOMEM;
        err = 0;
-       mutex_lock(&register_mutex);
-       if (snd_rawmidi_search(rmidi->card, rmidi->device))
-               err = -EBUSY;
-       else
-               list_add_tail(&rmidi->list, &snd_rawmidi_devices);
-       mutex_unlock(&register_mutex);
+       scoped_guard(mutex, &register_mutex) {
+               if (snd_rawmidi_search(rmidi->card, rmidi->device))
+                       err = -EBUSY;
+               else
+                       list_add_tail(&rmidi->list, &snd_rawmidi_devices);
+       }
        if (err < 0)
                return err;
 
@@ -2102,9 +2038,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
  error_unregister:
        snd_unregister_device(rmidi->dev);
  error:
-       mutex_lock(&register_mutex);
-       list_del(&rmidi->list);
-       mutex_unlock(&register_mutex);
+       scoped_guard(mutex, &register_mutex)
+               list_del(&rmidi->list);
        return err;
 }
 
@@ -2113,8 +2048,8 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
        struct snd_rawmidi *rmidi = device->device_data;
        int dir;
 
-       mutex_lock(&register_mutex);
-       mutex_lock(&rmidi->open_mutex);
+       guard(mutex)(&register_mutex);
+       guard(mutex)(&rmidi->open_mutex);
        wake_up(&rmidi->open_wait);
        list_del_init(&rmidi->list);
        for (dir = 0; dir < 2; dir++) {
@@ -2140,8 +2075,6 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
        }
 #endif /* CONFIG_SND_OSSEMUL */
        snd_unregister_device(rmidi->dev);
-       mutex_unlock(&rmidi->open_mutex);
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
index c14981daf9432f7218ab1cc03943c9ec9b6536d9..0374bbf51cd4d38af3564d25d82fe27d4ad5a08a 100644 (file)
@@ -71,7 +71,6 @@ config SND_SEQ_UMP
          among legacy and UMP clients.
 
 config SND_SEQ_UMP_CLIENT
-       tristate
        def_tristate SND_UMP
 
 endif # SND_SEQUENCER
index 6c2c4fb9b753786784a967efe926c09a4f603d20..f0e964b19af7aea9122a8df915f6195644111552 100644 (file)
@@ -163,6 +163,6 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
 
 
 /* misc. functions for proc interface */
-char *enabled_str(int bool);
+char *enabled_str(bool b);
 
 #endif /* __SEQ_OSS_DEVICE_H */
index 42d4e7535a820456b1f4f92fa4a12c81fa07e60d..676246fa02f15edc8fa0e5bdaaf827cc1cd6810f 100644 (file)
@@ -63,20 +63,18 @@ int __init
 snd_seq_oss_create_client(void)
 {
        int rc;
-       struct snd_seq_port_info *port;
+       struct snd_seq_port_info *port __free(kfree) = NULL;
        struct snd_seq_port_callback port_callback;
 
        port = kzalloc(sizeof(*port), GFP_KERNEL);
-       if (!port) {
-               rc = -ENOMEM;
-               goto __error;
-       }
+       if (!port)
+               return -ENOMEM;
 
        /* create ALSA client */
        rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
                                          "OSS sequencer");
        if (rc < 0)
-               goto __error;
+               return rc;
 
        system_client = rc;
 
@@ -104,14 +102,11 @@ snd_seq_oss_create_client(void)
                subs.dest.port = system_port;
                call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
        }
-       rc = 0;
 
        /* look up midi devices */
        schedule_work(&async_lookup_work);
 
- __error:
-       kfree(port);
-       return rc;
+       return 0;
 }
 
 
@@ -455,9 +450,9 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
  * misc. functions for proc interface
  */
 char *
-enabled_str(int bool)
+enabled_str(bool b)
 {
-       return bool ? "enabled" : "disabled";
+       return b ? "enabled" : "disabled";
 }
 
 static const char *
index f2940b29595f0921ad97e94f090e33561ac6d3cd..f8e247d9e5c99fd1773612c53d3344fbe71e0830 100644 (file)
@@ -64,16 +64,13 @@ 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;
-       struct snd_seq_port_info *pinfo;
+       struct snd_seq_client_info *clinfo __free(kfree) = NULL;
+       struct snd_seq_port_info *pinfo __free(kfree) = NULL;
 
        clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);
        pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
-       if (! clinfo || ! pinfo) {
-               kfree(clinfo);
-               kfree(pinfo);
+       if (!clinfo || !pinfo)
                return -ENOMEM;
-       }
        clinfo->client = -1;
        while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
                if (clinfo->client == client)
@@ -83,8 +80,6 @@ snd_seq_oss_midi_lookup_ports(int client)
                while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
                        snd_seq_oss_midi_check_new_port(pinfo);
        }
-       kfree(clinfo);
-       kfree(pinfo);
        return 0;
 }
 
index 1e35bf086a51da5a41884fb3fc2c3bf7b146b955..643af4c1e838669a2fe45c9ecfb4072d166c917b 100644 (file)
@@ -31,8 +31,8 @@ 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)
 {
-       int err = -EFAULT;
-       struct snd_seq_port_info *data;
+       struct snd_seq_port_info *data __free(kfree) = NULL;
+       int err;
 
        data = kmalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -41,20 +41,18 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
        if (copy_from_user(data, data32, sizeof(*data32)) ||
            get_user(data->flags, &data32->flags) ||
            get_user(data->time_queue, &data32->time_queue))
-               goto error;
+               return -EFAULT;
        data->kernel = NULL;
 
        err = snd_seq_kernel_client_ctl(client->number, cmd, data);
        if (err < 0)
-               goto error;
+               return err;
 
        if (copy_to_user(data32, data, sizeof(*data32)) ||
            put_user(data->flags, &data32->flags) ||
            put_user(data->time_queue, &data32->time_queue))
-               err = -EFAULT;
+               return -EFAULT;
 
- error:
-       kfree(data);
        return err;
 }
 
index f8e02e98709abe12a5ff6336958cc6e16f9f10ca..3a10b081f129c30d677a9e834a7477a065fd8044 100644 (file)
@@ -88,12 +88,11 @@ void snd_seq_fifo_clear(struct snd_seq_fifo *f)
        atomic_set(&f->overflow, 0);
 
        snd_use_lock_sync(&f->use_lock);
-       spin_lock_irq(&f->lock);
+       guard(spinlock_irq)(&f->lock);
        /* drain the fifo */
        while ((cell = fifo_cell_out(f)) != NULL) {
                snd_seq_cell_free(cell);
        }
-       spin_unlock_irq(&f->lock);
 }
 
 
@@ -102,7 +101,6 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
                          struct snd_seq_event *event)
 {
        struct snd_seq_event_cell *cell;
-       unsigned long flags;
        int err;
 
        if (snd_BUG_ON(!f))
@@ -118,15 +116,15 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
        }
                
        /* append new cells to fifo */
-       spin_lock_irqsave(&f->lock, flags);
-       if (f->tail != NULL)
-               f->tail->next = cell;
-       f->tail = cell;
-       if (f->head == NULL)
-               f->head = cell;
-       cell->next = NULL;
-       f->cells++;
-       spin_unlock_irqrestore(&f->lock, flags);
+       scoped_guard(spinlock_irqsave, &f->lock) {
+               if (f->tail != NULL)
+                       f->tail->next = cell;
+               f->tail = cell;
+               if (f->head == NULL)
+                       f->head = cell;
+               cell->next = NULL;
+               f->cells++;
+       }
 
        /* wakeup client */
        if (waitqueue_active(&f->input_sleep))
@@ -199,16 +197,13 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
 void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
                               struct snd_seq_event_cell *cell)
 {
-       unsigned long flags;
-
        if (cell) {
-               spin_lock_irqsave(&f->lock, flags);
+               guard(spinlock_irqsave)(&f->lock);
                cell->next = f->head;
                f->head = cell;
                if (!f->tail)
                        f->tail = cell;
                f->cells++;
-               spin_unlock_irqrestore(&f->lock, flags);
        }
 }
 
@@ -239,17 +234,17 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
                return -ENOMEM;
        }
 
-       spin_lock_irq(&f->lock);
-       /* remember old pool */
-       oldpool = f->pool;
-       oldhead = f->head;
-       /* exchange pools */
-       f->pool = newpool;
-       f->head = NULL;
-       f->tail = NULL;
-       f->cells = 0;
-       /* NOTE: overflow flag is not cleared */
-       spin_unlock_irq(&f->lock);
+       scoped_guard(spinlock_irq, &f->lock) {
+               /* remember old pool */
+               oldpool = f->pool;
+               oldhead = f->head;
+               /* exchange pools */
+               f->pool = newpool;
+               f->head = NULL;
+               f->tail = NULL;
+               f->cells = 0;
+               /* NOTE: overflow flag is not cleared */
+       }
 
        /* close the old pool and wait until all users are gone */
        snd_seq_pool_mark_closing(oldpool);
@@ -268,16 +263,14 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
 /* get the number of unused cells safely */
 int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f)
 {
-       unsigned long flags;
        int cells;
 
        if (!f)
                return 0;
 
        snd_use_lock_use(&f->use_lock);
-       spin_lock_irqsave(&f->lock, flags);
-       cells = snd_seq_unused_cells(f->pool);
-       spin_unlock_irqrestore(&f->lock, flags);
+       scoped_guard(spinlock_irqsave, &f->lock)
+               cells = snd_seq_unused_cells(f->pool);
        snd_use_lock_free(&f->use_lock);
        return cells;
 }
index e705e753811895f2aa4ff45e9bac9c3711bc8ac0..20155e3e87c6ac1d51c2598e8ce979ebe9499e99 100644 (file)
@@ -232,7 +232,6 @@ static inline void free_cell(struct snd_seq_pool *pool,
 
 void snd_seq_cell_free(struct snd_seq_event_cell * cell)
 {
-       unsigned long flags;
        struct snd_seq_pool *pool;
 
        if (snd_BUG_ON(!cell))
@@ -241,7 +240,7 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
        if (snd_BUG_ON(!pool))
                return;
 
-       spin_lock_irqsave(&pool->lock, flags);
+       guard(spinlock_irqsave)(&pool->lock);
        free_cell(pool, cell);
        if (snd_seq_ev_is_variable(&cell->event)) {
                if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
@@ -259,7 +258,6 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
                if (snd_seq_output_ok(pool))
                        wake_up(&pool->output_sleep);
        }
-       spin_unlock_irqrestore(&pool->lock, flags);
 }
 
 
@@ -449,9 +447,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
                return -ENOMEM;
 
        /* add new cells to the free cell list */
-       spin_lock_irq(&pool->lock);
+       guard(spinlock_irq)(&pool->lock);
        if (pool->ptr) {
-               spin_unlock_irq(&pool->lock);
                kvfree(cellptr);
                return 0;
        }
@@ -470,20 +467,16 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
        /* init statistics */
        pool->max_used = 0;
        pool->total_elements = pool->size;
-       spin_unlock_irq(&pool->lock);
        return 0;
 }
 
 /* refuse the further insertion to the pool */
 void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!pool))
                return;
-       spin_lock_irqsave(&pool->lock, flags);
+       guard(spinlock_irqsave)(&pool->lock);
        pool->closing = 1;
-       spin_unlock_irqrestore(&pool->lock, flags);
 }
 
 /* remove events */
@@ -502,18 +495,17 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
                schedule_timeout_uninterruptible(1);
        
        /* release all resources */
-       spin_lock_irq(&pool->lock);
-       ptr = pool->ptr;
-       pool->ptr = NULL;
-       pool->free = NULL;
-       pool->total_elements = 0;
-       spin_unlock_irq(&pool->lock);
+       scoped_guard(spinlock_irq, &pool->lock) {
+               ptr = pool->ptr;
+               pool->ptr = NULL;
+               pool->free = NULL;
+               pool->total_elements = 0;
+       }
 
        kvfree(ptr);
 
-       spin_lock_irq(&pool->lock);
+       guard(spinlock_irq)(&pool->lock);
        pool->closing = 0;
-       spin_unlock_irq(&pool->lock);
 
        return 0;
 }
index 18320a248aa7daa080df9499668c400f4da22066..ba52a77eda3828a44e1ab9da53c78d4e968b5ab7 100644 (file)
@@ -113,6 +113,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
        return 0;
 }
 
+/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */
+static int __dump_midi(void *ptr, void *buf, int count)
+{
+       return dump_midi(ptr, buf, count);
+}
+
 static int event_process_midi(struct snd_seq_event *ev, int direct,
                              void *private_data, int atomic, int hop)
 {
@@ -132,7 +138,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
                        pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
                        return 0;
                }
-               snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+               snd_seq_dump_var_event(ev, __dump_midi, substream);
                snd_midi_event_reset_decode(msynth->parser);
        } else {
                if (msynth->parser == NULL)
@@ -264,8 +270,8 @@ snd_seq_midisynth_probe(struct 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;
-       struct snd_rawmidi_info *info;
+       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;
@@ -291,31 +297,24 @@ snd_seq_midisynth_probe(struct device *_dev)
        ports = output_count;
        if (ports < input_count)
                ports = input_count;
-       if (ports == 0) {
-               kfree(info);
+       if (ports == 0)
                return -ENODEV;
-       }
        if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
                ports = 256 / SNDRV_RAWMIDI_DEVICES;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        client = synths[card->number];
        if (client == NULL) {
                newclient = 1;
                client = kzalloc(sizeof(*client), GFP_KERNEL);
-               if (client == NULL) {
-                       mutex_unlock(&register_mutex);
-                       kfree(info);
+               if (client == NULL)
                        return -ENOMEM;
-               }
                client->seq_client =
                        snd_seq_create_kernel_client(
                                card, 0, "%s", card->shortname[0] ?
                                (const char *)card->shortname : "External MIDI");
                if (client->seq_client < 0) {
                        kfree(client);
-                       mutex_unlock(&register_mutex);
-                       kfree(info);
                        return -ENOMEM;
                }
        }
@@ -396,9 +395,6 @@ snd_seq_midisynth_probe(struct device *_dev)
        client->num_ports++;
        if (newclient)
                synths[card->number] = client;
-       mutex_unlock(&register_mutex);
-       kfree(info);
-       kfree(port);
        return 0;       /* success */
 
       __nomem:
@@ -411,9 +407,6 @@ snd_seq_midisynth_probe(struct device *_dev)
                snd_seq_delete_kernel_client(client->seq_client);
                kfree(client);
        }
-       kfree(info);
-       kfree(port);
-       mutex_unlock(&register_mutex);
        return -ENOMEM;
 }
 
@@ -427,12 +420,10 @@ snd_seq_midisynth_remove(struct device *_dev)
        struct snd_card *card = dev->card;
        int device = dev->device, p, ports;
        
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        client = synths[card->number];
-       if (client == NULL || client->ports[device] == NULL) {
-               mutex_unlock(&register_mutex);
+       if (client == NULL || client->ports[device] == NULL)
                return -ENODEV;
-       }
        ports = client->ports_per_device[device];
        client->ports_per_device[device] = 0;
        msynth = client->ports[device];
@@ -446,7 +437,6 @@ snd_seq_midisynth_remove(struct device *_dev)
                synths[card->number] = NULL;
                kfree(client);
        }
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
index 7511462fe0718604266381acd69d1e4ba1e2d987..fa9dfc53c3fcc65c07cfde3a2a2276aeda08d1a4 100644 (file)
@@ -144,21 +144,15 @@ static inline void reset_encode(struct snd_midi_event *dev)
 
 void snd_midi_event_reset_encode(struct snd_midi_event *dev)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->lock, flags);
+       guard(spinlock_irqsave)(&dev->lock);
        reset_encode(dev);
-       spin_unlock_irqrestore(&dev->lock, flags);
 }
 EXPORT_SYMBOL(snd_midi_event_reset_encode);
 
 void snd_midi_event_reset_decode(struct snd_midi_event *dev)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->lock, flags);
+       guard(spinlock_irqsave)(&dev->lock);
        dev->lastcmd = 0xff;
-       spin_unlock_irqrestore(&dev->lock, flags);
 }
 EXPORT_SYMBOL(snd_midi_event_reset_decode);
 
@@ -177,7 +171,6 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c,
                                struct snd_seq_event *ev)
 {
        bool rc = false;
-       unsigned long flags;
 
        if (c >= MIDI_CMD_COMMON_CLOCK) {
                /* real-time event */
@@ -187,7 +180,7 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c,
                return ev->type != SNDRV_SEQ_EVENT_NONE;
        }
 
-       spin_lock_irqsave(&dev->lock, flags);
+       guard(spinlock_irqsave)(&dev->lock);
        if ((c & 0x80) &&
            (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
                /* new command */
@@ -236,7 +229,6 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c,
                }
        }
 
-       spin_unlock_irqrestore(&dev->lock, flags);
        return rc;
 }
 EXPORT_SYMBOL(snd_midi_event_encode_byte);
index f3f14ff0f80f0c533e7f9df37929d6d88cb21470..ca631ca4f2c64ebf46292b99a2c6c48128e293cf 100644 (file)
@@ -48,17 +48,15 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
 
        if (client == NULL)
                return NULL;
-       read_lock(&client->ports_lock);
+       guard(read_lock)(&client->ports_lock);
        list_for_each_entry(port, &client->ports_list_head, list) {
                if (port->addr.port == num) {
                        if (port->closing)
                                break; /* deleting now */
                        snd_use_lock_use(&port->use_lock);
-                       read_unlock(&client->ports_lock);
                        return port;
                }
        }
-       read_unlock(&client->ports_lock);
        return NULL;            /* not found */
 }
 
@@ -73,7 +71,7 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
 
        num = pinfo->addr.port;
        found = NULL;
-       read_lock(&client->ports_lock);
+       guard(read_lock)(&client->ports_lock);
        list_for_each_entry(port, &client->ports_list_head, list) {
                if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) &&
                    !check_inactive)
@@ -93,7 +91,6 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
                else
                        snd_use_lock_use(&found->use_lock);
        }
-       read_unlock(&client->ports_lock);
        return found;
 }
 
@@ -145,13 +142,12 @@ int snd_seq_create_port(struct snd_seq_client *client, int port,
        snd_use_lock_use(&new_port->use_lock);
 
        num = max(port, 0);
-       mutex_lock(&client->ports_mutex);
-       write_lock_irq(&client->ports_lock);
+       guard(mutex)(&client->ports_mutex);
+       guard(write_lock_irq)(&client->ports_lock);
        list_for_each_entry(p, &client->ports_list_head, list) {
                if (p->addr.port == port) {
                        kfree(new_port);
-                       num = -EBUSY;
-                       goto unlock;
+                       return -EBUSY;
                }
                if (p->addr.port > num)
                        break;
@@ -164,9 +160,6 @@ int snd_seq_create_port(struct snd_seq_client *client, int port,
        new_port->addr.port = num;      /* store the port number in the port */
        sprintf(new_port->name, "port-%d", num);
        *port_ret = new_port;
- unlock:
-       write_unlock_irq(&client->ports_lock);
-       mutex_unlock(&client->ports_mutex);
 
        return num;
 }
@@ -281,19 +274,18 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
 {
        struct snd_seq_client_port *found = NULL, *p;
 
-       mutex_lock(&client->ports_mutex);
-       write_lock_irq(&client->ports_lock);
-       list_for_each_entry(p, &client->ports_list_head, list) {
-               if (p->addr.port == port) {
-                       /* ok found.  delete from the list at first */
-                       list_del(&p->list);
-                       client->num_ports--;
-                       found = p;
-                       break;
+       scoped_guard(mutex, &client->ports_mutex) {
+               guard(write_lock_irq)(&client->ports_lock);
+               list_for_each_entry(p, &client->ports_list_head, list) {
+                       if (p->addr.port == port) {
+                               /* ok found.  delete from the list at first */
+                               list_del(&p->list);
+                               client->num_ports--;
+                               found = p;
+                               break;
+                       }
                }
        }
-       write_unlock_irq(&client->ports_lock);
-       mutex_unlock(&client->ports_mutex);
        if (found)
                return port_delete(client, found);
        else
@@ -309,16 +301,16 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
        /* move the port list to deleted_list, and
         * clear the port list in the client data.
         */
-       mutex_lock(&client->ports_mutex);
-       write_lock_irq(&client->ports_lock);
-       if (! list_empty(&client->ports_list_head)) {
-               list_add(&deleted_list, &client->ports_list_head);
-               list_del_init(&client->ports_list_head);
-       } else {
-               INIT_LIST_HEAD(&deleted_list);
+       guard(mutex)(&client->ports_mutex);
+       scoped_guard(write_lock_irq, &client->ports_lock) {
+               if (!list_empty(&client->ports_list_head)) {
+                       list_add(&deleted_list, &client->ports_list_head);
+                       list_del_init(&client->ports_list_head);
+               } else {
+                       INIT_LIST_HEAD(&deleted_list);
+               }
+               client->num_ports = 0;
        }
-       client->num_ports = 0;
-       write_unlock_irq(&client->ports_lock);
 
        /* remove each port in deleted_list */
        list_for_each_entry_safe(port, tmp, &deleted_list, list) {
@@ -326,7 +318,6 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
                snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
                port_delete(client, port);
        }
-       mutex_unlock(&client->ports_mutex);
        return 0;
 }
 
@@ -506,42 +497,37 @@ static int check_and_subscribe_port(struct snd_seq_client *client,
        int err;
 
        grp = is_src ? &port->c_src : &port->c_dest;
-       err = -EBUSY;
-       down_write(&grp->list_mutex);
+       guard(rwsem_write)(&grp->list_mutex);
        if (exclusive) {
                if (!list_empty(&grp->list_head))
-                       goto __error;
+                       return -EBUSY;
        } else {
                if (grp->exclusive)
-                       goto __error;
+                       return -EBUSY;
                /* check whether already exists */
                list_for_each(p, &grp->list_head) {
                        s = get_subscriber(p, is_src);
                        if (match_subs_info(&subs->info, &s->info))
-                               goto __error;
+                               return -EBUSY;
                }
        }
 
        err = subscribe_port(client, port, grp, &subs->info, ack);
        if (err < 0) {
                grp->exclusive = 0;
-               goto __error;
+               return err;
        }
 
        /* add to list */
-       write_lock_irq(&grp->list_lock);
+       guard(write_lock_irq)(&grp->list_lock);
        if (is_src)
                list_add_tail(&subs->src_list, &grp->list_head);
        else
                list_add_tail(&subs->dest_list, &grp->list_head);
        grp->exclusive = exclusive;
        atomic_inc(&subs->ref_count);
-       write_unlock_irq(&grp->list_lock);
-       err = 0;
 
- __error:
-       up_write(&grp->list_mutex);
-       return err;
+       return 0;
 }
 
 /* called with grp->list_mutex held */
@@ -556,12 +542,12 @@ static void __delete_and_unsubscribe_port(struct snd_seq_client *client,
 
        grp = is_src ? &port->c_src : &port->c_dest;
        list = is_src ? &subs->src_list : &subs->dest_list;
-       write_lock_irq(&grp->list_lock);
-       empty = list_empty(list);
-       if (!empty)
-               list_del_init(list);
-       grp->exclusive = 0;
-       write_unlock_irq(&grp->list_lock);
+       scoped_guard(write_lock_irq, &grp->list_lock) {
+               empty = list_empty(list);
+               if (!empty)
+                       list_del_init(list);
+               grp->exclusive = 0;
+       }
 
        if (!empty)
                unsubscribe_port(client, port, grp, &subs->info, ack);
@@ -575,9 +561,8 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
        struct snd_seq_port_subs_info *grp;
 
        grp = is_src ? &port->c_src : &port->c_dest;
-       down_write(&grp->list_mutex);
+       guard(rwsem_write)(&grp->list_mutex);
        __delete_and_unsubscribe_port(client, port, subs, is_src, ack);
-       up_write(&grp->list_mutex);
 }
 
 /* connect two ports */
@@ -639,18 +624,18 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
        /* always start from deleting the dest port for avoiding concurrent
         * deletions
         */
-       down_write(&dest->list_mutex);
-       /* look for the connection */
-       list_for_each_entry(subs, &dest->list_head, dest_list) {
-               if (match_subs_info(info, &subs->info)) {
-                       __delete_and_unsubscribe_port(dest_client, dest_port,
-                                                     subs, false,
-                                                     connector->number != dest_client->number);
-                       err = 0;
-                       break;
+       scoped_guard(rwsem_write, &dest->list_mutex) {
+               /* look for the connection */
+               list_for_each_entry(subs, &dest->list_head, dest_list) {
+                       if (match_subs_info(info, &subs->info)) {
+                               __delete_and_unsubscribe_port(dest_client, dest_port,
+                                                             subs, false,
+                                                             connector->number != dest_client->number);
+                               err = 0;
+                               break;
+                       }
                }
        }
-       up_write(&dest->list_mutex);
        if (err < 0)
                return err;
 
@@ -669,7 +654,7 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
        struct snd_seq_subscribers *s;
        int err = -ENOENT;
 
-       down_read(&src_grp->list_mutex);
+       guard(rwsem_read)(&src_grp->list_mutex);
        list_for_each_entry(s, &src_grp->list_head, src_list) {
                if (addr_match(dest_addr, &s->info.dest)) {
                        *subs = s->info;
@@ -677,7 +662,6 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
                        break;
                }
        }
-       up_read(&src_grp->list_mutex);
        return err;
 }
 
index 1d857981e8764ce506df3457e97ce44eddb593fa..e649485a877222a58c93468df825d43051be608c 100644 (file)
@@ -132,7 +132,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
                          struct snd_seq_event_cell * cell)
 {
        struct snd_seq_event_cell *cur, *prev;
-       unsigned long flags;
        int count;
        int prior;
 
@@ -142,7 +141,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
        /* check flags */
        prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
 
-       spin_lock_irqsave(&f->lock, flags);
+       guard(spinlock_irqsave)(&f->lock);
 
        /* check if this element needs to inserted at the end (ie. ordered 
           data is inserted) This will be very likeley if a sequencer 
@@ -154,7 +153,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
                        f->tail = cell;
                        cell->next = NULL;
                        f->cells++;
-                       spin_unlock_irqrestore(&f->lock, flags);
                        return 0;
                }
        }
@@ -179,7 +177,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
                prev = cur;
                cur = cur->next;
                if (! --count) {
-                       spin_unlock_irqrestore(&f->lock, flags);
                        pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
                        return -EINVAL;
                }
@@ -195,7 +192,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
        if (cur == NULL) /* reached end of the list */
                f->tail = cell;
        f->cells++;
-       spin_unlock_irqrestore(&f->lock, flags);
        return 0;
 }
 
@@ -213,14 +209,13 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
                                                  void *current_time)
 {
        struct snd_seq_event_cell *cell;
-       unsigned long flags;
 
        if (f == NULL) {
                pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
                return NULL;
        }
-       spin_lock_irqsave(&f->lock, flags);
 
+       guard(spinlock_irqsave)(&f->lock);
        cell = f->head;
        if (cell && current_time && !event_is_ready(&cell->event, current_time))
                cell = NULL;
@@ -235,7 +230,6 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
                f->cells--;
        }
 
-       spin_unlock_irqrestore(&f->lock, flags);
        return cell;
 }
 
@@ -249,73 +243,43 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f)
        return f->cells;
 }
 
-static inline int prioq_match(struct snd_seq_event_cell *cell,
-                             int client, int timestamp)
-{
-       if (cell->event.source.client == client ||
-           cell->event.dest.client == client)
-               return 1;
-       if (!timestamp)
-               return 0;
-       switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
-       case SNDRV_SEQ_TIME_STAMP_TICK:
-               if (cell->event.time.tick)
-                       return 1;
-               break;
-       case SNDRV_SEQ_TIME_STAMP_REAL:
-               if (cell->event.time.time.tv_sec ||
-                   cell->event.time.time.tv_nsec)
-                       return 1;
-               break;
-       }
-       return 0;
-}
-
-/* remove cells for left client */
-void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
+/* remove cells matching with the condition */
+static void prioq_remove_cells(struct snd_seq_prioq *f,
+                              bool (*match)(struct snd_seq_event_cell *cell,
+                                            void *arg),
+                              void *arg)
 {
        register struct snd_seq_event_cell *cell, *next;
-       unsigned long flags;
        struct snd_seq_event_cell *prev = NULL;
        struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
 
        /* collect all removed cells */
-       spin_lock_irqsave(&f->lock, flags);
-       cell = f->head;
-       while (cell) {
-               next = cell->next;
-               if (prioq_match(cell, client, timestamp)) {
+       scoped_guard(spinlock_irqsave, &f->lock) {
+               for (cell = f->head; cell; cell = next) {
+                       next = cell->next;
+                       if (!match(cell, arg)) {
+                               prev = cell;
+                               continue;
+                       }
+
                        /* remove cell from prioq */
-                       if (cell == f->head) {
+                       if (cell == f->head)
                                f->head = cell->next;
-                       } else {
+                       else
                                prev->next = cell->next;
-                       }
                        if (cell == f->tail)
                                f->tail = cell->next;
                        f->cells--;
+
                        /* add cell to free list */
                        cell->next = NULL;
-                       if (freefirst == NULL) {
+                       if (freefirst == NULL)
                                freefirst = cell;
-                       } else {
+                       else
                                freeprev->next = cell;
-                       }
                        freeprev = cell;
-               } else {
-#if 0
-                       pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
-                              "client = %i\n",
-                               cell->event.type,
-                               cell->event.source.client,
-                               cell->event.dest.client,
-                               client);
-#endif
-                       prev = cell;
                }
-               cell = next;            
        }
-       spin_unlock_irqrestore(&f->lock, flags);        
 
        /* remove selected cells */
        while (freefirst) {
@@ -325,22 +289,68 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
        }
 }
 
-static int prioq_remove_match(struct snd_seq_remove_events *info,
-                             struct snd_seq_event *ev)
+struct prioq_match_arg {
+       int client;
+       int timestamp;
+};
+
+static inline bool prioq_match(struct snd_seq_event_cell *cell, void *arg)
+{
+       struct prioq_match_arg *v = arg;
+
+       if (cell->event.source.client == v->client ||
+           cell->event.dest.client == v->client)
+               return true;
+       if (!v->timestamp)
+               return false;
+       switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+       case SNDRV_SEQ_TIME_STAMP_TICK:
+               if (cell->event.time.tick)
+                       return true;
+               break;
+       case SNDRV_SEQ_TIME_STAMP_REAL:
+               if (cell->event.time.time.tv_sec ||
+                   cell->event.time.time.tv_nsec)
+                       return true;
+               break;
+       }
+       return false;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp)
 {
+       struct prioq_match_arg arg = { client, timestamp };
+
+       return prioq_remove_cells(f, prioq_match, &arg);
+}
+
+struct prioq_remove_match_arg {
+       int client;
+       struct snd_seq_remove_events *info;
+};
+
+static bool prioq_remove_match(struct snd_seq_event_cell *cell, void *arg)
+{
+       struct prioq_remove_match_arg *v = arg;
+       struct snd_seq_event *ev = &cell->event;
+       struct snd_seq_remove_events *info = v->info;
        int res;
 
+       if (ev->source.client != v->client)
+               return false;
+
        if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
                if (ev->dest.client != info->dest.client ||
                                ev->dest.port != info->dest.port)
-                       return 0;
+                       return false;
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
                if (! snd_seq_ev_is_channel_type(ev))
-                       return 0;
+                       return false;
                /* data.note.channel and data.control.channel are identical */
                if (ev->data.note.channel != info->channel)
-                       return 0;
+                       return false;
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
                if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -348,7 +358,7 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
                else
                        res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
                if (!res)
-                       return 0;
+                       return false;
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
                if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -356,81 +366,35 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
                else
                        res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
                if (res)
-                       return 0;
+                       return false;
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
                if (ev->type != info->type)
-                       return 0;
+                       return false;
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
                /* Do not remove off events */
                switch (ev->type) {
                case SNDRV_SEQ_EVENT_NOTEOFF:
                /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
-                       return 0;
+                       return false;
                default:
                        break;
                }
        }
        if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
                if (info->tag != ev->tag)
-                       return 0;
+                       return false;
        }
 
-       return 1;
+       return true;
 }
 
 /* remove cells matching remove criteria */
 void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
                                 struct snd_seq_remove_events *info)
 {
-       struct snd_seq_event_cell *cell, *next;
-       unsigned long flags;
-       struct snd_seq_event_cell *prev = NULL;
-       struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
-
-       /* collect all removed cells */
-       spin_lock_irqsave(&f->lock, flags);
-       cell = f->head;
-
-       while (cell) {
-               next = cell->next;
-               if (cell->event.source.client == client &&
-                       prioq_remove_match(info, &cell->event)) {
+       struct prioq_remove_match_arg arg = { client, info };
 
-                       /* remove cell from prioq */
-                       if (cell == f->head) {
-                               f->head = cell->next;
-                       } else {
-                               prev->next = cell->next;
-                       }
-
-                       if (cell == f->tail)
-                               f->tail = cell->next;
-                       f->cells--;
-
-                       /* add cell to free list */
-                       cell->next = NULL;
-                       if (freefirst == NULL) {
-                               freefirst = cell;
-                       } else {
-                               freeprev->next = cell;
-                       }
-
-                       freeprev = cell;
-               } else {
-                       prev = cell;
-               }
-               cell = next;            
-       }
-       spin_unlock_irqrestore(&f->lock, flags);        
-
-       /* remove selected cells */
-       while (freefirst) {
-               freenext = freefirst->next;
-               snd_seq_cell_free(freefirst);
-               freefirst = freenext;
-       }
+       return prioq_remove_cells(f, prioq_remove_match, &arg);
 }
-
-
index bc933104c3eea64556bf92bc041d869fc629137e..500ee6b19c717746af1b3f723d4e667825813b33 100644 (file)
@@ -50,43 +50,35 @@ int snd_seq_queue_get_cur_queues(void)
 static int queue_list_add(struct snd_seq_queue *q)
 {
        int i;
-       unsigned long flags;
 
-       spin_lock_irqsave(&queue_list_lock, flags);
+       guard(spinlock_irqsave)(&queue_list_lock);
        for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
                if (! queue_list[i]) {
                        queue_list[i] = q;
                        q->queue = i;
                        num_queues++;
-                       spin_unlock_irqrestore(&queue_list_lock, flags);
                        return i;
                }
        }
-       spin_unlock_irqrestore(&queue_list_lock, flags);
        return -1;
 }
 
 static struct snd_seq_queue *queue_list_remove(int id, int client)
 {
        struct snd_seq_queue *q;
-       unsigned long flags;
 
-       spin_lock_irqsave(&queue_list_lock, flags);
+       guard(spinlock_irqsave)(&queue_list_lock);
        q = queue_list[id];
        if (q) {
-               spin_lock(&q->owner_lock);
+               guard(spinlock)(&q->owner_lock);
                if (q->owner == client) {
                        /* found */
                        q->klocked = 1;
-                       spin_unlock(&q->owner_lock);
                        queue_list[id] = NULL;
                        num_queues--;
-                       spin_unlock_irqrestore(&queue_list_lock, flags);
                        return q;
                }
-               spin_unlock(&q->owner_lock);
        }
-       spin_unlock_irqrestore(&queue_list_lock, flags);
        return NULL;
 }
 
@@ -203,15 +195,13 @@ int snd_seq_queue_delete(int client, int queueid)
 struct snd_seq_queue *queueptr(int queueid)
 {
        struct snd_seq_queue *q;
-       unsigned long flags;
 
        if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
                return NULL;
-       spin_lock_irqsave(&queue_list_lock, flags);
+       guard(spinlock_irqsave)(&queue_list_lock);
        q = queue_list[queueid];
        if (q)
                snd_use_lock_use(&q->use_lock);
-       spin_unlock_irqrestore(&queue_list_lock, flags);
        return q;
 }
 
@@ -239,7 +229,6 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
 
 void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
 {
-       unsigned long flags;
        struct snd_seq_event_cell *cell;
        snd_seq_tick_time_t cur_tick;
        snd_seq_real_time_t cur_time;
@@ -249,14 +238,13 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
                return;
 
        /* make this function non-reentrant */
-       spin_lock_irqsave(&q->check_lock, flags);
-       if (q->check_blocked) {
-               q->check_again = 1;
-               spin_unlock_irqrestore(&q->check_lock, flags);
-               return;         /* other thread is already checking queues */
+       scoped_guard(spinlock_irqsave, &q->check_lock) {
+               if (q->check_blocked) {
+                       q->check_again = 1;
+                       return; /* other thread is already checking queues */
+               }
+               q->check_blocked = 1;
        }
-       q->check_blocked = 1;
-       spin_unlock_irqrestore(&q->check_lock, flags);
 
       __again:
        /* Process tick queue... */
@@ -283,16 +271,14 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
 
  out:
        /* free lock */
-       spin_lock_irqsave(&q->check_lock, flags);
-       if (q->check_again) {
-               q->check_again = 0;
-               if (processed < MAX_CELL_PROCESSES_IN_QUEUE) {
-                       spin_unlock_irqrestore(&q->check_lock, flags);
-                       goto __again;
+       scoped_guard(spinlock_irqsave, &q->check_lock) {
+               if (q->check_again) {
+                       q->check_again = 0;
+                       if (processed < MAX_CELL_PROCESSES_IN_QUEUE)
+                               goto __again;
                }
+               q->check_blocked = 0;
        }
-       q->check_blocked = 0;
-       spin_unlock_irqrestore(&q->check_lock, flags);
 }
 
 
@@ -361,25 +347,20 @@ static inline int check_access(struct snd_seq_queue *q, int client)
  */
 static int queue_access_lock(struct snd_seq_queue *q, int client)
 {
-       unsigned long flags;
        int access_ok;
        
-       spin_lock_irqsave(&q->owner_lock, flags);
+       guard(spinlock_irqsave)(&q->owner_lock);
        access_ok = check_access(q, client);
        if (access_ok)
                q->klocked = 1;
-       spin_unlock_irqrestore(&q->owner_lock, flags);
        return access_ok;
 }
 
 /* unlock the queue */
 static inline void queue_access_unlock(struct snd_seq_queue *q)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&q->owner_lock, flags);
+       guard(spinlock_irqsave)(&q->owner_lock);
        q->klocked = 0;
-       spin_unlock_irqrestore(&q->owner_lock, flags);
 }
 
 /* exported - only checking permission */
@@ -387,13 +368,11 @@ int snd_seq_queue_check_access(int queueid, int client)
 {
        struct snd_seq_queue *q = queueptr(queueid);
        int access_ok;
-       unsigned long flags;
 
        if (! q)
                return 0;
-       spin_lock_irqsave(&q->owner_lock, flags);
-       access_ok = check_access(q, client);
-       spin_unlock_irqrestore(&q->owner_lock, flags);
+       scoped_guard(spinlock_irqsave, &q->owner_lock)
+               access_ok = check_access(q, client);
        queuefree(q);
        return access_ok;
 }
@@ -406,7 +385,6 @@ int snd_seq_queue_check_access(int queueid, int client)
 int snd_seq_queue_set_owner(int queueid, int client, int locked)
 {
        struct snd_seq_queue *q = queueptr(queueid);
-       unsigned long flags;
 
        if (q == NULL)
                return -EINVAL;
@@ -416,10 +394,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
                return -EPERM;
        }
 
-       spin_lock_irqsave(&q->owner_lock, flags);
-       q->locked = locked ? 1 : 0;
-       q->owner = client;
-       spin_unlock_irqrestore(&q->owner_lock, flags);
+       scoped_guard(spinlock_irqsave, &q->owner_lock) {
+               q->locked = locked ? 1 : 0;
+               q->owner = client;
+       }
        queue_access_unlock(q);
        queuefree(q);
 
@@ -750,10 +728,10 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
                else
                        bpm = 0;
 
-               spin_lock_irq(&q->owner_lock);
-               locked = q->locked;
-               owner = q->owner;
-               spin_unlock_irq(&q->owner_lock);
+               scoped_guard(spinlock_irq, &q->owner_lock) {
+                       locked = q->locked;
+                       owner = q->owner;
+               }
 
                snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
                snd_iprintf(buffer, "owned by client    : %d\n", owner);
index 9863be6fd43e1a34aece5ffe258ffc627eb05bc2..ad2b97e2762d998c140eabf2a783340c8ed02e5a 100644 (file)
@@ -75,9 +75,7 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
 
 void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        /* setup defaults */
        tmr->ppq = 96;          /* 96 PPQ */
        tmr->tempo = 500000;    /* 120 BPM */
@@ -93,7 +91,6 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
        tmr->preferred_resolution = seq_default_timer_resolution;
 
        tmr->skew = tmr->skew_base = SKEW_BASE;
-       spin_unlock_irqrestore(&tmr->lock, flags);
 }
 
 static void seq_timer_reset(struct snd_seq_timer *tmr)
@@ -108,11 +105,8 @@ static void seq_timer_reset(struct snd_seq_timer *tmr)
 
 void snd_seq_timer_reset(struct snd_seq_timer *tmr)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        seq_timer_reset(tmr);
-       spin_unlock_irqrestore(&tmr->lock, flags);
 }
 
 
@@ -121,7 +115,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
                                    unsigned long resolution,
                                    unsigned long ticks)
 {
-       unsigned long flags;
        struct snd_seq_queue *q = timeri->callback_data;
        struct snd_seq_timer *tmr;
 
@@ -130,29 +123,27 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
        tmr = q->timer;
        if (tmr == NULL)
                return;
-       spin_lock_irqsave(&tmr->lock, flags);
-       if (!tmr->running) {
-               spin_unlock_irqrestore(&tmr->lock, flags);
-               return;
-       }
 
-       resolution *= ticks;
-       if (tmr->skew != tmr->skew_base) {
-               /* FIXME: assuming skew_base = 0x10000 */
-               resolution = (resolution >> 16) * tmr->skew +
-                       (((resolution & 0xffff) * tmr->skew) >> 16);
-       }
+       scoped_guard(spinlock_irqsave, &tmr->lock) {
+               if (!tmr->running)
+                       return;
 
-       /* update timer */
-       snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+               resolution *= ticks;
+               if (tmr->skew != tmr->skew_base) {
+                       /* FIXME: assuming skew_base = 0x10000 */
+                       resolution = (resolution >> 16) * tmr->skew +
+                               (((resolution & 0xffff) * tmr->skew) >> 16);
+               }
 
-       /* calculate current tick */
-       snd_seq_timer_update_tick(&tmr->tick, resolution);
+               /* update timer */
+               snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
 
-       /* register actual time of this timer update */
-       ktime_get_ts64(&tmr->last_update);
+               /* calculate current tick */
+               snd_seq_timer_update_tick(&tmr->tick, resolution);
 
-       spin_unlock_irqrestore(&tmr->lock, flags);
+               /* register actual time of this timer update */
+               ktime_get_ts64(&tmr->last_update);
+       }
 
        /* check queues and dispatch events */
        snd_seq_check_queue(q, 1, 0);
@@ -161,18 +152,15 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
 /* set current tempo */
 int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
        if (tempo <= 0)
                return -EINVAL;
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        if ((unsigned int)tempo != tmr->tempo) {
                tmr->tempo = tempo;
                snd_seq_timer_set_tick_resolution(tmr);
        }
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
 
@@ -180,17 +168,15 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
 int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
 {
        int changed;
-       unsigned long flags;
 
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
        if (tempo <= 0 || ppq <= 0)
                return -EINVAL;
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        if (tmr->running && (ppq != tmr->ppq)) {
                /* refuse to change ppq on running timers */
                /* because it will upset the song position (ticks) */
-               spin_unlock_irqrestore(&tmr->lock, flags);
                pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
                return -EBUSY;
        }
@@ -199,7 +185,6 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
        tmr->ppq = ppq;
        if (changed)
                snd_seq_timer_set_tick_resolution(tmr);
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
 
@@ -207,15 +192,12 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
 int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
                                    snd_seq_tick_time_t position)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
 
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        tmr->tick.cur_tick = position;
        tmr->tick.fraction = 0;
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
 
@@ -223,15 +205,12 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
 int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
                                    snd_seq_real_time_t position)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
 
        snd_seq_sanity_real_time(&position);
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        tmr->cur_time = position;
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
 
@@ -239,8 +218,6 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
 int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
                           unsigned int base)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
 
@@ -249,9 +226,8 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
                pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
                return -EINVAL;
        }
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        tmr->skew = skew;
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
 
@@ -296,12 +272,12 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
                snd_timer_instance_free(t);
                return err;
        }
-       spin_lock_irq(&tmr->lock);
-       if (tmr->timeri)
-               err = -EBUSY;
-       else
-               tmr->timeri = t;
-       spin_unlock_irq(&tmr->lock);
+       scoped_guard(spinlock_irq, &tmr->lock) {
+               if (tmr->timeri)
+                       err = -EBUSY;
+               else
+                       tmr->timeri = t;
+       }
        if (err < 0) {
                snd_timer_close(t);
                snd_timer_instance_free(t);
@@ -318,10 +294,10 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
        tmr = q->timer;
        if (snd_BUG_ON(!tmr))
                return -EINVAL;
-       spin_lock_irq(&tmr->lock);
-       t = tmr->timeri;
-       tmr->timeri = NULL;
-       spin_unlock_irq(&tmr->lock);
+       scoped_guard(spinlock_irq, &tmr->lock) {
+               t = tmr->timeri;
+               tmr->timeri = NULL;
+       }
        if (t) {
                snd_timer_close(t);
                snd_timer_instance_free(t);
@@ -342,13 +318,8 @@ static int seq_timer_stop(struct snd_seq_timer *tmr)
 
 int snd_seq_timer_stop(struct snd_seq_timer *tmr)
 {
-       unsigned long flags;
-       int err;
-
-       spin_lock_irqsave(&tmr->lock, flags);
-       err = seq_timer_stop(tmr);
-       spin_unlock_irqrestore(&tmr->lock, flags);
-       return err;
+       guard(spinlock_irqsave)(&tmr->lock);
+       return seq_timer_stop(tmr);
 }
 
 static int initialize_timer(struct snd_seq_timer *tmr)
@@ -398,13 +369,8 @@ static int seq_timer_start(struct snd_seq_timer *tmr)
 
 int snd_seq_timer_start(struct snd_seq_timer *tmr)
 {
-       unsigned long flags;
-       int err;
-
-       spin_lock_irqsave(&tmr->lock, flags);
-       err = seq_timer_start(tmr);
-       spin_unlock_irqrestore(&tmr->lock, flags);
-       return err;
+       guard(spinlock_irqsave)(&tmr->lock);
+       return seq_timer_start(tmr);
 }
 
 static int seq_timer_continue(struct snd_seq_timer *tmr)
@@ -426,13 +392,8 @@ static int seq_timer_continue(struct snd_seq_timer *tmr)
 
 int snd_seq_timer_continue(struct snd_seq_timer *tmr)
 {
-       unsigned long flags;
-       int err;
-
-       spin_lock_irqsave(&tmr->lock, flags);
-       err = seq_timer_continue(tmr);
-       spin_unlock_irqrestore(&tmr->lock, flags);
-       return err;
+       guard(spinlock_irqsave)(&tmr->lock);
+       return seq_timer_continue(tmr);
 }
 
 /* return current 'real' time. use timeofday() to get better granularity. */
@@ -440,9 +401,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
                                               bool adjust_ktime)
 {
        snd_seq_real_time_t cur_time;
-       unsigned long flags;
 
-       spin_lock_irqsave(&tmr->lock, flags);
+       guard(spinlock_irqsave)(&tmr->lock);
        cur_time = tmr->cur_time;
        if (adjust_ktime && tmr->running) {
                struct timespec64 tm;
@@ -453,7 +413,6 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
                cur_time.tv_sec += tm.tv_sec;
                snd_seq_sanity_real_time(&cur_time);
        }
-       spin_unlock_irqrestore(&tmr->lock, flags);
        return cur_time;        
 }
 
@@ -461,13 +420,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
  high PPQ values) */
 snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
 {
-       snd_seq_tick_time_t cur_tick;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tmr->lock, flags);
-       cur_tick = tmr->tick.cur_tick;
-       spin_unlock_irqrestore(&tmr->lock, flags);
-       return cur_tick;
+       guard(spinlock_irqsave)(&tmr->lock);
+       return tmr->tick.cur_tick;
 }
 
 
@@ -486,19 +440,18 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
                q = queueptr(idx);
                if (q == NULL)
                        continue;
-               mutex_lock(&q->timer_mutex);
-               tmr = q->timer;
-               if (!tmr)
-                       goto unlock;
-               ti = tmr->timeri;
-               if (!ti)
-                       goto unlock;
-               snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
-               resolution = snd_timer_resolution(ti) * tmr->ticks;
-               snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
-               snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
-unlock:
-               mutex_unlock(&q->timer_mutex);
+               scoped_guard(mutex, &q->timer_mutex) {
+                       tmr = q->timer;
+                       if (!tmr)
+                               break;
+                       ti = tmr->timeri;
+                       if (!ti)
+                               break;
+                       snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
+                       resolution = snd_timer_resolution(ti) * tmr->ticks;
+                       snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
+                       snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+               }
                queuefree(q);
        }
 }
index 2db371d79930d0f2af7630f50901a3d69006e538..c627d72f7fe2029579a9bc41cfea5fdcbe02ee70 100644 (file)
@@ -115,21 +115,19 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct,
 static int seq_ump_client_open(struct seq_ump_client *client, int dir)
 {
        struct snd_ump_endpoint *ump = client->ump;
-       int err = 0;
+       int err;
 
-       mutex_lock(&ump->open_mutex);
+       guard(mutex)(&ump->open_mutex);
        if (dir == STR_OUT && !client->opened[dir]) {
                err = snd_rawmidi_kernel_open(&ump->core, 0,
                                              SNDRV_RAWMIDI_LFLG_OUTPUT |
                                              SNDRV_RAWMIDI_LFLG_APPEND,
                                              &client->out_rfile);
                if (err < 0)
-                       goto unlock;
+                       return err;
        }
        client->opened[dir]++;
- unlock:
-       mutex_unlock(&ump->open_mutex);
-       return err;
+       return 0;
 }
 
 /* close the rawmidi */
@@ -137,11 +135,10 @@ static int seq_ump_client_close(struct seq_ump_client *client, int dir)
 {
        struct snd_ump_endpoint *ump = client->ump;
 
-       mutex_lock(&ump->open_mutex);
+       guard(mutex)(&ump->open_mutex);
        if (!--client->opened[dir])
                if (dir == STR_OUT)
                        snd_rawmidi_kernel_release(&client->out_rfile);
-       mutex_unlock(&ump->open_mutex);
        return 0;
 }
 
@@ -217,15 +214,12 @@ static void fill_port_info(struct snd_seq_port_info *port,
 static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
 {
        struct seq_ump_group *group = &client->groups[group_index];
-       struct snd_seq_port_info *port;
+       struct snd_seq_port_info *port __free(kfree) = NULL;
        struct snd_seq_port_callback pcallbacks;
-       int err;
 
        port = kzalloc(sizeof(*port), GFP_KERNEL);
-       if (!port) {
-               err = -ENOMEM;
-               goto error;
-       }
+       if (!port)
+               return -ENOMEM;
 
        fill_port_info(port, client, group);
        port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
@@ -238,24 +232,22 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
        pcallbacks.unuse = seq_ump_unuse;
        pcallbacks.event_input = seq_ump_process_event;
        port->kernel = &pcallbacks;
-       err = snd_seq_kernel_client_ctl(client->seq_client,
-                                       SNDRV_SEQ_IOCTL_CREATE_PORT,
-                                       port);
- error:
-       kfree(port);
-       return err;
+       return snd_seq_kernel_client_ctl(client->seq_client,
+                                        SNDRV_SEQ_IOCTL_CREATE_PORT,
+                                        port);
 }
 
 /* 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, *new;
+       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);
        if (!old || !new)
-               goto error;
+               return;
 
        for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
                old->addr.client = client->seq_client;
@@ -264,7 +256,7 @@ static void update_port_infos(struct seq_ump_client *client)
                                                SNDRV_SEQ_IOCTL_GET_PORT_INFO,
                                                old);
                if (err < 0)
-                       goto error;
+                       return;
                fill_port_info(new, client, &client->groups[i]);
                if (old->capability == new->capability &&
                    !strcmp(old->name, new->name))
@@ -273,13 +265,10 @@ static void update_port_infos(struct seq_ump_client *client)
                                                SNDRV_SEQ_IOCTL_SET_PORT_INFO,
                                                new);
                if (err < 0)
-                       goto error;
+                       return;
                /* notify to system port */
                snd_seq_system_client_ev_port_change(client->seq_client, i);
        }
- error:
-       kfree(new);
-       kfree(old);
 }
 
 /* update dir_bits and active flag for all groups in the client */
@@ -334,7 +323,7 @@ static void update_group_attrs(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;
+       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;
@@ -383,7 +372,6 @@ static int create_ump_endpoint_port(struct seq_ump_client *client)
        err = snd_seq_kernel_client_ctl(client->seq_client,
                                        SNDRV_SEQ_IOCTL_CREATE_PORT,
                                        port);
-       kfree(port);
        return err;
 }
 
index 1b9260108e48219b35fc66b04149db647c2a6775..b4672613c26136adf778cde4a72e7274abcddd18 100644 (file)
@@ -62,6 +62,13 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
 /*
  * decode input event and put to read buffer of each opened file
  */
+
+/* callback for snd_seq_dump_var_event(), bridging to snd_rawmidi_receive() */
+static int dump_to_rawmidi(void *ptr, void *buf, int count)
+{
+       return snd_rawmidi_receive(ptr, buf, count);
+}
+
 static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
                                         struct snd_seq_event *ev,
                                         bool atomic)
@@ -80,7 +87,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
                if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
                        if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
                                continue;
-                       snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+                       snd_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream);
                        snd_midi_event_reset_decode(vmidi->parser);
                } else {
                        len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
@@ -192,11 +199,10 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
        vmidi->client = rdev->client;
        vmidi->port = rdev->port;       
        runtime->private_data = vmidi;
-       down_write(&rdev->filelist_sem);
-       write_lock_irq(&rdev->filelist_lock);
-       list_add_tail(&vmidi->list, &rdev->filelist);
-       write_unlock_irq(&rdev->filelist_lock);
-       up_write(&rdev->filelist_sem);
+       scoped_guard(rwsem_write, &rdev->filelist_sem) {
+               guard(write_lock_irq)(&rdev->filelist_lock);
+               list_add_tail(&vmidi->list, &rdev->filelist);
+       }
        vmidi->rdev = rdev;
        return 0;
 }
@@ -236,11 +242,10 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
        struct snd_virmidi *vmidi = substream->runtime->private_data;
 
-       down_write(&rdev->filelist_sem);
-       write_lock_irq(&rdev->filelist_lock);
-       list_del(&vmidi->list);
-       write_unlock_irq(&rdev->filelist_lock);
-       up_write(&rdev->filelist_sem);
+       scoped_guard(rwsem_write, &rdev->filelist_sem) {
+               guard(write_lock_irq)(&rdev->filelist_lock);
+               list_del(&vmidi->list);
+       }
        snd_midi_event_free(vmidi->parser);
        substream->runtime->private_data = NULL;
        kfree(vmidi);
@@ -356,26 +361,22 @@ 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;
+       struct snd_seq_port_info *pinfo __free(kfree) = NULL;
        int err;
 
        if (rdev->client >= 0)
                return 0;
 
        pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
-       if (!pinfo) {
-               err = -ENOMEM;
-               goto __error;
-       }
+       if (!pinfo)
+               return -ENOMEM;
 
        client = snd_seq_create_kernel_client(rdev->card, rdev->device,
                                              "%s %d-%d", rdev->rmidi->name,
                                              rdev->card->number,
                                              rdev->device);
-       if (client < 0) {
-               err = client;
-               goto __error;
-       }
+       if (client < 0)
+               return client;
        rdev->client = client;
 
        /* create a port */
@@ -403,15 +404,11 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
        if (err < 0) {
                snd_seq_delete_kernel_client(client);
                rdev->client = -1;
-               goto __error;
+               return err;
        }
 
        rdev->port = pinfo->addr.port;
-       err = 0; /* success */
-
- __error:
-       kfree(pinfo);
-       return err;
+       return 0; /* success */
 }
 
 
index 7f3fd8eb016fe825e13394576f803f0f27603fe0..654d620d0199f7e0e501075d22899ff37afa937a 100644 (file)
@@ -49,7 +49,7 @@ static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
                sdrv->argsize == sdev->argsize;
 }
 
-static struct bus_type snd_seq_bus_type = {
+static const struct bus_type snd_seq_bus_type = {
        .name = "snd_seq",
        .match = snd_seq_bus_match,
 };
index df5571d9862955fe576be3be5649d67a61e170c3..b9db9aa0bfcb583a143a88347ab7d4cdf37321fa 100644 (file)
@@ -103,7 +103,7 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
 
        if (minor >= ARRAY_SIZE(snd_minors))
                return NULL;
-       mutex_lock(&sound_mutex);
+       guard(mutex)(&sound_mutex);
        mreg = snd_minors[minor];
        if (mreg && mreg->type == type) {
                private_data = mreg->private_data;
@@ -111,7 +111,6 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
                        get_device(&mreg->card_ptr->card_dev);
        } else
                private_data = NULL;
-       mutex_unlock(&sound_mutex);
        return private_data;
 }
 EXPORT_SYMBOL(snd_lookup_minor_data);
@@ -150,17 +149,15 @@ static int snd_open(struct inode *inode, struct file *file)
 
        if (minor >= ARRAY_SIZE(snd_minors))
                return -ENODEV;
-       mutex_lock(&sound_mutex);
-       mptr = snd_minors[minor];
-       if (mptr == NULL) {
-               mptr = autoload_device(minor);
-               if (!mptr) {
-                       mutex_unlock(&sound_mutex);
-                       return -ENODEV;
+       scoped_guard(mutex, &sound_mutex) {
+               mptr = snd_minors[minor];
+               if (mptr == NULL) {
+                       mptr = autoload_device(minor);
+                       if (!mptr)
+                               return -ENODEV;
                }
+               new_fops = fops_get(mptr->f_ops);
        }
-       new_fops = fops_get(mptr->f_ops);
-       mutex_unlock(&sound_mutex);
        if (!new_fops)
                return -ENODEV;
        replace_fops(file, new_fops);
@@ -269,7 +266,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
        preg->f_ops = f_ops;
        preg->private_data = private_data;
        preg->card_ptr = card;
-       mutex_lock(&sound_mutex);
+       guard(mutex)(&sound_mutex);
        minor = snd_find_free_minor(type, card, dev);
        if (minor < 0) {
                err = minor;
@@ -284,7 +281,6 @@ int snd_register_device(int type, struct snd_card *card, int dev,
 
        snd_minors[minor] = preg;
  error:
-       mutex_unlock(&sound_mutex);
        if (err < 0)
                kfree(preg);
        return err;
@@ -305,7 +301,7 @@ int snd_unregister_device(struct device *dev)
        int minor;
        struct snd_minor *preg;
 
-       mutex_lock(&sound_mutex);
+       guard(mutex)(&sound_mutex);
        for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
                preg = snd_minors[minor];
                if (preg && preg->dev == dev) {
@@ -315,7 +311,6 @@ int snd_unregister_device(struct device *dev)
                        break;
                }
        }
-       mutex_unlock(&sound_mutex);
        if (minor >= ARRAY_SIZE(snd_minors))
                return -ENOENT;
        return 0;
@@ -355,7 +350,7 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
        int minor;
        struct snd_minor *mptr;
 
-       mutex_lock(&sound_mutex);
+       guard(mutex)(&sound_mutex);
        for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
                mptr = snd_minors[minor];
                if (!mptr)
@@ -373,7 +368,6 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
                        snd_iprintf(buffer, "%3i:        : %s\n", minor,
                                    snd_device_type_name(mptr->type));
        }
-       mutex_unlock(&sound_mutex);
 }
 
 int __init snd_minor_info_init(void)
diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c
new file mode 100644 (file)
index 0000000..eb90f62
--- /dev/null
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sound core KUnit test
+ * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#define SILENCE_BUFFER_MAX_FRAMES 260
+#define SILENCE_BUFFER_SIZE (sizeof(u64) * SILENCE_BUFFER_MAX_FRAMES)
+#define SILENCE(...) { __VA_ARGS__ }
+#define DEFINE_FORMAT(fmt, pbits, wd, endianness, signd, silence_arr) {                \
+       .format = SNDRV_PCM_FORMAT_##fmt, .physical_bits = pbits,               \
+       .width = wd, .le = endianness, .sd = signd, .silence = silence_arr,     \
+       .name = #fmt,                                                           \
+}
+
+#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1)
+#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1
+
+#define VALID_NAME "ValidName"
+#define NAME_W_SPEC_CHARS "In%v@1id name"
+#define NAME_W_SPACE "Test name"
+#define NAME_W_SPACE_REMOVED "Testname"
+
+#define TEST_FIRST_COMPONENT "Component1"
+#define TEST_SECOND_COMPONENT "Component2"
+
+struct snd_format_test_data {
+       snd_pcm_format_t format;
+       int physical_bits;
+       int width;
+       int le;
+       int sd;
+       unsigned char silence[8];
+       unsigned char *name;
+};
+
+struct avail_test_data {
+       snd_pcm_uframes_t buffer_size;
+       snd_pcm_uframes_t hw_ptr;
+       snd_pcm_uframes_t appl_ptr;
+       snd_pcm_uframes_t expected_avail;
+};
+
+static struct snd_format_test_data valid_fmt[] = {
+       DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()),
+       DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)),
+       DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S16_BE, 16, 16, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U16_LE, 16, 16, 1, 0, SILENCE(0x00, 0x80)),
+       DEFINE_FORMAT(U16_BE, 16, 16, 0, 0, SILENCE(0x80, 0x00)),
+       DEFINE_FORMAT(S24_LE, 32, 24, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S24_BE, 32, 24, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U24_LE, 32, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+       DEFINE_FORMAT(U24_BE, 32, 24, 0, 0, SILENCE(0x00, 0x80, 0x00, 0x00)),
+       DEFINE_FORMAT(S32_LE, 32, 32, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S32_BE, 32, 32, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U32_LE, 32, 32, 1, 0, SILENCE(0x00, 0x00, 0x00, 0x80)),
+       DEFINE_FORMAT(U32_BE, 32, 32, 0, 0, SILENCE(0x80, 0x00, 0x00, 0x00)),
+       DEFINE_FORMAT(FLOAT_LE, 32, 32, 1, -1, SILENCE()),
+       DEFINE_FORMAT(FLOAT_BE, 32, 32, 0, -1, SILENCE()),
+       DEFINE_FORMAT(FLOAT64_LE, 64, 64, 1, -1, SILENCE()),
+       DEFINE_FORMAT(FLOAT64_BE, 64, 64, 0, -1, SILENCE()),
+       DEFINE_FORMAT(IEC958_SUBFRAME_LE, 32, 32, 1, -1, SILENCE()),
+       DEFINE_FORMAT(IEC958_SUBFRAME_BE, 32, 32, 0, -1, SILENCE()),
+       DEFINE_FORMAT(MU_LAW, 8, 8, -1, -1, SILENCE(0x7f)),
+       DEFINE_FORMAT(A_LAW, 8, 8, -1, -1, SILENCE(0x55)),
+       DEFINE_FORMAT(IMA_ADPCM, 4, 4, -1, -1, SILENCE()),
+       DEFINE_FORMAT(G723_24, 3, 3, -1, -1, SILENCE()),
+       DEFINE_FORMAT(G723_40, 5, 5, -1, -1, SILENCE()),
+       DEFINE_FORMAT(DSD_U8, 8, 8, 1, 0, SILENCE(0x69)),
+       DEFINE_FORMAT(DSD_U16_LE, 16, 16, 1, 0, SILENCE(0x69, 0x69)),
+       DEFINE_FORMAT(DSD_U32_LE, 32, 32, 1, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+       DEFINE_FORMAT(DSD_U16_BE, 16, 16, 0, 0, SILENCE(0x69, 0x69)),
+       DEFINE_FORMAT(DSD_U32_BE, 32, 32, 0, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+       DEFINE_FORMAT(S20_LE, 32, 20, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S20_BE, 32, 20, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U20_LE, 32, 20, 1, 0, SILENCE(0x00, 0x00, 0x08, 0x00)),
+       DEFINE_FORMAT(U20_BE, 32, 20, 0, 0, SILENCE(0x00, 0x08, 0x00, 0x00)),
+       DEFINE_FORMAT(S24_3LE, 24, 24, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S24_3BE, 24, 24, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U24_3LE, 24, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+       DEFINE_FORMAT(U24_3BE, 24, 24, 0, 0, SILENCE(0x80, 0x00, 0x00)),
+       DEFINE_FORMAT(S20_3LE, 24, 20, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S20_3BE, 24, 20, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U20_3LE, 24, 20, 1, 0, SILENCE(0x00, 0x00, 0x08)),
+       DEFINE_FORMAT(U20_3BE, 24, 20, 0, 0, SILENCE(0x08, 0x00, 0x00)),
+       DEFINE_FORMAT(S18_3LE, 24, 18, 1, 1, SILENCE()),
+       DEFINE_FORMAT(S18_3BE, 24, 18, 0, 1, SILENCE()),
+       DEFINE_FORMAT(U18_3LE, 24, 18, 1, 0, SILENCE(0x00, 0x00, 0x02)),
+       DEFINE_FORMAT(U18_3BE, 24, 18, 0, 0, SILENCE(0x02, 0x00, 0x00)),
+       DEFINE_FORMAT(G723_24_1B, 8, 3, -1, -1, SILENCE()),
+       DEFINE_FORMAT(G723_40_1B, 8, 5, -1, -1, SILENCE()),
+};
+
+static void test_phys_format_size(struct kunit *test)
+{
+       u32 i;
+
+       for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(valid_fmt[i].format),
+                               valid_fmt[i].physical_bits);
+       }
+
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_1), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_width(struct kunit *test)
+{
+       u32 i;
+
+       for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_width(valid_fmt[i].format),
+                               valid_fmt[i].width);
+       }
+
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_signed(struct kunit *test)
+{
+       u32 i;
+
+       for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_signed(valid_fmt[i].format),
+                               valid_fmt[i].sd < 0 ? -EINVAL : valid_fmt[i].sd);
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_unsigned(valid_fmt[i].format),
+                               valid_fmt[i].sd < 0 ? -EINVAL : 1 - valid_fmt[i].sd);
+       }
+
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_endianness(struct kunit *test)
+{
+       u32 i;
+
+       for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(valid_fmt[i].format),
+                               valid_fmt[i].le < 0 ? -EINVAL : valid_fmt[i].le);
+               KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(valid_fmt[i].format),
+                               valid_fmt[i].le < 0 ? -EINVAL : 1 - valid_fmt[i].le);
+       }
+
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_1), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_2), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_1), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *data,
+                              u8 *buffer, size_t samples_count)
+{
+       size_t sample_bytes = data->physical_bits >> 3;
+       u32 i;
+
+       KUNIT_ASSERT_EQ(test, snd_pcm_format_set_silence(data->format, buffer, samples_count), 0);
+       for (i = 0; i < samples_count * sample_bytes; i++)
+               KUNIT_EXPECT_EQ(test, buffer[i], data->silence[i % sample_bytes]);
+}
+
+static void test_format_fill_silence(struct kunit *test)
+{
+       u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES };
+       u8 *buffer;
+       u32 i, j;
+
+       buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL);
+
+       for (i = 0; i < ARRAY_SIZE(buf_samples); i++) {
+               for (j = 0; j < ARRAY_SIZE(valid_fmt); j++)
+                       _test_fill_silence(test, &valid_fmt[j], buffer, buf_samples[i]);
+       }
+
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(WRONG_FORMAT_1, buffer, 20), -EINVAL);
+       KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_LAST, buffer, 0), 0);
+}
+
+static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size)
+{
+       snd_pcm_uframes_t boundary = buffer_size;
+
+       while (boundary * 2 <= 0x7fffffffUL - buffer_size)
+               boundary *= 2;
+       return boundary;
+}
+
+static struct avail_test_data p_avail_data[] = {
+       /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */
+       { 128, 1000, 1129, 1073741824UL - 1 },
+       /*
+        * buf_size + hw_ptr - appl_ptr >= boundary =>
+        * => avail = buf_size + hw_ptr - appl_ptr - boundary
+        */
+       { 128, 1073741824UL, 10, 118 },
+       /* standard case: avail = buf_size + hw_ptr - appl_ptr */
+       { 128, 1000, 1001, 127 },
+};
+
+static void test_playback_avail(struct kunit *test)
+{
+       struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+       u32 i;
+
+       r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+       r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+
+       for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) {
+               r->buffer_size = p_avail_data[i].buffer_size;
+               r->boundary = calculate_boundary(r->buffer_size);
+               r->status->hw_ptr = p_avail_data[i].hw_ptr;
+               r->control->appl_ptr = p_avail_data[i].appl_ptr;
+               KUNIT_EXPECT_EQ(test, snd_pcm_playback_avail(r), p_avail_data[i].expected_avail);
+       }
+}
+
+static struct avail_test_data c_avail_data[] = {
+       /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */
+       { 128, 1000, 1001, 1073741824UL - 1 },
+       /* standard case: avail = hw_ptr - appl_ptr */
+       { 128, 1001, 1000, 1 },
+};
+
+static void test_capture_avail(struct kunit *test)
+{
+       struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+       u32 i;
+
+       r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+       r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+
+       for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) {
+               r->buffer_size = c_avail_data[i].buffer_size;
+               r->boundary = calculate_boundary(r->buffer_size);
+               r->status->hw_ptr = c_avail_data[i].hw_ptr;
+               r->control->appl_ptr = c_avail_data[i].appl_ptr;
+               KUNIT_EXPECT_EQ(test, snd_pcm_capture_avail(r), c_avail_data[i].expected_avail);
+       }
+}
+
+static void test_card_set_id(struct kunit *test)
+{
+       struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+
+       snd_card_set_id(card, VALID_NAME);
+       KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME);
+
+       /* clear the first id character so we can set it again */
+       card->id[0] = '\0';
+       snd_card_set_id(card, NAME_W_SPEC_CHARS);
+       KUNIT_EXPECT_STRNEQ(test, card->id, NAME_W_SPEC_CHARS);
+
+       card->id[0] = '\0';
+       snd_card_set_id(card, NAME_W_SPACE);
+       kunit_info(test, "%s", card->id);
+       KUNIT_EXPECT_STREQ(test, card->id, NAME_W_SPACE_REMOVED);
+}
+
+static void test_pcm_format_name(struct kunit *test)
+{
+       u32 i;
+       const char *name;
+
+       for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+               name = snd_pcm_format_name(valid_fmt[i].format);
+               KUNIT_ASSERT_NOT_NULL_MSG(test, name, "Don't have name for %s", valid_fmt[i].name);
+               KUNIT_EXPECT_STREQ(test, name, valid_fmt[i].name);
+       }
+
+       KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_1), "Unknown");
+       KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_2), "Unknown");
+}
+
+static void test_card_add_component(struct kunit *test)
+{
+       struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+
+       snd_component_add(card, TEST_FIRST_COMPONENT);
+       KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT);
+
+       snd_component_add(card, TEST_SECOND_COMPONENT);
+       KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT " " TEST_SECOND_COMPONENT);
+}
+
+static struct kunit_case sound_utils_cases[] = {
+       KUNIT_CASE(test_phys_format_size),
+       KUNIT_CASE(test_format_width),
+       KUNIT_CASE(test_format_endianness),
+       KUNIT_CASE(test_format_signed),
+       KUNIT_CASE(test_format_fill_silence),
+       KUNIT_CASE(test_playback_avail),
+       KUNIT_CASE(test_capture_avail),
+       KUNIT_CASE(test_card_set_id),
+       KUNIT_CASE(test_pcm_format_name),
+       KUNIT_CASE(test_card_add_component),
+       {},
+};
+
+static struct kunit_suite sound_utils_suite = {
+       .name = "sound-core-test",
+       .test_cases = sound_utils_cases,
+};
+
+kunit_test_suite(sound_utils_suite);
+MODULE_AUTHOR("Ivan Orlov");
+MODULE_LICENSE("GPL");
index 2751bf2ff61bca8dfd54125489c5ab047c45bb9b..d65cc6fee2e60ce6d10ba817e5e7fbcaf9e887e2 100644 (file)
@@ -29,7 +29,7 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
 
        if (minor >= ARRAY_SIZE(snd_oss_minors))
                return NULL;
-       mutex_lock(&sound_oss_mutex);
+       guard(mutex)(&sound_oss_mutex);
        mreg = snd_oss_minors[minor];
        if (mreg && mreg->type == type) {
                private_data = mreg->private_data;
@@ -37,7 +37,6 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
                        get_device(&mreg->card_ptr->card_dev);
        } else
                private_data = NULL;
-       mutex_unlock(&sound_oss_mutex);
        return private_data;
 }
 EXPORT_SYMBOL(snd_lookup_oss_minor_data);
@@ -106,7 +105,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
        preg->f_ops = f_ops;
        preg->private_data = private_data;
        preg->card_ptr = card;
-       mutex_lock(&sound_oss_mutex);
+       guard(mutex)(&sound_oss_mutex);
        snd_oss_minors[minor] = preg;
        minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
        switch (minor_unit) {
@@ -130,7 +129,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
                        goto __end;
                snd_oss_minors[track2] = preg;
        }
-       mutex_unlock(&sound_oss_mutex);
        return 0;
 
       __end:
@@ -139,7 +137,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
        if (register1 >= 0)
                unregister_sound_special(register1);
        snd_oss_minors[minor] = NULL;
-       mutex_unlock(&sound_oss_mutex);
        kfree(preg);
        return -EBUSY;
 }
@@ -156,12 +153,10 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
                return 0;
        if (minor < 0)
                return minor;
-       mutex_lock(&sound_oss_mutex);
+       guard(mutex)(&sound_oss_mutex);
        mptr = snd_oss_minors[minor];
-       if (mptr == NULL) {
-               mutex_unlock(&sound_oss_mutex);
+       if (mptr == NULL)
                return -ENOENT;
-       }
        switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
        case SNDRV_MINOR_OSS_PCM:
                track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
@@ -176,7 +171,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
        if (track2 >= 0)
                snd_oss_minors[track2] = NULL;
        snd_oss_minors[minor] = NULL;
-       mutex_unlock(&sound_oss_mutex);
 
        /* call unregister_sound_special() outside sound_oss_mutex;
         * otherwise may deadlock, as it can trigger the release of a card
@@ -220,7 +214,7 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
        int minor;
        struct snd_minor *mptr;
 
-       mutex_lock(&sound_oss_mutex);
+       guard(mutex)(&sound_oss_mutex);
        for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
                mptr = snd_oss_minors[minor];
                if (!mptr)
@@ -233,7 +227,6 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
                        snd_iprintf(buffer, "%3i:       : %s\n", minor,
                                    snd_oss_device_type_name(mptr->type));
        }
-       mutex_unlock(&sound_oss_mutex);
 }
 
 
index e6e551d4a29e0137a1a769576d6c1ccff3e041be..15b07d09c4b72064d1f4eaff7d41cd40c9a29d01 100644 (file)
@@ -224,14 +224,12 @@ static int check_matching_master_slave(struct snd_timer_instance *master,
                return -EBUSY;
        list_move_tail(&slave->open_list, &master->slave_list_head);
        master->timer->num_instances++;
-       spin_lock_irq(&slave_active_lock);
-       spin_lock(&master->timer->lock);
+       guard(spinlock_irq)(&slave_active_lock);
+       guard(spinlock)(&master->timer->lock);
        slave->master = master;
        slave->timer = master->timer;
        if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
                list_add_tail(&slave->active_list, &master->slave_active_head);
-       spin_unlock(&master->timer->lock);
-       spin_unlock_irq(&slave_active_lock);
        return 1;
 }
 
@@ -382,6 +380,25 @@ list_added:
 }
 EXPORT_SYMBOL(snd_timer_open);
 
+/* remove slave links, called from snd_timer_close_locked() below */
+static void remove_slave_links(struct snd_timer_instance *timeri,
+                              struct snd_timer *timer)
+{
+       struct snd_timer_instance *slave, *tmp;
+
+       guard(spinlock_irq)(&slave_active_lock);
+       guard(spinlock)(&timer->lock);
+       timeri->timer = NULL;
+       list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) {
+               list_move_tail(&slave->open_list, &snd_timer_slave_list);
+               timer->num_instances--;
+               slave->master = NULL;
+               slave->timer = NULL;
+               list_del_init(&slave->ack_list);
+               list_del_init(&slave->active_list);
+       }
+}
+
 /*
  * close a timer instance
  * call this with register_mutex down.
@@ -390,12 +407,10 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri,
                                   struct device **card_devp_to_put)
 {
        struct snd_timer *timer = timeri->timer;
-       struct snd_timer_instance *slave, *tmp;
 
        if (timer) {
-               spin_lock_irq(&timer->lock);
+               guard(spinlock)(&timer->lock);
                timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
-               spin_unlock_irq(&timer->lock);
        }
 
        if (!list_empty(&timeri->open_list)) {
@@ -418,21 +433,7 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri,
                }
                spin_unlock_irq(&timer->lock);
 
-               /* remove slave links */
-               spin_lock_irq(&slave_active_lock);
-               spin_lock(&timer->lock);
-               timeri->timer = NULL;
-               list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
-                                        open_list) {
-                       list_move_tail(&slave->open_list, &snd_timer_slave_list);
-                       timer->num_instances--;
-                       slave->master = NULL;
-                       slave->timer = NULL;
-                       list_del_init(&slave->ack_list);
-                       list_del_init(&slave->active_list);
-               }
-               spin_unlock(&timer->lock);
-               spin_unlock_irq(&slave_active_lock);
+               remove_slave_links(timeri, timer);
 
                /* slave doesn't need to release timer resources below */
                if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
@@ -459,9 +460,8 @@ void snd_timer_close(struct snd_timer_instance *timeri)
        if (snd_BUG_ON(!timeri))
                return;
 
-       mutex_lock(&register_mutex);
-       snd_timer_close_locked(timeri, &card_dev_to_put);
-       mutex_unlock(&register_mutex);
+       scoped_guard(mutex, &register_mutex)
+               snd_timer_close_locked(timeri, &card_dev_to_put);
        /* put_device() is called after unlock for avoiding deadlock */
        if (card_dev_to_put)
                put_device(card_dev_to_put);
@@ -480,15 +480,13 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
 {
        struct snd_timer * timer;
        unsigned long ret = 0;
-       unsigned long flags;
 
        if (timeri == NULL)
                return 0;
        timer = timeri->timer;
        if (timer) {
-               spin_lock_irqsave(&timer->lock, flags);
+               guard(spinlock_irqsave)(&timer->lock);
                ret = snd_timer_hw_resolution(timer);
-               spin_unlock_irqrestore(&timer->lock, flags);
        }
        return ret;
 }
@@ -532,26 +530,19 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
 {
        struct snd_timer *timer;
        int result;
-       unsigned long flags;
 
        timer = timeri->timer;
        if (!timer)
                return -EINVAL;
 
-       spin_lock_irqsave(&timer->lock, flags);
-       if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
-               result = -EINVAL;
-               goto unlock;
-       }
-       if (timer->card && timer->card->shutdown) {
-               result = -ENODEV;
-               goto unlock;
-       }
+       guard(spinlock_irqsave)(&timer->lock);
+       if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+               return -EINVAL;
+       if (timer->card && timer->card->shutdown)
+               return -ENODEV;
        if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
-                            SNDRV_TIMER_IFLG_START)) {
-               result = -EBUSY;
-               goto unlock;
-       }
+                            SNDRV_TIMER_IFLG_START))
+               return -EBUSY;
 
        if (start)
                timeri->ticks = timeri->cticks = ticks;
@@ -577,8 +568,6 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
        }
        snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
                          SNDRV_TIMER_EVENT_CONTINUE);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
        return result;
 }
 
@@ -586,53 +575,38 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
 static int snd_timer_start_slave(struct snd_timer_instance *timeri,
                                 bool start)
 {
-       unsigned long flags;
-       int err;
-
-       spin_lock_irqsave(&slave_active_lock, flags);
-       if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
-               err = -EINVAL;
-               goto unlock;
-       }
-       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
-               err = -EBUSY;
-               goto unlock;
-       }
+       guard(spinlock_irqsave)(&slave_active_lock);
+       if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+               return -EINVAL;
+       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING)
+               return -EBUSY;
        timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
        if (timeri->master && timeri->timer) {
-               spin_lock(&timeri->timer->lock);
+               guard(spinlock)(&timeri->timer->lock);
                list_add_tail(&timeri->active_list,
                              &timeri->master->slave_active_head);
                snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
                                  SNDRV_TIMER_EVENT_CONTINUE);
-               spin_unlock(&timeri->timer->lock);
        }
-       err = 1; /* delayed start */
- unlock:
-       spin_unlock_irqrestore(&slave_active_lock, flags);
-       return err;
+       return 1; /* delayed start */
 }
 
 /* stop/pause a master timer */
 static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
 {
        struct snd_timer *timer;
-       int result = 0;
-       unsigned long flags;
 
        timer = timeri->timer;
        if (!timer)
                return -EINVAL;
-       spin_lock_irqsave(&timer->lock, flags);
+       guard(spinlock_irqsave)(&timer->lock);
        list_del_init(&timeri->ack_list);
        list_del_init(&timeri->active_list);
        if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
-                              SNDRV_TIMER_IFLG_START))) {
-               result = -EBUSY;
-               goto unlock;
-       }
+                              SNDRV_TIMER_IFLG_START)))
+               return -EBUSY;
        if (timer->card && timer->card->shutdown)
-               goto unlock;
+               return 0;
        if (stop) {
                timeri->cticks = timeri->ticks;
                timeri->pticks = 0;
@@ -656,30 +630,25 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
                timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
        snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
                          SNDRV_TIMER_EVENT_PAUSE);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
-       return result;
+       return 0;
 }
 
 /* stop/pause a slave timer */
 static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
 {
-       unsigned long flags;
        bool running;
 
-       spin_lock_irqsave(&slave_active_lock, flags);
+       guard(spinlock_irqsave)(&slave_active_lock);
        running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
        timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
        if (timeri->timer) {
-               spin_lock(&timeri->timer->lock);
+               guard(spinlock)(&timeri->timer->lock);
                list_del_init(&timeri->ack_list);
                list_del_init(&timeri->active_list);
                if (running)
                        snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
                                          SNDRV_TIMER_EVENT_PAUSE);
-               spin_unlock(&timeri->timer->lock);
        }
-       spin_unlock_irqrestore(&slave_active_lock, flags);
        return running ? 0 : -EBUSY;
 }
 
@@ -804,12 +773,9 @@ static void snd_timer_process_callbacks(struct snd_timer *timer,
 static void snd_timer_clear_callbacks(struct snd_timer *timer,
                                      struct list_head *head)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&timer->lock, flags);
+       guard(spinlock_irqsave)(&timer->lock);
        while (!list_empty(head))
                list_del_init(head->next);
-       spin_unlock_irqrestore(&timer->lock, flags);
 }
 
 /*
@@ -819,16 +785,14 @@ static void snd_timer_clear_callbacks(struct snd_timer *timer,
 static void snd_timer_work(struct work_struct *work)
 {
        struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
-       unsigned long flags;
 
        if (timer->card && timer->card->shutdown) {
                snd_timer_clear_callbacks(timer, &timer->sack_list_head);
                return;
        }
 
-       spin_lock_irqsave(&timer->lock, flags);
+       guard(spinlock_irqsave)(&timer->lock);
        snd_timer_process_callbacks(timer, &timer->sack_list_head);
-       spin_unlock_irqrestore(&timer->lock, flags);
 }
 
 /*
@@ -842,8 +806,6 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
        struct snd_timer_instance *ti, *ts, *tmp;
        unsigned long resolution;
        struct list_head *ack_list_head;
-       unsigned long flags;
-       bool use_work = false;
 
        if (timer == NULL)
                return;
@@ -853,7 +815,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                return;
        }
 
-       spin_lock_irqsave(&timer->lock, flags);
+       guard(spinlock_irqsave)(&timer->lock);
 
        /* remember the current resolution */
        resolution = snd_timer_hw_resolution(timer);
@@ -919,10 +881,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
        snd_timer_process_callbacks(timer, &timer->ack_list_head);
 
        /* do we have any slow callbacks? */
-       use_work = !list_empty(&timer->sack_list_head);
-       spin_unlock_irqrestore(&timer->lock, flags);
-
-       if (use_work)
+       if (!list_empty(&timer->sack_list_head))
                queue_work(system_highpri_wq, &timer->task_work);
 }
 EXPORT_SYMBOL(snd_timer_interrupt);
@@ -988,7 +947,7 @@ static int snd_timer_free(struct snd_timer *timer)
        if (!timer)
                return 0;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        if (! list_empty(&timer->open_list_head)) {
                struct list_head *p, *n;
                struct snd_timer_instance *ti;
@@ -1000,7 +959,6 @@ static int snd_timer_free(struct snd_timer *timer)
                }
        }
        list_del(&timer->device_list);
-       mutex_unlock(&register_mutex);
 
        if (timer->private_free)
                timer->private_free(timer);
@@ -1025,7 +983,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
            !timer->hw.resolution && timer->hw.c_resolution == NULL)
                return -EINVAL;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        list_for_each_entry(timer1, &snd_timer_list, device_list) {
                if (timer1->tmr_class > timer->tmr_class)
                        break;
@@ -1046,11 +1004,9 @@ static int snd_timer_dev_register(struct snd_device *dev)
                if (timer1->tmr_subdevice < timer->tmr_subdevice)
                        continue;
                /* conflicts.. */
-               mutex_unlock(&register_mutex);
                return -EBUSY;
        }
        list_add_tail(&timer->device_list, &timer1->device_list);
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -1059,20 +1015,18 @@ static int snd_timer_dev_disconnect(struct snd_device *device)
        struct snd_timer *timer = device->device_data;
        struct snd_timer_instance *ti;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        list_del_init(&timer->device_list);
        /* wake up pending sleepers */
        list_for_each_entry(ti, &timer->open_list_head, open_list) {
                if (ti->disconnect)
                        ti->disconnect(ti);
        }
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
 void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
 {
-       unsigned long flags;
        unsigned long resolution = 0;
        struct snd_timer_instance *ti, *ts;
 
@@ -1083,7 +1037,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tst
        if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
                       event > SNDRV_TIMER_EVENT_MRESUME))
                return;
-       spin_lock_irqsave(&timer->lock, flags);
+       guard(spinlock_irqsave)(&timer->lock);
        if (event == SNDRV_TIMER_EVENT_MSTART ||
            event == SNDRV_TIMER_EVENT_MCONTINUE ||
            event == SNDRV_TIMER_EVENT_MRESUME)
@@ -1095,7 +1049,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tst
                        if (ts->ccallback)
                                ts->ccallback(ts, event, tstamp, resolution);
        }
-       spin_unlock_irqrestore(&timer->lock, flags);
 }
 EXPORT_SYMBOL(snd_timer_notify);
 
@@ -1248,7 +1201,7 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
        struct snd_timer_instance *ti;
        unsigned long resolution;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        list_for_each_entry(timer, &snd_timer_list, device_list) {
                if (timer->card && timer->card->shutdown)
                        continue;
@@ -1270,9 +1223,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
                                    timer->tmr_device, timer->tmr_subdevice);
                }
                snd_iprintf(buffer, "%s :", timer->name);
-               spin_lock_irq(&timer->lock);
-               resolution = snd_timer_hw_resolution(timer);
-               spin_unlock_irq(&timer->lock);
+               scoped_guard(spinlock_irq, &timer->lock)
+                       resolution = snd_timer_hw_resolution(timer);
                if (resolution)
                        snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
                                    resolution / 1000,
@@ -1288,7 +1240,6 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
                                                  SNDRV_TIMER_IFLG_RUNNING))
                                    ? "running" : "stopped");
        }
-       mutex_unlock(&register_mutex);
 }
 
 static struct snd_info_entry *snd_timer_proc_entry;
@@ -1329,7 +1280,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
        struct snd_timer_read *r;
        int prev;
 
-       spin_lock(&tu->qlock);
+       guard(spinlock)(&tu->qlock);
        if (tu->qused > 0) {
                prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
                r = &tu->queue[prev];
@@ -1348,7 +1299,6 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
                tu->qused++;
        }
       __wake:
-       spin_unlock(&tu->qlock);
        snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
        wake_up(&tu->qchange_sleep);
 }
@@ -1372,7 +1322,6 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
 {
        struct snd_timer_user *tu = timeri->callback_data;
        struct snd_timer_tread64 r1;
-       unsigned long flags;
 
        if (event >= SNDRV_TIMER_EVENT_START &&
            event <= SNDRV_TIMER_EVENT_PAUSE)
@@ -1384,9 +1333,8 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
        r1.tstamp_sec = tstamp->tv_sec;
        r1.tstamp_nsec = tstamp->tv_nsec;
        r1.val = resolution;
-       spin_lock_irqsave(&tu->qlock, flags);
-       snd_timer_user_append_to_tqueue(tu, &r1);
-       spin_unlock_irqrestore(&tu->qlock, flags);
+       scoped_guard(spinlock_irqsave, &tu->qlock)
+               snd_timer_user_append_to_tqueue(tu, &r1);
        snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
        wake_up(&tu->qchange_sleep);
 }
@@ -1410,51 +1358,48 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
 
        memset(&r1, 0, sizeof(r1));
        memset(&tstamp, 0, sizeof(tstamp));
-       spin_lock(&tu->qlock);
-       if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
-                          (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
-               spin_unlock(&tu->qlock);
-               return;
-       }
-       if (tu->last_resolution != resolution || ticks > 0) {
-               if (timer_tstamp_monotonic)
-                       ktime_get_ts64(&tstamp);
-               else
-                       ktime_get_real_ts64(&tstamp);
-       }
-       if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
-           tu->last_resolution != resolution) {
-               r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
+       scoped_guard(spinlock, &tu->qlock) {
+               if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
+                                  (1 << SNDRV_TIMER_EVENT_TICK))) == 0)
+                       return;
+               if (tu->last_resolution != resolution || ticks > 0) {
+                       if (timer_tstamp_monotonic)
+                               ktime_get_ts64(&tstamp);
+                       else
+                               ktime_get_real_ts64(&tstamp);
+               }
+               if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
+                   tu->last_resolution != resolution) {
+                       r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
+                       r1.tstamp_sec = tstamp.tv_sec;
+                       r1.tstamp_nsec = tstamp.tv_nsec;
+                       r1.val = resolution;
+                       snd_timer_user_append_to_tqueue(tu, &r1);
+                       tu->last_resolution = resolution;
+                       append++;
+               }
+               if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
+                       break;
+               if (ticks == 0)
+                       break;
+               if (tu->qused > 0) {
+                       prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
+                       r = &tu->tqueue[prev];
+                       if (r->event == SNDRV_TIMER_EVENT_TICK) {
+                               r->tstamp_sec = tstamp.tv_sec;
+                               r->tstamp_nsec = tstamp.tv_nsec;
+                               r->val += ticks;
+                               append++;
+                               break;
+                       }
+               }
+               r1.event = SNDRV_TIMER_EVENT_TICK;
                r1.tstamp_sec = tstamp.tv_sec;
                r1.tstamp_nsec = tstamp.tv_nsec;
-               r1.val = resolution;
+               r1.val = ticks;
                snd_timer_user_append_to_tqueue(tu, &r1);
-               tu->last_resolution = resolution;
                append++;
        }
-       if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
-               goto __wake;
-       if (ticks == 0)
-               goto __wake;
-       if (tu->qused > 0) {
-               prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
-               r = &tu->tqueue[prev];
-               if (r->event == SNDRV_TIMER_EVENT_TICK) {
-                       r->tstamp_sec = tstamp.tv_sec;
-                       r->tstamp_nsec = tstamp.tv_nsec;
-                       r->val += ticks;
-                       append++;
-                       goto __wake;
-               }
-       }
-       r1.event = SNDRV_TIMER_EVENT_TICK;
-       r1.tstamp_sec = tstamp.tv_sec;
-       r1.tstamp_nsec = tstamp.tv_nsec;
-       r1.val = ticks;
-       snd_timer_user_append_to_tqueue(tu, &r1);
-       append++;
-      __wake:
-       spin_unlock(&tu->qlock);
        if (append == 0)
                return;
        snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
@@ -1476,14 +1421,13 @@ static int realloc_user_queue(struct snd_timer_user *tu, int size)
                        return -ENOMEM;
        }
 
-       spin_lock_irq(&tu->qlock);
+       guard(spinlock_irq)(&tu->qlock);
        kfree(tu->queue);
        kfree(tu->tqueue);
        tu->queue_size = size;
        tu->queue = queue;
        tu->tqueue = tqueue;
        tu->qhead = tu->qtail = tu->qused = 0;
-       spin_unlock_irq(&tu->qlock);
 
        return 0;
 }
@@ -1519,12 +1463,12 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
        if (file->private_data) {
                tu = file->private_data;
                file->private_data = NULL;
-               mutex_lock(&tu->ioctl_lock);
-               if (tu->timeri) {
-                       snd_timer_close(tu->timeri);
-                       snd_timer_instance_free(tu->timeri);
+               scoped_guard(mutex, &tu->ioctl_lock) {
+                       if (tu->timeri) {
+                               snd_timer_close(tu->timeri);
+                               snd_timer_instance_free(tu->timeri);
+                       }
                }
-               mutex_unlock(&tu->ioctl_lock);
                snd_fasync_free(tu->fasync);
                kfree(tu->queue);
                kfree(tu->tqueue);
@@ -1559,7 +1503,7 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
 
        if (copy_from_user(&id, _tid, sizeof(id)))
                return -EFAULT;
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        if (id.dev_class < 0) {         /* first item */
                if (list_empty(&snd_timer_list))
                        snd_timer_user_zero_id(&id);
@@ -1636,7 +1580,6 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
                        snd_timer_user_zero_id(&id);
                }
        }
-       mutex_unlock(&register_mutex);
        if (copy_to_user(_tid, &id, sizeof(*_tid)))
                return -EFAULT;
        return 0;
@@ -1645,70 +1588,54 @@ 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;
+       struct snd_timer_ginfo *ginfo __free(kfree) = NULL;
        struct snd_timer_id tid;
        struct snd_timer *t;
        struct list_head *p;
-       int err = 0;
 
        ginfo = memdup_user(_ginfo, sizeof(*ginfo));
        if (IS_ERR(ginfo))
-               return PTR_ERR(ginfo);
+               return PTR_ERR(no_free_ptr(ginfo));
 
        tid = ginfo->tid;
        memset(ginfo, 0, sizeof(*ginfo));
        ginfo->tid = tid;
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        t = snd_timer_find(&tid);
-       if (t != NULL) {
-               ginfo->card = t->card ? t->card->number : -1;
-               if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
-                       ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
-               strscpy(ginfo->id, t->id, sizeof(ginfo->id));
-               strscpy(ginfo->name, t->name, sizeof(ginfo->name));
-               spin_lock_irq(&t->lock);
+       if (!t)
+               return -ENODEV;
+       ginfo->card = t->card ? t->card->number : -1;
+       if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
+               ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
+       strscpy(ginfo->id, t->id, sizeof(ginfo->id));
+       strscpy(ginfo->name, t->name, sizeof(ginfo->name));
+       scoped_guard(spinlock_irq, &t->lock)
                ginfo->resolution = snd_timer_hw_resolution(t);
-               spin_unlock_irq(&t->lock);
-               if (t->hw.resolution_min > 0) {
-                       ginfo->resolution_min = t->hw.resolution_min;
-                       ginfo->resolution_max = t->hw.resolution_max;
-               }
-               list_for_each(p, &t->open_list_head) {
-                       ginfo->clients++;
-               }
-       } else {
-               err = -ENODEV;
+       if (t->hw.resolution_min > 0) {
+               ginfo->resolution_min = t->hw.resolution_min;
+               ginfo->resolution_max = t->hw.resolution_max;
        }
-       mutex_unlock(&register_mutex);
-       if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
-               err = -EFAULT;
-       kfree(ginfo);
-       return err;
+       list_for_each(p, &t->open_list_head) {
+               ginfo->clients++;
+       }
+       if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
+               return -EFAULT;
+       return 0;
 }
 
 static int timer_set_gparams(struct snd_timer_gparams *gparams)
 {
        struct snd_timer *t;
-       int err;
 
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        t = snd_timer_find(&gparams->tid);
-       if (!t) {
-               err = -ENODEV;
-               goto _error;
-       }
-       if (!list_empty(&t->open_list_head)) {
-               err = -EBUSY;
-               goto _error;
-       }
-       if (!t->hw.set_period) {
-               err = -ENOSYS;
-               goto _error;
-       }
-       err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
-_error:
-       mutex_unlock(&register_mutex);
-       return err;
+       if (!t)
+               return -ENODEV;
+       if (!list_empty(&t->open_list_head))
+               return -EBUSY;
+       if (!t->hw.set_period)
+               return -ENOSYS;
+       return t->hw.set_period(t, gparams->period_num, gparams->period_den);
 }
 
 static int snd_timer_user_gparams(struct file *file,
@@ -1734,10 +1661,10 @@ static int snd_timer_user_gstatus(struct file *file,
        tid = gstatus.tid;
        memset(&gstatus, 0, sizeof(gstatus));
        gstatus.tid = tid;
-       mutex_lock(&register_mutex);
+       guard(mutex)(&register_mutex);
        t = snd_timer_find(&tid);
        if (t != NULL) {
-               spin_lock_irq(&t->lock);
+               guard(spinlock_irq)(&t->lock);
                gstatus.resolution = snd_timer_hw_resolution(t);
                if (t->hw.precise_resolution) {
                        t->hw.precise_resolution(t, &gstatus.resolution_num,
@@ -1746,11 +1673,9 @@ static int snd_timer_user_gstatus(struct file *file,
                        gstatus.resolution_num = gstatus.resolution;
                        gstatus.resolution_den = 1000000000uL;
                }
-               spin_unlock_irq(&t->lock);
        } else {
                err = -ENODEV;
        }
-       mutex_unlock(&register_mutex);
        if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
                err = -EFAULT;
        return err;
@@ -1804,9 +1729,8 @@ 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;
+       struct snd_timer_info *info __free(kfree) = NULL;
        struct snd_timer *t;
-       int err = 0;
 
        tu = file->private_data;
        if (!tu->timeri)
@@ -1823,13 +1747,11 @@ static int snd_timer_user_info(struct file *file,
                info->flags |= SNDRV_TIMER_FLG_SLAVE;
        strscpy(info->id, t->id, sizeof(info->id));
        strscpy(info->name, t->name, sizeof(info->name));
-       spin_lock_irq(&t->lock);
-       info->resolution = snd_timer_hw_resolution(t);
-       spin_unlock_irq(&t->lock);
+       scoped_guard(spinlock_irq, &t->lock)
+               info->resolution = snd_timer_hw_resolution(t);
        if (copy_to_user(_info, info, sizeof(*_info)))
-               err = -EFAULT;
-       kfree(info);
-       return err;
+               return -EFAULT;
+       return 0;
 }
 
 static int snd_timer_user_params(struct file *file,
@@ -1887,45 +1809,47 @@ static int snd_timer_user_params(struct file *file,
                goto _end;
        }
        snd_timer_stop(tu->timeri);
-       spin_lock_irq(&t->lock);
-       tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
-                              SNDRV_TIMER_IFLG_EXCLUSIVE|
-                              SNDRV_TIMER_IFLG_EARLY_EVENT);
-       if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
-               tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
-       if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
-               tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
-       if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
-               tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
-       spin_unlock_irq(&t->lock);
+       scoped_guard(spinlock_irq, &t->lock) {
+               tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
+                                      SNDRV_TIMER_IFLG_EXCLUSIVE|
+                                      SNDRV_TIMER_IFLG_EARLY_EVENT);
+               if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
+                       tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+               if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
+                       tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
+               if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
+                       tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
+       }
        if (params.queue_size > 0 &&
            (unsigned int)tu->queue_size != params.queue_size) {
                err = realloc_user_queue(tu, params.queue_size);
                if (err < 0)
                        goto _end;
        }
-       spin_lock_irq(&tu->qlock);
-       tu->qhead = tu->qtail = tu->qused = 0;
-       if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
-               if (tu->tread) {
-                       struct snd_timer_tread64 tread;
-                       memset(&tread, 0, sizeof(tread));
-                       tread.event = SNDRV_TIMER_EVENT_EARLY;
-                       tread.tstamp_sec = 0;
-                       tread.tstamp_nsec = 0;
-                       tread.val = 0;
-                       snd_timer_user_append_to_tqueue(tu, &tread);
-               } else {
-                       struct snd_timer_read *r = &tu->queue[0];
-                       r->resolution = 0;
-                       r->ticks = 0;
-                       tu->qused++;
-                       tu->qtail++;
+       scoped_guard(spinlock_irq, &tu->qlock) {
+               tu->qhead = tu->qtail = tu->qused = 0;
+               if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
+                       if (tu->tread) {
+                               struct snd_timer_tread64 tread;
+
+                               memset(&tread, 0, sizeof(tread));
+                               tread.event = SNDRV_TIMER_EVENT_EARLY;
+                               tread.tstamp_sec = 0;
+                               tread.tstamp_nsec = 0;
+                               tread.val = 0;
+                               snd_timer_user_append_to_tqueue(tu, &tread);
+                       } else {
+                               struct snd_timer_read *r = &tu->queue[0];
+
+                               r->resolution = 0;
+                               r->ticks = 0;
+                               tu->qused++;
+                               tu->qtail++;
+                       }
                }
+               tu->filter = params.filter;
+               tu->ticks = params.ticks;
        }
-       tu->filter = params.filter;
-       tu->ticks = params.ticks;
-       spin_unlock_irq(&tu->qlock);
        err = 0;
  _end:
        if (copy_to_user(_params, &params, sizeof(params)))
@@ -1948,9 +1872,8 @@ static int snd_timer_user_status32(struct file *file,
        status.resolution = snd_timer_resolution(tu->timeri);
        status.lost = tu->timeri->lost;
        status.overrun = tu->overrun;
-       spin_lock_irq(&tu->qlock);
-       status.queue = tu->qused;
-       spin_unlock_irq(&tu->qlock);
+       scoped_guard(spinlock_irq, &tu->qlock)
+               status.queue = tu->qused;
        if (copy_to_user(_status, &status, sizeof(status)))
                return -EFAULT;
        return 0;
@@ -1971,9 +1894,8 @@ static int snd_timer_user_status64(struct file *file,
        status.resolution = snd_timer_resolution(tu->timeri);
        status.lost = tu->timeri->lost;
        status.overrun = tu->overrun;
-       spin_lock_irq(&tu->qlock);
-       status.queue = tu->qused;
-       spin_unlock_irq(&tu->qlock);
+       scoped_guard(spinlock_irq, &tu->qlock)
+               status.queue = tu->qused;
        if (copy_to_user(_status, &status, sizeof(status)))
                return -EFAULT;
        return 0;
@@ -2131,12 +2053,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
                                 unsigned long arg)
 {
        struct snd_timer_user *tu = file->private_data;
-       long ret;
 
-       mutex_lock(&tu->ioctl_lock);
-       ret = __snd_timer_user_ioctl(file, cmd, arg, false);
-       mutex_unlock(&tu->ioctl_lock);
-       return ret;
+       guard(mutex)(&tu->ioctl_lock);
+       return __snd_timer_user_ioctl(file, cmd, arg, false);
 }
 
 static int snd_timer_user_fasync(int fd, struct file * file, int on)
@@ -2263,12 +2182,11 @@ static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
         poll_wait(file, &tu->qchange_sleep, wait);
 
        mask = 0;
-       spin_lock_irq(&tu->qlock);
+       guard(spinlock_irq)(&tu->qlock);
        if (tu->qused)
                mask |= EPOLLIN | EPOLLRDNORM;
        if (tu->disconnected)
                mask |= EPOLLERR;
-       spin_unlock_irq(&tu->qlock);
 
        return mask;
 }
index ee973b7b804499a52936100bdfcbe4235aeb4a28..4ae9eaeb5afb1a9351e4caf872f4562b298524fe 100644 (file)
@@ -115,10 +115,7 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
                                        unsigned long arg)
 {
        struct snd_timer_user *tu = file->private_data;
-       long ret;
 
-       mutex_lock(&tu->ioctl_lock);
-       ret = __snd_timer_user_ioctl_compat(file, cmd, arg);
-       mutex_unlock(&tu->ioctl_lock);
-       return ret;
+       guard(mutex)(&tu->ioctl_lock);
+       return __snd_timer_user_ioctl_compat(file, cmd, arg);
 }
index fe7911498cc4325a866a87328087c54a0a6c791a..fd6a68a5427886d11e443132a74568d933f2f163 100644 (file)
@@ -985,13 +985,11 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
        struct snd_ump_endpoint *ump = substream->rmidi->private_data;
        int dir = substream->stream;
        int group = ump->legacy_mapping[substream->number];
-       int err = 0;
+       int err;
 
-       mutex_lock(&ump->open_mutex);
-       if (ump->legacy_substreams[dir][group]) {
-               err = -EBUSY;
-               goto unlock;
-       }
+       guard(mutex)(&ump->open_mutex);
+       if (ump->legacy_substreams[dir][group])
+               return -EBUSY;
        if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
                if (!ump->legacy_out_opens) {
                        err = snd_rawmidi_kernel_open(&ump->core, 0,
@@ -999,17 +997,14 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
                                                      SNDRV_RAWMIDI_LFLG_APPEND,
                                                      &ump->legacy_out_rfile);
                        if (err < 0)
-                               goto unlock;
+                               return err;
                }
                ump->legacy_out_opens++;
                snd_ump_convert_reset(&ump->out_cvts[group]);
        }
-       spin_lock_irq(&ump->legacy_locks[dir]);
+       guard(spinlock_irq)(&ump->legacy_locks[dir]);
        ump->legacy_substreams[dir][group] = substream;
-       spin_unlock_irq(&ump->legacy_locks[dir]);
- unlock:
-       mutex_unlock(&ump->open_mutex);
-       return err;
+       return 0;
 }
 
 static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
@@ -1018,15 +1013,13 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
        int dir = substream->stream;
        int group = ump->legacy_mapping[substream->number];
 
-       mutex_lock(&ump->open_mutex);
-       spin_lock_irq(&ump->legacy_locks[dir]);
-       ump->legacy_substreams[dir][group] = NULL;
-       spin_unlock_irq(&ump->legacy_locks[dir]);
+       guard(mutex)(&ump->open_mutex);
+       scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
+               ump->legacy_substreams[dir][group] = NULL;
        if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
                if (!--ump->legacy_out_opens)
                        snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
        }
-       mutex_unlock(&ump->open_mutex);
        return 0;
 }
 
@@ -1078,12 +1071,11 @@ static int process_legacy_output(struct snd_ump_endpoint *ump,
        const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
        unsigned char c;
        int group, size = 0;
-       unsigned long flags;
 
        if (!ump->out_cvts || !ump->legacy_out_opens)
                return 0;
 
-       spin_lock_irqsave(&ump->legacy_locks[dir], flags);
+       guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
        for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
                substream = ump->legacy_substreams[dir][group];
                if (!substream)
@@ -1099,7 +1091,6 @@ static int process_legacy_output(struct snd_ump_endpoint *ump,
                        break;
                }
        }
-       spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
        return size;
 }
 
@@ -1109,18 +1100,16 @@ static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
        struct snd_rawmidi_substream *substream;
        unsigned char buf[16];
        unsigned char group;
-       unsigned long flags;
        const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
        int size;
 
        size = snd_ump_convert_from_ump(src, buf, &group);
        if (size <= 0)
                return;
-       spin_lock_irqsave(&ump->legacy_locks[dir], flags);
+       guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
        substream = ump->legacy_substreams[dir][group];
        if (substream)
                snd_rawmidi_receive(substream, buf, size);
-       spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
 }
 
 /* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
index 378d2c7c3d4af188a046feb238aeecc3179d477c..04a57f7be6ea42ae2891b70cf85f0b7048389665 100644 (file)
@@ -56,7 +56,7 @@ struct link_follower {
 
 static int follower_update(struct link_follower *follower)
 {
-       struct snd_ctl_elem_value *uctl;
+       struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
        int err, ch;
 
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
@@ -65,18 +65,16 @@ static int follower_update(struct link_follower *follower)
        uctl->id = follower->follower.id;
        err = follower->follower.get(&follower->follower, uctl);
        if (err < 0)
-               goto error;
+               return err;
        for (ch = 0; ch < follower->info.count; ch++)
                follower->vals[ch] = uctl->value.integer.value[ch];
- error:
-       kfree(uctl);
-       return err < 0 ? err : 0;
+       return 0;
 }
 
 /* get the follower ctl info and save the initial values */
 static int follower_init(struct link_follower *follower)
 {
-       struct snd_ctl_elem_info *uinfo;
+       struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
        int err;
 
        if (follower->info.count) {
@@ -91,22 +89,18 @@ static int follower_init(struct link_follower *follower)
                return -ENOMEM;
        uinfo->id = follower->follower.id;
        err = follower->follower.info(&follower->follower, uinfo);
-       if (err < 0) {
-               kfree(uinfo);
+       if (err < 0)
                return err;
-       }
        follower->info.type = uinfo->type;
        follower->info.count = uinfo->count;
        if (follower->info.count > 2  ||
            (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
             follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
                pr_err("ALSA: vmaster: invalid follower element\n");
-               kfree(uinfo);
                return -EINVAL;
        }
        follower->info.min_val = uinfo->value.integer.min;
        follower->info.max_val = uinfo->value.integer.max;
-       kfree(uinfo);
 
        return follower_update(follower);
 }
@@ -341,7 +335,7 @@ 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;
+       struct snd_ctl_elem_value *uval __free(kfree) = NULL;
 
        uval = kmalloc(sizeof(*uval), GFP_KERNEL);
        if (!uval)
@@ -353,7 +347,6 @@ static int sync_followers(struct link_master *master, int old_val, int new_val)
                master->val = new_val;
                follower_put_val(follower, uval);
        }
-       kfree(uval);
        return 0;
 }
 
index 1c65e0a3b13ce875f7416d3f63912f609080ffc0..892c4e29c0a348a9f7d87c826233d59b131c064f 100644 (file)
@@ -1830,7 +1830,6 @@ static int loopback_probe(struct platform_device *devptr)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int loopback_suspend(struct device *pdev)
 {
        struct snd_card *card = dev_get_drvdata(pdev);
@@ -1847,11 +1846,7 @@ static int loopback_resume(struct device *pdev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
-#define LOOPBACK_PM_OPS        &loopback_pm
-#else
-#define LOOPBACK_PM_OPS        NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
 
 #define SND_LOOPBACK_DRIVER    "snd_aloop"
 
@@ -1859,7 +1854,7 @@ static struct platform_driver loopback_driver = {
        .probe          = loopback_probe,
        .driver         = {
                .name   = SND_LOOPBACK_DRIVER,
-               .pm     = LOOPBACK_PM_OPS,
+               .pm     = &loopback_pm,
        },
 };
 
index 4317677ba24aafbf76ec2e3f58de0164162c3d00..52ff6ac3f743564bd9e608d318f89f4a620aab72 100644 (file)
@@ -1098,7 +1098,6 @@ static int snd_dummy_probe(struct platform_device *devptr)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_dummy_suspend(struct device *pdev)
 {
        struct snd_card *card = dev_get_drvdata(pdev);
@@ -1115,11 +1114,7 @@ static int snd_dummy_resume(struct device *pdev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
-#define SND_DUMMY_PM_OPS       &snd_dummy_pm
-#else
-#define SND_DUMMY_PM_OPS       NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
 
 #define SND_DUMMY_DRIVER       "snd_dummy"
 
@@ -1127,7 +1122,7 @@ static struct platform_driver snd_dummy_driver = {
        .probe          = snd_dummy_probe,
        .driver         = {
                .name   = SND_DUMMY_DRIVER,
-               .pm     = SND_DUMMY_PM_OPS,
+               .pm     = &snd_dummy_pm,
        },
 };
 
index c7be1c395bcb3dda1d44df4d484a795046e3685e..7195cb49e00f8c10c786f1aff408e4304d005c6e 100644 (file)
@@ -176,7 +176,6 @@ static void pcsp_stop_beep(struct snd_pcsp *chip)
        pcspkr_stop_sound();
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int pcsp_suspend(struct device *dev)
 {
        struct snd_pcsp *chip = dev_get_drvdata(dev);
@@ -184,11 +183,7 @@ static int pcsp_suspend(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
-#define PCSP_PM_OPS    &pcsp_pm
-#else
-#define PCSP_PM_OPS    NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
 
 static void pcsp_shutdown(struct platform_device *dev)
 {
@@ -199,7 +194,7 @@ static void pcsp_shutdown(struct platform_device *dev)
 static struct platform_driver pcsp_platform_driver = {
        .driver         = {
                .name   = "pcspkr",
-               .pm     = PCSP_PM_OPS,
+               .pm     = &pcsp_pm,
        },
        .probe          = pcsp_probe,
        .shutdown       = pcsp_shutdown,
index 22b6c779682a3e5a565cfebdd1b74a878fa450fd..5973c25c2add62c84df89547fad9f8598f1c3e56 100644 (file)
@@ -175,6 +175,8 @@ config SND_FIREWIRE_MOTU
          * 8pre
          * 828mk3 (FireWire only)
          * 828mk3 (Hybrid)
+         * 896mk3 (FireWire only)
+         * 896mk3 (Hybrid)
          * Ultralite mk3 (FireWire only)
          * Ultralite mk3 (Hybrid)
          * Traveler mk3
index 7be17bca257f0ddba4799875c9250f96ba4e4b8a..c9f153f85ae6b6a29214847870a5599801209f50 100644 (file)
@@ -773,10 +773,14 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
        } else {
                unsigned int dbc_interval;
 
-               if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
-                       dbc_interval = s->ctx_data.tx.dbc_interval;
-               else
-                       dbc_interval = *data_blocks;
+               if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) {
+                       if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
+                               dbc_interval = s->ctx_data.tx.dbc_interval;
+                       else
+                               dbc_interval = *data_blocks;
+               } else {
+                       dbc_interval = payload_length / sizeof(__be32);
+               }
 
                lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
        }
index b7ff44751ab93b625a154057535b532a65b46706..a1ed2e80f91a7cc07fecf55288a20a240d0900da 100644 (file)
@@ -37,6 +37,9 @@
  *     the value of current SYT_INTERVAL; e.g. initial value is not zero.
  * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff.
  *     For incoming packet, the value in SYT field of CIP is not handled.
+ * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with
+ *     CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets
+ *     in CIP payload, instead of the number of accumulated data blocks.
  */
 enum cip_flags {
        CIP_NONBLOCKING         = 0x00,
@@ -51,6 +54,7 @@ enum cip_flags {
        CIP_NO_HEADER           = 0x100,
        CIP_UNALIGHED_DBC       = 0x200,
        CIP_UNAWARE_SYT         = 0x400,
+       CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800,
 };
 
 /**
index 8a0426920a760b838e56169dcea3c42d4ce7eeb1..7254fdfe046abd21b6087a1f4e511eccb35502a6 100644 (file)
@@ -261,6 +261,7 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
 
        if (motu->spec == &snd_motu_spec_828mk3_fw ||
            motu->spec == &snd_motu_spec_828mk3_hybrid ||
+           motu->spec == &snd_motu_spec_896mk3 ||
            motu->spec == &snd_motu_spec_traveler_mk3 ||
            motu->spec == &snd_motu_spec_track16)
                return detect_packet_formats_with_opt_ifaces(motu, data);
@@ -288,6 +289,14 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
        .rx_fixed_pcm_chunks = {14, 14, 14},    // Additional 4 dummy chunks at higher rate.
 };
 
+const struct snd_motu_spec snd_motu_spec_896mk3 = {
+       .name = "896mk3",
+       .protocol_version = SND_MOTU_PROTOCOL_V3,
+       .flags = SND_MOTU_SPEC_COMMAND_DSP,
+       .tx_fixed_pcm_chunks = {18, 14, 10},
+       .rx_fixed_pcm_chunks = {18, 14, 10},
+};
+
 const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
        .name = "TravelerMk3",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
index d73599eb7d5aae304d8c2fee9d46d470d3a01712..d14ab5dd5bea42f2bf0486e417297e7804ce0f71 100644 (file)
@@ -168,10 +168,12 @@ static const struct ieee1394_device_id motu_id_table[] = {
        SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
        SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
        SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
+       SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only.
        SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
        SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3),
        SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
        SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
+       SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid.
        SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
        SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16),
        SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
index 3b1dc98a7be023231fe041611778e178bd8b8793..c66be0a89ccf174fcf298e8eacd21c084707d42b 100644 (file)
@@ -138,6 +138,7 @@ extern const struct snd_motu_spec snd_motu_spec_8pre;
 
 extern const struct snd_motu_spec snd_motu_spec_828mk3_fw;
 extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid;
+extern const struct snd_motu_spec snd_motu_spec_896mk3;
 extern const struct snd_motu_spec snd_motu_spec_traveler_mk3;
 extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
 extern const struct snd_motu_spec snd_motu_spec_audio_express;
index f4a702def3979437faf2684a77351bb2a07c7600..00f7feb91f929515254ddf80c918e2dd93db2884 100644 (file)
@@ -177,6 +177,8 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
                        flags |= CIP_JUMBO_PAYLOAD;
                if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS)
                        flags |= CIP_WRONG_DBS;
+               if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS)
+                       flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS;
        } else {
                conn = &oxfw->in_conn;
                c_dir = CMP_INPUT;
@@ -486,26 +488,57 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
                                enum avc_general_plug_dir dir,
                                struct snd_oxfw_stream_formation *formation)
 {
-       u8 *format;
-       unsigned int len;
        int err;
 
-       len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
-       format = kmalloc(len, GFP_KERNEL);
-       if (format == NULL)
-               return -ENOMEM;
+       if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+               u8 *format;
+               unsigned int len;
 
-       err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
-       if (err < 0)
-               goto end;
-       if (len < 3) {
-               err = -EIO;
-               goto end;
+               len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+               format = kmalloc(len, GFP_KERNEL);
+               if (format == NULL)
+                       return -ENOMEM;
+
+               err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+               if (err >= 0) {
+                       if (len < 3)
+                               err = -EIO;
+                       else
+                               err = snd_oxfw_stream_parse_format(format, formation);
+               }
+
+               kfree(format);
+       } else {
+               // Miglia Harmony Audio does not support Extended Stream Format Information
+               // command. Use the duplicated hard-coded format, instead.
+               unsigned int rate;
+               u8 *const *formats;
+               int i;
+
+               err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0);
+               if (err < 0)
+                       return err;
+
+               if (dir == AVC_GENERAL_PLUG_DIR_IN)
+                       formats = oxfw->rx_stream_formats;
+               else
+                       formats = oxfw->tx_stream_formats;
+
+               for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) {
+                       if (!formats[i])
+                               continue;
+
+                       err = snd_oxfw_stream_parse_format(formats[i], formation);
+                       if (err < 0)
+                               continue;
+
+                       if (formation->rate == rate)
+                               break;
+               }
+               if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+                       return -EIO;
        }
 
-       err = snd_oxfw_stream_parse_format(format, formation);
-end:
-       kfree(format);
        return err;
 }
 
@@ -515,7 +548,7 @@ end:
  * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
  * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
  */
-int snd_oxfw_stream_parse_format(u8 *format,
+int snd_oxfw_stream_parse_format(const u8 *format,
                                 struct snd_oxfw_stream_formation *formation)
 {
        unsigned int i, e, channels, type;
@@ -600,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
        unsigned int i, eid;
        int err;
 
-       /* get format at current sampling rate */
-       err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
-       if (err < 0) {
-               dev_err(&oxfw->unit->device,
-               "fail to get current stream format for isoc %s plug %d:%d\n",
-                       (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
-                       pid, err);
-               goto end;
+       // get format at current sampling rate.
+       if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+               err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+               if (err < 0) {
+                       dev_err(&oxfw->unit->device,
+                               "fail to get current stream format for isoc %s plug %d:%d\n",
+                               (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+                               pid, err);
+                       goto end;
+               }
+       } else {
+               // Miglia Harmony Audio does not support Extended Stream Format Information
+               // command. Use the hard-coded format, instead.
+               buf[0] = 0x90;
+               buf[1] = 0x40;
+               buf[2] = avc_stream_rate_table[0];
+               buf[3] = 0x00;
+               buf[4] = 0x01;
+
+               if (dir == AVC_GENERAL_PLUG_DIR_IN)
+                       buf[5] = 0x08;
+               else
+                       buf[5] = 0x02;
+
+               buf[6] = 0x06;
+
+               *len = 7;
        }
 
        /* parse and set stream format */
index 241a697ce26b2585f7271ef6e9ae0cbcadce879b..98ae0e8cba8790eead0a6fb0c3a102bb590cd13d 100644 (file)
@@ -21,6 +21,7 @@
 #define VENDOR_TASCAM          0x00022e
 #define OUI_STANTON            0x001260
 #define OUI_APOGEE             0x0003db
+#define OUI_OXFORD             0x0030e0
 
 #define MODEL_SATELLITE                0x00200f
 #define MODEL_SCS1M            0x001000
@@ -232,6 +233,11 @@ static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent
        if (err < 0)
                goto error;
 
+       if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) {
+               oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED |
+                               SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS;
+       }
+
        err = snd_oxfw_stream_discover(oxfw);
        if (err < 0)
                goto error;
@@ -330,6 +336,9 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
        //
        OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave),
        OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers),
+       // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the
+       // default value of ASIC.
+       OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL),
        // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all.
        OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL),
        // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff.
@@ -337,7 +346,6 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
        // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is
        // installed to avoid the postpone, the value of SYT field is always 0xffff.
        OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL),
-       // Miglia HarmonyAudio. Not yet identified.
 
        //
        // OXFW971 devices:
index d728e451a25c6b98818ec31634d4896d58fbcb23..39ea9a6dde33a5c5a72a26741bc65c0330f9a001 100644 (file)
@@ -52,6 +52,11 @@ enum snd_oxfw_quirk {
        // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
        // are ignored, thus driver should transfer packets with timestamp.
        SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
+       // Miglia Harmony Audio does not support AV/C Stream Format Information command.
+       SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40,
+       // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number
+       // of accumulated payload quadlets including the packet.
+       SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80,
 };
 
 /* This is an arbitrary number for convinience. */
@@ -136,7 +141,7 @@ struct snd_oxfw_stream_formation {
        unsigned int pcm;
        unsigned int midi;
 };
-int snd_oxfw_stream_parse_format(u8 *format,
+int snd_oxfw_stream_parse_format(const u8 *format,
                                 struct snd_oxfw_stream_formation *formation);
 int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
                                enum avc_general_plug_dir dir,
index 365c36fdf205854c12a6f1eda217b757f9e462bd..e9425213320eae71dbb5297c03195193fb437665 100644 (file)
@@ -127,15 +127,41 @@ static int i915_component_master_match(struct device *dev, int subcomponent,
 /* check whether Intel graphics is present and reachable */
 static int i915_gfx_present(struct pci_dev *hdac_pci)
 {
+       /* List of known platforms with no i915 support. */
+       static const struct pci_device_id denylist[] = {
+               /* CNL */
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 },
+               /* LKF */
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 },
+               {}
+       };
        struct pci_dev *display_dev = NULL;
 
        if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only()))
                return false;
 
        for_each_pci_dev(display_dev) {
-               if (display_dev->vendor == PCI_VENDOR_ID_INTEL &&
-                   (display_dev->class >> 16) == PCI_BASE_CLASS_DISPLAY &&
-                   connectivity_check(display_dev, hdac_pci)) {
+               if (display_dev->vendor != PCI_VENDOR_ID_INTEL ||
+                   (display_dev->class >> 16) != PCI_BASE_CLASS_DISPLAY)
+                       continue;
+
+               if (pci_match_id(denylist, display_dev))
+                       continue;
+
+               if (connectivity_check(display_dev, hdac_pci)) {
                        pci_dev_put(display_dev);
                        return true;
                }
index 610ea7a33cd85adac277bfeae10a97494ed82a18..b53de020309f2bc1db45b88004986f628680cd54 100644 (file)
@@ -567,7 +567,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
        return 0;
 
  error:
-       dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+       dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
                azx_dev->bufsize, period_bytes);
        return -EINVAL;
 }
index b57d72ea4503fa7504757eb6542b51e5b2e7b5e9..5f60658c6051c90bf06b42c8d39a53e3dd54c1f1 100644 (file)
@@ -23,6 +23,10 @@ static int ctrl_link_mask;
 module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
 MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
 
+static ulong ctrl_addr = 0x40000000;
+module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444);
+MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR");
+
 static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
 {
        struct fwnode_handle *link;
@@ -141,6 +145,9 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
        if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
                return AE_OK; /* keep going */
 
+       if (adr != ctrl_addr)
+               return AE_OK; /* keep going */
+
        /* found the correct SoundWire controller */
        info->handle = handle;
 
index 1d786bd5ce3e3ae8642bd96e748900baac6197a3..cd83aa864ea3e6e966140803a19818d663c49d72 100644 (file)
@@ -41,12 +41,9 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
 static void reset_tlv(struct snd_ac97 *ac97, const char *name,
                      const unsigned int *tlv)
 {
-       struct snd_ctl_elem_id sid;
        struct snd_kcontrol *kctl;
-       memset(&sid, 0, sizeof(sid));
-       strcpy(sid.name, name);
-       sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+
+       kctl = snd_ctl_find_id_mixer(ac97->bus->card, name);
        if (kctl && kctl->tlv.p)
                kctl->tlv.p = tlv;
 }
index 2378a39abaebec573a150184cf374d61636d7f7e..31e51e2df6557d6d946bca4c1b872f6e5a8905f8 100644 (file)
@@ -243,9 +243,7 @@ struct snd_ali {
        spinlock_t      reg_lock;
        spinlock_t      voice_alloc;
 
-#ifdef CONFIG_PM_SLEEP
-       struct snd_ali_image *image;
-#endif
+       struct snd_ali_image image;
 };
 
 static const struct pci_device_id snd_ali_ids[] = {
@@ -1824,18 +1822,13 @@ static int snd_ali_mixer(struct snd_ali *codec)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int ali_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct snd_ali *chip = card->private_data;
-       struct snd_ali_image *im;
+       struct snd_ali_image *im = &chip->image;
        int i, j;
 
-       im = chip->image;
-       if (!im)
-               return 0;
-
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        for (i = 0; i < chip->num_of_codecs; i++)
                snd_ac97_suspend(chip->ac97[i]);
@@ -1872,13 +1865,9 @@ static int ali_resume(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct snd_ali *chip = card->private_data;
-       struct snd_ali_image *im;
+       struct snd_ali_image *im = &chip->image;
        int i, j;
 
-       im = chip->image;
-       if (!im)
-               return 0;
-
        spin_lock_irq(&chip->reg_lock);
        
        for (i = 0; i < ALI_CHANNELS; i++) {
@@ -1908,11 +1897,7 @@ static int ali_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
-#define ALI_PM_OPS     &ali_pm
-#else
-#define ALI_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
 
 static void snd_ali_free(struct snd_card *card)
 {
@@ -2112,13 +2097,6 @@ static int snd_ali_create(struct snd_card *card,
                return err;
        }
 
-#ifdef CONFIG_PM_SLEEP
-       codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image),
-                                   GFP_KERNEL);
-       if (!codec->image)
-               dev_warn(card->dev, "can't allocate apm buffer\n");
-#endif
-
        snd_ali_enable_address_interrupt(codec);
        codec->hw_initialized = 1;
        return 0;
@@ -2181,7 +2159,7 @@ static struct pci_driver ali5451_driver = {
        .id_table = snd_ali_ids,
        .probe = snd_ali_probe,
        .driver = {
-               .pm = ALI_PM_OPS,
+               .pm = &ali_pm,
        },
 };                                
 
index c70aff0601205ede92ea2c97a2a999ed21ddbf42..c7c481203ef860a34891962d9bddac8a0e1423b2 100644 (file)
@@ -654,7 +654,6 @@ static int snd_als300_create(struct snd_card *card,
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_als300_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -677,11 +676,7 @@ static int snd_als300_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume);
-#define SND_ALS300_PM_OPS      &snd_als300_pm
-#else
-#define SND_ALS300_PM_OPS      NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume);
 
 static int snd_als300_probe(struct pci_dev *pci,
                              const struct pci_device_id *pci_id)
@@ -739,7 +734,7 @@ static struct pci_driver als300_driver = {
        .id_table = snd_als300_ids,
        .probe = snd_als300_probe,
        .driver = {
-               .pm = SND_ALS300_PM_OPS,
+               .pm = &snd_als300_pm,
        },
 };
 
index f33aeb692a112ae4e9a539b63792857c024e2f8c..022473594c73ea581e797fa93d3836e7980d8cc1 100644 (file)
@@ -936,7 +936,6 @@ static int snd_card_als4000_probe(struct pci_dev *pci,
        return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id));
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_als4000_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -968,18 +967,14 @@ static int snd_als4000_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
-#define SND_ALS4000_PM_OPS     &snd_als4000_pm
-#else
-#define SND_ALS4000_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
 
 static struct pci_driver als4000_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_als4000_ids,
        .probe = snd_card_als4000_probe,
        .driver = {
-               .pm = SND_ALS4000_PM_OPS,
+               .pm = &snd_als4000_pm,
        },
 };
 
index 43d01f1847ed7d3a8301104422bee1b3e2290ef0..df2fef726d603bbcfa15b9328bf1532fe01bba83 100644 (file)
@@ -520,7 +520,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_atiixp_aclink_down(struct atiixp *chip)
 {
        // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -530,7 +529,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
                     ATI_REG_CMD_POWERDOWN);
        return 0;
 }
-#endif
 
 /*
  * auto-detection of codecs
@@ -1454,7 +1452,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock,
 }
 
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -1499,12 +1496,7 @@ static int snd_atiixp_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
-#define SND_ATIIXP_PM_OPS      &snd_atiixp_pm
-#else
-#define SND_ATIIXP_PM_OPS      NULL
-#endif /* CONFIG_PM_SLEEP */
-
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
 
 /*
  * proc interface for register dump
@@ -1634,7 +1626,7 @@ static struct pci_driver atiixp_driver = {
        .id_table = snd_atiixp_ids,
        .probe = snd_atiixp_probe,
        .driver = {
-               .pm = SND_ATIIXP_PM_OPS,
+               .pm = &snd_atiixp_pm,
        },
 };
 
index 8864c4c3c7e136b6fc7d6f61029ac481bff35743..eb569539f322543c5a15c53d0ea7dd7211cee600 100644 (file)
@@ -496,7 +496,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
 {
        // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -506,7 +505,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
                     ATI_REG_CMD_POWERDOWN);
        return 0;
 }
-#endif
 
 /*
  * auto-detection of codecs
@@ -1094,7 +1092,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
 }
 
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -1128,11 +1125,7 @@ static int snd_atiixp_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
-#define SND_ATIIXP_PM_OPS      &snd_atiixp_pm
-#else
-#define SND_ATIIXP_PM_OPS      NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
 
 /*
  * proc interface for register dump
@@ -1258,7 +1251,7 @@ static struct pci_driver atiixp_modem_driver = {
        .id_table = snd_atiixp_ids,
        .probe = snd_atiixp_probe,
        .driver = {
-               .pm = SND_ATIIXP_PM_OPS,
+               .pm = &snd_atiixp_pm,
        },
 };
 
index b5c5a71c0ac3884f98a4b6a5636beb5ad43876f0..3a3de56b9b07a88163d4f05c742ff6636e4bcb6f 100644 (file)
 
 #define NUM_STREAM_CAPTURE_ANA 0
 
-typedef void (*snd_aw2_saa7146_it_cb) (void *);
+struct snd_pcm_substream;
+typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *);
 
 struct snd_aw2_saa7146_cb_param {
        snd_aw2_saa7146_it_cb p_it_callback;
-       void *p_callback_param;
+       struct snd_pcm_substream *p_callback_param;
 };
 
 /* definition of the chip-specific record */
index 431f0026b507e2ce35f3e6f966ad03b4efe6946d..84989c291cd773357b5cd34f79dbcf02ab3478bd 100644 (file)
@@ -295,7 +295,6 @@ struct snd_azf3328 {
         * CONFIG_PM register storage below, but that's slightly difficult. */
        u16 shadow_reg_ctrl_6AH;
 
-#ifdef CONFIG_PM_SLEEP
        /* register value containers for power management
         * Note: not always full I/O range preserved (similar to Win driver!) */
        u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
@@ -303,7 +302,6 @@ struct snd_azf3328 {
        u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
        u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
        u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
-#endif
 };
 
 static const struct pci_device_id snd_azf3328_ids[] = {
@@ -2517,7 +2515,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id));
 }
 
-#ifdef CONFIG_PM_SLEEP
 static inline void
 snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
                         unsigned long io_addr, unsigned count, u32 *saved_regs)
@@ -2633,18 +2630,14 @@ snd_azf3328_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
-#define SND_AZF3328_PM_OPS     &snd_azf3328_pm
-#else
-#define SND_AZF3328_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
 
 static struct pci_driver azf3328_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_azf3328_ids,
        .probe = snd_azf3328_probe,
        .driver = {
-               .pm = SND_AZF3328_PM_OPS,
+               .pm = &snd_azf3328_pm,
        },
 };
 
index 08e34b184780187f66922448c40761e6281d84a2..36014501f7ed2e26595deee70aa8bc3a3e078f4b 100644 (file)
@@ -486,10 +486,8 @@ struct cmipci {
 
        spinlock_t reg_lock;
 
-#ifdef CONFIG_PM_SLEEP
        unsigned int saved_regs[0x20];
        unsigned char saved_mixers[0x20];
-#endif
 };
 
 
@@ -3260,7 +3258,6 @@ static int snd_cmipci_probe(struct pci_dev *pci,
        return err;
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -3324,18 +3321,14 @@ static int snd_cmipci_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume);
-#define SND_CMIPCI_PM_OPS      &snd_cmipci_pm
-#else
-#define SND_CMIPCI_PM_OPS      NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume);
 
 static struct pci_driver cmipci_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cmipci_ids,
        .probe = snd_cmipci_probe,
        .driver = {
-               .pm = SND_CMIPCI_PM_OPS,
+               .pm = &snd_cmipci_pm,
        },
 };
        
index 0c9cadf7b3b802f99ad9d820f3e667c0952c41ab..0cc86e73cc62ec416fb5e4df9875956c382e67fd 100644 (file)
@@ -470,10 +470,7 @@ struct cs4281 {
 
        struct gameport *gameport;
 
-#ifdef CONFIG_PM_SLEEP
        u32 suspend_regs[SUSPEND_REGISTERS];
-#endif
-
 };
 
 static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);
@@ -1897,8 +1894,6 @@ static int snd_cs4281_probe(struct pci_dev *pci,
 /*
  * Power Management
  */
-#ifdef CONFIG_PM_SLEEP
-
 static const int saved_regs[SUSPEND_REGISTERS] = {
        BA0_JSCTL,
        BA0_GPIOR,
@@ -1987,18 +1982,14 @@ static int cs4281_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume);
-#define CS4281_PM_OPS  &cs4281_pm
-#else
-#define CS4281_PM_OPS  NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume);
 
 static struct pci_driver cs4281_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cs4281_ids,
        .probe = snd_cs4281_probe,
        .driver = {
-               .pm = CS4281_PM_OPS,
+               .pm = &cs4281_pm,
        },
 };
        
index d074727c3e21d3edf25f934b259e441a83738b1f..397900929aa659057bf5244154f47cd2dc56c887 100644 (file)
@@ -292,7 +292,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
        return 0;
 }
 
-int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr)
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr)
 {
        int err;
        struct amixer_mgr *amixer_mgr;
@@ -321,8 +321,9 @@ error:
        return err;
 }
 
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+int amixer_mgr_destroy(void *ptr)
 {
+       struct amixer_mgr *amixer_mgr = ptr;
        rsc_mgr_uninit(&amixer_mgr->mgr);
        kfree(amixer_mgr);
        return 0;
@@ -446,7 +447,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
        return 0;
 }
 
-int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr)
+int sum_mgr_create(struct hw *hw, void **rsum_mgr)
 {
        int err;
        struct sum_mgr *sum_mgr;
@@ -475,8 +476,9 @@ error:
        return err;
 }
 
-int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+int sum_mgr_destroy(void *ptr)
 {
+       struct sum_mgr *sum_mgr = ptr;
        rsc_mgr_uninit(&sum_mgr->mgr);
        kfree(sum_mgr);
        return 0;
index 4498e6139d0efa89c797f9f259b6a58cbec97cd6..8fc017da6bda8be8604222059108a31a42001ac1 100644 (file)
@@ -43,8 +43,8 @@ struct sum_mgr {
 };
 
 /* Constructor and destructor of daio resource manager */
-int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr);
-int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+int sum_mgr_create(struct hw *hw, void **ptr);
+int sum_mgr_destroy(void *ptr);
 
 /* Define the descriptor of a amixer resource */
 struct amixer_rsc_ops;
@@ -89,7 +89,7 @@ struct amixer_mgr {
 };
 
 /* Constructor and destructor of amixer resource manager */
-int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr);
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr);
+int amixer_mgr_destroy(void *amixer_mgr);
 
 #endif /* CTAMIXER_H */
index fbdb8a3d5b8e559dba742a1cd282b14408a90d00..2a3e9d8ba7dbe71aa5222d9242e0702f0e7aa398 100644 (file)
@@ -105,23 +105,20 @@ static struct {
                            .public_name = "Mixer"}
 };
 
-typedef int (*create_t)(struct hw *, void **);
-typedef int (*destroy_t)(void *);
-
 static struct {
        int (*create)(struct hw *hw, void **rmgr);
        int (*destroy)(void *mgr);
 } rsc_mgr_funcs[NUM_RSCTYP] = {
-       [SRC]           = { .create     = (create_t)src_mgr_create,
-                           .destroy    = (destroy_t)src_mgr_destroy    },
-       [SRCIMP]        = { .create     = (create_t)srcimp_mgr_create,
-                           .destroy    = (destroy_t)srcimp_mgr_destroy },
-       [AMIXER]        = { .create     = (create_t)amixer_mgr_create,
-                           .destroy    = (destroy_t)amixer_mgr_destroy },
-       [SUM]           = { .create     = (create_t)sum_mgr_create,
-                           .destroy    = (destroy_t)sum_mgr_destroy    },
-       [DAIO]          = { .create     = (create_t)daio_mgr_create,
-                           .destroy    = (destroy_t)daio_mgr_destroy   }
+       [SRC]           = { .create     = src_mgr_create,
+                           .destroy    = src_mgr_destroy       },
+       [SRCIMP]        = { .create     = srcimp_mgr_create,
+                           .destroy    = srcimp_mgr_destroy    },
+       [AMIXER]        = { .create     = amixer_mgr_create,
+                           .destroy    = amixer_mgr_destroy    },
+       [SUM]           = { .create     = sum_mgr_create,
+                           .destroy    = sum_mgr_destroy       },
+       [DAIO]          = { .create     = daio_mgr_create,
+                           .destroy    = daio_mgr_destroy      }
 };
 
 static int
index 7fc720046ce293564e513ad78fccda687f45aa38..83aaf9441ef30f0f49cda656ff17aaf8be31859a 100644 (file)
@@ -684,7 +684,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr)
        return 0;
 }
 
-int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr)
+int daio_mgr_create(struct hw *hw, void **rdaio_mgr)
 {
        int err, i;
        struct daio_mgr *daio_mgr;
@@ -738,8 +738,9 @@ error1:
        return err;
 }
 
-int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+int daio_mgr_destroy(void *ptr)
 {
+       struct daio_mgr *daio_mgr = ptr;
        unsigned long flags;
 
        /* free daio input mapper list */
index bd6310f48013901ca0e562f5fe52eca386fc2c52..15147fe5f74a0b21a253d216762d1369fd5fef4f 100644 (file)
@@ -115,7 +115,7 @@ struct daio_mgr {
 };
 
 /* Constructor and destructor of daio resource manager */
-int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr);
-int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+int daio_mgr_create(struct hw *hw, void **ptr);
+int daio_mgr_destroy(void *ptr);
 
 #endif /* CTDAIO_H */
index 4a94b4708a77e55f9ee81315b1d0f725c8cc3a33..159bd40080691715d71fb91bdedc74fe41440cc2 100644 (file)
@@ -540,7 +540,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr)
        return 0;
 }
 
-int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr)
+int src_mgr_create(struct hw *hw, void **rsrc_mgr)
 {
        int err, i;
        struct src_mgr *src_mgr;
@@ -580,8 +580,9 @@ error1:
        return err;
 }
 
-int src_mgr_destroy(struct src_mgr *src_mgr)
+int src_mgr_destroy(void *ptr)
 {
+       struct src_mgr *src_mgr = ptr;
        rsc_mgr_uninit(&src_mgr->mgr);
        kfree(src_mgr);
 
@@ -821,7 +822,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
        return err;
 }
 
-int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr)
+int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr)
 {
        int err;
        struct srcimp_mgr *srcimp_mgr;
@@ -866,8 +867,9 @@ error1:
        return err;
 }
 
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+int srcimp_mgr_destroy(void *ptr)
 {
+       struct srcimp_mgr *srcimp_mgr = ptr;
        unsigned long flags;
 
        /* free src input mapper list */
index 1124daf50c9bea174296ad76714b0fc8bcbf9650..e6366cc6a7ae6aaaca8ae157b86514e7e8759c2d 100644 (file)
@@ -139,10 +139,10 @@ struct srcimp_mgr {
 };
 
 /* Constructor and destructor of SRC resource manager */
-int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr);
-int src_mgr_destroy(struct src_mgr *src_mgr);
+int src_mgr_create(struct hw *hw, void **ptr);
+int src_mgr_destroy(void *ptr);
 /* Constructor and destructor of SRCIMP resource manager */
-int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr);
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+int srcimp_mgr_create(struct hw *hw, void **ptr);
+int srcimp_mgr_destroy(void *ptr);
 
 #endif /* CTSRC_H */
index c70c3ac4e99a530d847f240c178d58dc4138a286..7484de255a3eda85b3dd5d20c4e6274ae7045da4 100644 (file)
@@ -34,7 +34,6 @@ static int get_firmware(const struct firmware **fw_entry,
        int err;
        char name[30];
 
-#ifdef CONFIG_PM_SLEEP
        if (chip->fw_cache[fw_index]) {
                dev_dbg(chip->card->dev,
                        "firmware requested: %s is cached\n",
@@ -42,7 +41,6 @@ static int get_firmware(const struct firmware **fw_entry,
                *fw_entry = chip->fw_cache[fw_index];
                return 0;
        }
-#endif
 
        dev_dbg(chip->card->dev,
                "firmware requested: %s\n", card_fw[fw_index].data);
@@ -51,10 +49,8 @@ static int get_firmware(const struct firmware **fw_entry,
        if (err < 0)
                dev_err(chip->card->dev,
                        "get_firmware(): Firmware not available (%d)\n", err);
-#ifdef CONFIG_PM_SLEEP
        else
                chip->fw_cache[fw_index] = *fw_entry;
-#endif
        return err;
 }
 
@@ -63,18 +59,13 @@ static int get_firmware(const struct firmware **fw_entry,
 static void free_firmware(const struct firmware *fw_entry,
                          struct echoaudio *chip)
 {
-#ifdef CONFIG_PM_SLEEP
        dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
-#else
-       release_firmware(fw_entry);
-#endif
 }
 
 
 
 static void free_firmware_cache(struct echoaudio *chip)
 {
-#ifdef CONFIG_PM_SLEEP
        int i;
 
        for (i = 0; i < 8 ; i++)
@@ -82,8 +73,6 @@ static void free_firmware_cache(struct echoaudio *chip)
                        release_firmware(chip->fw_cache[i]);
                        dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
                }
-
-#endif
 }
 
 
@@ -2146,8 +2135,6 @@ static int snd_echo_probe(struct pci_dev *pci,
 }
 
 
-#if defined(CONFIG_PM_SLEEP)
-
 static int snd_echo_suspend(struct device *dev)
 {
        struct echoaudio *chip = dev_get_drvdata(dev);
@@ -2237,11 +2224,7 @@ static int snd_echo_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
-#define SND_ECHO_PM_OPS        &snd_echo_pm
-#else
-#define SND_ECHO_PM_OPS        NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
 
 /******************************************************************************
        Everything starts and ends here
@@ -2253,7 +2236,7 @@ static struct pci_driver echo_driver = {
        .id_table = snd_echo_ids,
        .probe = snd_echo_probe,
        .driver = {
-               .pm = SND_ECHO_PM_OPS,
+               .pm = &snd_echo_pm,
        },
 };
 
index d51de3e4edfa8949bfd7bd754462a214604bc357..511f2fcc0fb92b395174b26aedbcf9cf0435b30f 100644 (file)
@@ -422,9 +422,7 @@ struct echoaudio {
        u32 __iomem *dsp_registers;             /* DSP's register base */
        u32 active_mask;                        /* Chs. active mask or
                                                 * punks out */
-#ifdef CONFIG_PM_SLEEP
        const struct firmware *fw_cache[8];     /* Cached firmwares */
-#endif
 
 #ifdef ECHOCARD_HAS_MIDI
        u16 mtc_state;                          /* State for MIDI input parsing state machine */
index cc3c79387194fea46c82582eefd5db952486c23e..18b4d4b4d38df04e4eb79813ade0f451c8a63c2d 100644 (file)
@@ -274,7 +274,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
                       chip->digital_mode == DIGITAL_MODE_ADAT))
                return -EINVAL;
 
-       clock = 0;
        control_reg = le32_to_cpu(chip->comm_page->control_register);
        control_reg &= E3G_CLOCK_CLEAR_MASK;
 
index 89210b2c734241c940381c35817fb8abf703bb30..18928b905939cf82afa404d699ab0abcd10b2a92 100644 (file)
@@ -1968,7 +1968,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
        outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int snd_ensoniq_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -2007,11 +2006,7 @@ static int snd_ensoniq_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume);
-#define SND_ENSONIQ_PM_OPS     &snd_ensoniq_pm
-#else
-#define SND_ENSONIQ_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume);
 
 static int snd_ensoniq_create(struct snd_card *card,
                              struct pci_dev *pci)
@@ -2380,7 +2375,7 @@ static struct pci_driver ens137x_driver = {
        .id_table = snd_audiopci_ids,
        .probe = snd_audiopci_probe,
        .driver = {
-               .pm = SND_ENSONIQ_PM_OPS,
+               .pm = &snd_ensoniq_pm,
        },
 };
        
index ec598ba1a8833fa982ac5db8b87aa9dd5601caf1..018a8d53ca53a43b6a3d76aa2fa91519e9c839d3 100644 (file)
@@ -216,9 +216,7 @@ struct es1938 {
 #ifdef SUPPORT_JOYSTICK
        struct gameport *gameport;
 #endif
-#ifdef CONFIG_PM_SLEEP
        unsigned char saved_regs[SAVED_REG_SIZE];
-#endif
 };
 
 static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
@@ -1395,7 +1393,6 @@ static void snd_es1938_chip_init(struct es1938 *chip)
        outb(0, SLDM_REG(chip, DMACLEAR));
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * PM support
  */
@@ -1461,11 +1458,7 @@ static int es1938_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);
-#define ES1938_PM_OPS  &es1938_pm
-#else
-#define ES1938_PM_OPS  NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);
 
 #ifdef SUPPORT_JOYSTICK
 static int snd_es1938_create_gameport(struct es1938 *chip)
@@ -1787,7 +1780,7 @@ static struct pci_driver es1938_driver = {
        .id_table = snd_es1938_ids,
        .probe = snd_es1938_probe,
        .driver = {
-               .pm = ES1938_PM_OPS,
+               .pm = &es1938_pm,
        },
 };
 
index 4bc0f53c223b7949cf9c33acee56166da301114b..c6c018b40c69f9bd1af9c4d361cb6258ae01b077 100644 (file)
@@ -473,9 +473,7 @@ struct esschan {
        /* linked list */
        struct list_head list;
 
-#ifdef CONFIG_PM_SLEEP
        u16 wc_map[4];
-#endif
 };
 
 struct es1968 {
@@ -526,9 +524,7 @@ struct es1968 {
        struct list_head substream_list;
        spinlock_t substream_lock;
 
-#ifdef CONFIG_PM_SLEEP
        u16 apu_map[NR_APUS][NR_APU_REGS];
-#endif
 
 #ifdef SUPPORT_JOYSTICK
        struct gameport *gameport;
@@ -689,9 +685,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat
 {
        if (snd_BUG_ON(channel >= NR_APUS))
                return;
-#ifdef CONFIG_PM_SLEEP
        chip->apu_map[channel][reg] = data;
-#endif
        reg |= (channel << 4);
        apu_index_set(chip, reg);
        apu_data_set(chip, data);
@@ -976,9 +970,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es
        /* set the wavecache control reg */
        wave_set_register(chip, es->apu[channel] << 3, tmpval);
 
-#ifdef CONFIG_PM_SLEEP
        es->wc_map[channel] = tmpval;
-#endif
 }
 
 
@@ -2356,7 +2348,6 @@ static void snd_es1968_start_irq(struct es1968 *chip)
        outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * PM support
  */
@@ -2418,11 +2409,7 @@ static int es1968_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume);
-#define ES1968_PM_OPS  &es1968_pm
-#else
-#define ES1968_PM_OPS  NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume);
 
 #ifdef SUPPORT_JOYSTICK
 #define JOYSTICK_ADDR  0x200
@@ -2852,7 +2839,7 @@ static struct pci_driver es1968_driver = {
        .id_table = snd_es1968_ids,
        .probe = snd_es1968_probe,
        .driver = {
-               .pm = ES1968_PM_OPS,
+               .pm = &es1968_pm,
        },
 };
 
index 62b3cb126c6d01291ab3ea811855c9410747f0df..7f4834c2d5e6b43b2339e8626f8a799637b2f997 100644 (file)
@@ -222,9 +222,7 @@ struct fm801 {
        struct snd_tea575x tea;
 #endif
 
-#ifdef CONFIG_PM_SLEEP
        u16 saved_regs[0x20];
-#endif
 };
 
 /*
@@ -1339,7 +1337,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
        return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id));
 }
 
-#ifdef CONFIG_PM_SLEEP
 static const unsigned char saved_regs[] = {
        FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
        FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
@@ -1396,18 +1393,14 @@ static int snd_fm801_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
-#define SND_FM801_PM_OPS       &snd_fm801_pm
-#else
-#define SND_FM801_PM_OPS       NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
 
 static struct pci_driver fm801_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_fm801_ids,
        .probe = snd_card_fm801_probe,
        .driver = {
-               .pm = SND_FM801_PM_OPS,
+               .pm = &snd_fm801_pm,
        },
 };
 
index 8e0ff70fb6101ff9b94aab0d79c4b6f7cf90498d..f806636242ee902c4fd5ac58d5d60aa0b6befd89 100644 (file)
@@ -116,6 +116,9 @@ config SND_HDA_CS_DSP_CONTROLS
        tristate
        select FW_CS_DSP
 
+config SND_HDA_SCODEC_COMPONENT
+       tristate
+
 config SND_HDA_SCODEC_CS35L41_I2C
        tristate "Build CS35L41 HD-audio side codec support for I2C Bus"
        depends on I2C
@@ -162,6 +165,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
        select SND_HDA_SCODEC_CS35L56
        select SND_HDA_CIRRUS_SCODEC
        select SND_HDA_CS_DSP_CONTROLS
+       select SND_SOC_CS_AMP_LIB
        help
          Say Y or M here to include CS35L56 amplifier support with
          I2C control.
@@ -177,6 +181,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
        select SND_HDA_SCODEC_CS35L56
        select SND_HDA_CIRRUS_SCODEC
        select SND_HDA_CS_DSP_CONTROLS
+       select SND_SOC_CS_AMP_LIB
        help
          Say Y or M here to include CS35L56 amplifier support with
          SPI control.
@@ -201,6 +206,7 @@ config SND_HDA_CODEC_REALTEK
        tristate "Build Realtek HD-audio codec support"
        select SND_HDA_GENERIC
        select SND_HDA_GENERIC_LEDS
+       select SND_HDA_SCODEC_COMPONENT
        help
          Say Y or M here to include Realtek HD-audio codec support in
          snd-hda-intel driver, such as ALC880.
index 793e296c3f64217b73eb58cefb902cb7caa094f6..13e04e1f65de2f661f79214a0f0a1d2bdba70965 100644 (file)
@@ -37,6 +37,7 @@ snd-hda-scodec-cs35l56-objs :=                cs35l56_hda.o
 snd-hda-scodec-cs35l56-i2c-objs :=     cs35l56_hda_i2c.o
 snd-hda-scodec-cs35l56-spi-objs :=     cs35l56_hda_spi.o
 snd-hda-cs-dsp-ctls-objs :=            hda_cs_dsp_ctl.o
+snd-hda-scodec-component-objs :=       hda_component.o
 snd-hda-scodec-tas2781-i2c-objs :=     tas2781_hda_i2c.o
 
 # common driver
@@ -67,6 +68,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o
 obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o
 obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
 obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
+obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
 obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
 
 # this must be the last entry after codec drivers;
index e436d4dab317f05c20a8a806f14e07580bfcb2de..72ec872afb8d27de1d2b23288988bf4a5e4f4b88 100644 (file)
@@ -51,19 +51,30 @@ static const struct cs35l41_config cs35l41_config_table[] = {
        { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 },
        { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
-       { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
-       { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
-       { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
        { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+       { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+       { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+       { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+       { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 },
        { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
        { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
        { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
@@ -210,6 +221,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
        struct spi_device *spi;
        bool dsd_found;
        int ret;
+       int i;
 
        for (cfg = cs35l41_config_table; cfg->ssid; cfg++) {
                if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id))
@@ -295,16 +307,6 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
                        cs35l41->index = id == 0x40 ? 0 : 1;
        }
 
-       if (cfg->num_amps == 3)
-               /* 3 amps means a center channel, so no duplicate channels */
-               cs35l41->channel_index = 0;
-       else
-               /*
-                * if 4 amps, there are duplicate channels, so they need different indexes
-                * if 2 amps, no duplicate channels, channel_index would be 0
-                */
-               cs35l41->channel_index = cs35l41->index / 2;
-
        cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset",
                                                     cs35l41->index, GPIOD_OUT_LOW,
                                                     "cs35l41-reset");
@@ -312,6 +314,11 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
 
        hw_cfg->spk_pos = cfg->channel[cs35l41->index];
 
+       cs35l41->channel_index = 0;
+       for (i = 0; i < cs35l41->index; i++)
+               if (cfg->channel[i] == hw_cfg->spk_pos)
+                       cs35l41->channel_index++;
+
        if (cfg->boost_type == INTERNAL) {
                hw_cfg->bst_type = CS35L41_INT_BOOST;
                hw_cfg->bst_ind = cfg->boost_ind_nanohenry;
@@ -335,6 +342,42 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
        return 0;
 }
 
+/*
+ * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has
+ * its own speaker id.
+ */
+static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+                                     const char *hid)
+{
+       struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+       /* If _DSD exists for this laptop, we cannot support it through here */
+       if (acpi_dev_has_props(cs35l41->dacpi))
+               return -ENOENT;
+
+       /* check I2C address to assign the index */
+       cs35l41->index = id == 0x40 ? 0 : 1;
+       cs35l41->channel_index = 0;
+       cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+       if (cs35l41->index == 0)
+               cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1);
+       else
+               cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
+       hw_cfg->spk_pos = cs35l41->index;
+       hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+       hw_cfg->gpio2.valid = true;
+       hw_cfg->valid = true;
+
+       hw_cfg->bst_type = CS35L41_INT_BOOST;
+       hw_cfg->bst_ind = 1000;
+       hw_cfg->bst_ipk = 4100;
+       hw_cfg->bst_cap = 24;
+       hw_cfg->gpio1.func = CS35L41_NOT_USED;
+       hw_cfg->gpio1.valid = true;
+
+       return 0;
+}
+
 /*
  * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
  * And devices created by serial-multi-instantiate don't have their device struct
@@ -392,19 +435,34 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
        { "CSC3551", "103C8A2E", generic_dsd_config },
        { "CSC3551", "103C8A30", generic_dsd_config },
        { "CSC3551", "103C8A31", generic_dsd_config },
+       { "CSC3551", "103C8A6E", generic_dsd_config },
        { "CSC3551", "103C8BB3", generic_dsd_config },
        { "CSC3551", "103C8BB4", generic_dsd_config },
+       { "CSC3551", "103C8BDD", generic_dsd_config },
+       { "CSC3551", "103C8BDE", generic_dsd_config },
        { "CSC3551", "103C8BDF", generic_dsd_config },
        { "CSC3551", "103C8BE0", generic_dsd_config },
        { "CSC3551", "103C8BE1", generic_dsd_config },
        { "CSC3551", "103C8BE2", generic_dsd_config },
-       { "CSC3551", "103C8BE9", generic_dsd_config },
-       { "CSC3551", "103C8BDD", generic_dsd_config },
-       { "CSC3551", "103C8BDE", generic_dsd_config },
        { "CSC3551", "103C8BE3", generic_dsd_config },
        { "CSC3551", "103C8BE5", generic_dsd_config },
        { "CSC3551", "103C8BE6", generic_dsd_config },
+       { "CSC3551", "103C8BE7", generic_dsd_config },
+       { "CSC3551", "103C8BE8", generic_dsd_config },
+       { "CSC3551", "103C8BE9", generic_dsd_config },
        { "CSC3551", "103C8B3A", generic_dsd_config },
+       { "CSC3551", "103C8C15", generic_dsd_config },
+       { "CSC3551", "103C8C16", generic_dsd_config },
+       { "CSC3551", "103C8C17", generic_dsd_config },
+       { "CSC3551", "103C8C4F", generic_dsd_config },
+       { "CSC3551", "103C8C50", generic_dsd_config },
+       { "CSC3551", "103C8C51", generic_dsd_config },
+       { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid },
+       { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid },
+       { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid },
+       { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid },
+       { "CSC3551", "103C8CDD", generic_dsd_config },
+       { "CSC3551", "103C8CDE", generic_dsd_config },
        { "CSC3551", "104312AF", generic_dsd_config },
        { "CSC3551", "10431433", generic_dsd_config },
        { "CSC3551", "10431463", generic_dsd_config },
index 75a14ba54fcd1c270459b47be66ecb6aa799aa33..41974b3897a723ccd429a8e51032245d35ec2e48 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
+#include <sound/cs-amp-lib.h>
 #include <sound/hda_codec.h>
 #include <sound/tlv.h>
 #include "cirrus_scodec.h"
@@ -458,13 +459,15 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
 
        if (preloaded_fw_ver) {
                snprintf(base_name, sizeof(base_name),
-                        "cirrus/cs35l56-%02x%s-%06x-dsp1-misc",
+                        "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc",
+                        cs35l56->base.type,
                         cs35l56->base.rev,
                         cs35l56->base.secured ? "-s" : "",
                         preloaded_fw_ver & 0xffffff);
        } else {
                snprintf(base_name, sizeof(base_name),
-                        "cirrus/cs35l56-%02x%s-dsp1-misc",
+                        "cirrus/cs35l%02x-%02x%s-dsp1-misc",
+                        cs35l56->base.type,
                         cs35l56->base.rev,
                         cs35l56->base.secured ? "-s" : "");
        }
@@ -547,6 +550,22 @@ static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
        hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info);
 }
 
+static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
+{
+       int ret;
+
+       if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
+               return;
+
+       ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
+                                     &cs35l56_calibration_controls,
+                                     &cs35l56->base.cal_data);
+       if (ret < 0)
+               dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
+       else
+               dev_info(cs35l56->base.dev, "Calibration applied\n");
+}
+
 static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
 {
        const struct firmware *coeff_firmware = NULL;
@@ -618,12 +637,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
        if (coeff_filename)
                dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
 
-       if (!firmware_missing) {
-               ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
-               if (ret)
-                       goto err_powered_up;
-       } else if (wmfw_firmware || coeff_firmware) {
-               /* If we downloaded firmware, reset the device and wait for it to boot */
+       /* If we downloaded firmware, reset the device and wait for it to boot */
+       if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
                cs35l56_system_reset(&cs35l56->base, false);
                regcache_mark_dirty(cs35l56->base.regmap);
                ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
@@ -646,6 +661,11 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
        if (ret)
                dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
 
+       cs35l56_hda_apply_calibration(cs35l56);
+       ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+       if (ret)
+               cs_dsp_stop(&cs35l56->cs_dsp);
+
 err_powered_up:
        if (!cs35l56->base.fw_patched)
                cs_dsp_power_down(&cs35l56->cs_dsp);
@@ -834,9 +854,10 @@ static int cs35l56_hda_system_resume(struct device *dev)
        return 0;
 }
 
-static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
+static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id)
 {
        u32 values[HDA_MAX_COMPONENTS];
+       char hid_string[8];
        struct acpi_device *adev;
        const char *property, *sub;
        size_t nval;
@@ -847,7 +868,8 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
         * the serial-multi-instantiate driver, so lookup the node by HID
         */
        if (!ACPI_COMPANION(cs35l56->base.dev)) {
-               adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1);
+               snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid);
+               adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1);
                if (!adev) {
                        dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
                                dev_name(cs35l56->base.dev));
@@ -935,14 +957,14 @@ err:
        return ret;
 }
 
-int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
 {
        int ret;
 
        mutex_init(&cs35l56->base.irq_lock);
        dev_set_drvdata(cs35l56->base.dev, cs35l56);
 
-       ret = cs35l56_hda_read_acpi(cs35l56, id);
+       ret = cs35l56_hda_read_acpi(cs35l56, hid, id);
        if (ret)
                goto err;
 
@@ -953,6 +975,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
                goto err;
        }
 
+       cs35l56->base.cal_index = cs35l56->index;
+
        cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
        cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
 
@@ -990,6 +1014,10 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
        if (ret)
                goto err;
 
+       ret = cs35l56_get_calibration(&cs35l56->base);
+       if (ret)
+               goto err;
+
        ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
        if (ret) {
                dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
@@ -1064,10 +1092,11 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
 EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
 
 MODULE_DESCRIPTION("CS35L56 HDA Driver");
+MODULE_IMPORT_NS(FW_CS_DSP);
 MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
 MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
 MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
 MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
 MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(FW_CS_DSP);
index 6e5bc5397db5f894863608b74e71c2e245433338..464e4aa63cd1b18c380571031a6148733eb4edb4 100644 (file)
@@ -42,7 +42,7 @@ struct cs35l56_hda {
 
 extern const struct dev_pm_ops cs35l56_hda_pm_ops;
 
-int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id);
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);
 void cs35l56_hda_remove(struct device *dev);
 
 #endif /*__CS35L56_HDA_H__*/
index a9ef6d86de8397ece6aaebce969199294f39ce32..13beee807308f1763145cc1f9c1590e427236dc6 100644 (file)
@@ -13,6 +13,7 @@
 
 static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
 {
+       const struct i2c_device_id *id = i2c_client_get_device_id(clt);
        struct cs35l56_hda *cs35l56;
        int ret;
 
@@ -33,7 +34,7 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
                return ret;
        }
 
-       ret = cs35l56_hda_common_probe(cs35l56, clt->addr);
+       ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr);
        if (ret)
                return ret;
        ret = cs35l56_irq_request(&cs35l56->base, clt->irq);
@@ -49,7 +50,9 @@ static void cs35l56_hda_i2c_remove(struct i2c_client *clt)
 }
 
 static const struct i2c_device_id cs35l56_hda_i2c_id[] = {
-       { "cs35l56-hda", 0 },
+       { "cs35l54-hda", 0x3554 },
+       { "cs35l56-hda", 0x3556 },
+       { "cs35l57-hda", 0x3557 },
        {}
 };
 
index 080426de90830eb148eacf08f41bea1b5d57205a..a3b2fa76663d3685cf404e59785cdc00c502e493 100644 (file)
@@ -13,6 +13,7 @@
 
 static int cs35l56_hda_spi_probe(struct spi_device *spi)
 {
+       const struct spi_device_id *id = spi_get_device_id(spi);
        struct cs35l56_hda *cs35l56;
        int ret;
 
@@ -33,7 +34,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
                return ret;
        }
 
-       ret = cs35l56_hda_common_probe(cs35l56, spi_get_chipselect(spi, 0));
+       ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0));
        if (ret)
                return ret;
        ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
@@ -49,7 +50,9 @@ static void cs35l56_hda_spi_remove(struct spi_device *spi)
 }
 
 static const struct spi_device_id cs35l56_hda_spi_id[] = {
-       { "cs35l56-hda", 0 },
+       { "cs35l54-hda", 0x3554 },
+       { "cs35l56-hda", 0x3556 },
+       { "cs35l57-hda", 0x3557 },
        {}
 };
 
index e63621bcb21427b6ecc1e5516f37ca38ac1cef2f..e51d475725576b4c88bfd50f504288cd6f96a719 100644 (file)
@@ -231,7 +231,6 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
        codec->beep = beep;
 
        INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
-       mutex_init(&beep->mutex);
 
        input_dev = input_allocate_device();
        if (!input_dev) {
index db76e3ddba65445bc8e47257436d6f6cb51df142..923ea862446a01448ad3422ca76f8116503bee63 100644 (file)
@@ -27,7 +27,6 @@ struct hda_beep {
        unsigned int playing:1;
        unsigned int keep_power_at_enable:1;    /* set by driver */
        struct work_struct beep_work; /* scheduled task for beep event */
-       struct mutex mutex;
        void (*power_hook)(struct hda_beep *beep, bool on);
 };
 
index 12f02cdc965942ef85c8bac1e0f15f680604ca68..2cac337f526322270e7afa25fd2119f7aa3bf8af 100644 (file)
@@ -3313,7 +3313,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
        list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
                int stream;
 
-               for (stream = 0; stream < 2; stream++) {
+               for_each_pcm_streams(stream) {
                        struct hda_pcm_stream *info = &cpcm->stream[stream];
 
                        if (!info->substreams)
diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c
new file mode 100644 (file)
index 0000000..cd299d7
--- /dev/null
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+ *                     Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/acpi.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/hda_codec.h>
+#include "hda_component.h"
+#include "hda_local.h"
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps,
+                                     acpi_handle handle, u32 event, void *data)
+{
+       int i;
+
+       for (i = 0; i < num_comps; i++) {
+               if (comps[i].dev && comps[i].acpi_notify)
+                       comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event,
+                                            comps[i].dev);
+       }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT);
+
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+                                                 struct hda_component *comps, int num_comps,
+                                                 acpi_notify_handler handler, void *data)
+{
+       bool support_notifications = false;
+       struct acpi_device *adev;
+       int ret;
+       int i;
+
+       adev = comps[0].adev;
+       if (!acpi_device_handle(adev))
+               return 0;
+
+       for (i = 0; i < num_comps; i++)
+               support_notifications = support_notifications ||
+                       comps[i].acpi_notifications_supported;
+
+       if (support_notifications) {
+               ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+                                                 handler, data);
+               if (ret < 0) {
+                       codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
+                       return 0;
+               }
+
+               codec_dbg(cdc, "Notify handler installed\n");
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
+
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+                                                    struct hda_component *comps,
+                                                    acpi_notify_handler handler)
+{
+       struct acpi_device *adev;
+       int ret;
+
+       adev = comps[0].adev;
+       if (!acpi_device_handle(adev))
+               return;
+
+       ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);
+       if (ret < 0)
+               codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action)
+{
+       int i;
+
+       for (i = 0; i < num_comps; i++) {
+               if (comps[i].dev && comps[i].pre_playback_hook)
+                       comps[i].pre_playback_hook(comps[i].dev, action);
+       }
+       for (i = 0; i < num_comps; i++) {
+               if (comps[i].dev && comps[i].playback_hook)
+                       comps[i].playback_hook(comps[i].dev, action);
+       }
+       for (i = 0; i < num_comps; i++) {
+               if (comps[i].dev && comps[i].post_playback_hook)
+                       comps[i].post_playback_hook(comps[i].dev, action);
+       }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT);
+
+struct hda_scodec_match {
+       const char *bus;
+       const char *hid;
+       const char *match_str;
+       int index;
+};
+
+/* match the device name in a slightly relaxed manner */
+static int hda_comp_match_dev_name(struct device *dev, void *data)
+{
+       struct hda_scodec_match *p = data;
+       const char *d = dev_name(dev);
+       int n = strlen(p->bus);
+       char tmp[32];
+
+       /* check the bus name */
+       if (strncmp(d, p->bus, n))
+               return 0;
+       /* skip the bus number */
+       if (isdigit(d[n]))
+               n++;
+       /* the rest must be exact matching */
+       snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);
+       return !strcmp(d + n, tmp);
+}
+
+int hda_component_manager_init(struct hda_codec *cdc,
+                              struct hda_component *comps, int count,
+                              const char *bus, const char *hid,
+                              const char *match_str,
+                              const struct component_master_ops *ops)
+{
+       struct device *dev = hda_codec_dev(cdc);
+       struct component_match *match = NULL;
+       struct hda_scodec_match *sm;
+       int ret, i;
+
+       for (i = 0; i < count; i++) {
+               sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);
+               if (!sm)
+                       return -ENOMEM;
+
+               sm->bus = bus;
+               sm->hid = hid;
+               sm->match_str = match_str;
+               sm->index = i;
+               comps[i].codec = cdc;
+               component_match_add(dev, &match, hda_comp_match_dev_name, sm);
+       }
+
+       ret = component_master_add_with_match(dev, ops, match);
+       if (ret)
+               codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT);
+
+void hda_component_manager_free(struct hda_codec *cdc,
+                               const struct component_master_ops *ops)
+{
+       struct device *dev = hda_codec_dev(cdc);
+
+       component_master_del(dev, ops);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT);
+
+MODULE_DESCRIPTION("HD Audio component binding library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
index bbd6f0ed16c13d09aac393059b84dd9419893979..c80a66691b5d86c5b27f99abfe127a2f1fafaf7f 100644 (file)
@@ -6,8 +6,12 @@
  *                    Cirrus Logic International Semiconductor Ltd.
  */
 
+#ifndef __HDA_COMPONENT_H__
+#define __HDA_COMPONENT_H__
+
 #include <linux/acpi.h>
 #include <linux/component.h>
+#include <sound/hda_codec.h>
 
 #define HDA_MAX_COMPONENTS     4
 #define HDA_MAX_NAME_SIZE      50
@@ -23,3 +27,64 @@ struct hda_component {
        void (*playback_hook)(struct device *dev, int action);
        void (*post_playback_hook)(struct device *dev, int action);
 };
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps,
+                                     acpi_handle handle, u32 event, void *data);
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+                                                 struct hda_component *comps, int num_comps,
+                                                 acpi_notify_handler handler, void *data);
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+                                                    struct hda_component *comps,
+                                                    acpi_notify_handler handler);
+#else
+static inline void hda_component_acpi_device_notify(struct hda_component *comps,
+                                                   int num_comps,
+                                                   acpi_handle handle,
+                                                   u32 event,
+                                                   void *data)
+{
+}
+
+static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+                                                               struct hda_component *comps,
+                                                               int num_comps,
+                                                               acpi_notify_handler handler,
+                                                               void *data)
+
+{
+       return 0;
+}
+
+static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+                                                                  struct hda_component *comps,
+                                                                  acpi_notify_handler handler)
+{
+}
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps,
+                                        int action);
+
+int hda_component_manager_init(struct hda_codec *cdc,
+                              struct hda_component *comps, int count,
+                              const char *bus, const char *hid,
+                              const char *match_str,
+                              const struct component_master_ops *ops);
+
+void hda_component_manager_free(struct hda_codec *cdc,
+                               const struct component_master_ops *ops);
+
+static inline int hda_component_manager_bind(struct hda_codec *cdc,
+                                            struct hda_component *comps)
+{
+       return component_bind_all(hda_codec_dev(cdc), comps);
+}
+
+static inline void hda_component_manager_unbind(struct hda_codec *cdc,
+                                              struct hda_component *comps)
+{
+       component_unbind_all(hda_codec_dev(cdc), comps);
+}
+
+#endif /* ifndef __HDA_COMPONENT_H__ */
index efe98f6f19a373b9c343861cf9bdc2039885d7e2..206306a0eb827abaf0030a668959bba33f882fe5 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/pcm_params.h>
 #include "hda_controller.h"
 #include "hda_local.h"
 
@@ -108,6 +109,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
        struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
        struct azx *chip = apcm->chip;
        struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct hdac_stream *hdas = azx_stream(azx_dev);
        int ret = 0;
 
        trace_azx_pcm_hw_params(chip, azx_dev);
@@ -117,9 +119,15 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
                goto unlock;
        }
 
-       azx_dev->core.bufsize = 0;
-       azx_dev->core.period_bytes = 0;
-       azx_dev->core.format_val = 0;
+       /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */
+       hdas->bufsize = params_buffer_bytes(hw_params);
+       hdas->period_bytes = params_period_bytes(hw_params);
+       hdas->format_val = 0;
+       hdas->no_period_wakeup =
+               (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+               (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
+       if (snd_hdac_stream_setup_periods(hdas) < 0)
+               ret = -ENOMEM;
 
 unlock:
        dsp_unlock(azx_dev);
index a1facdb98d9a00a4236ff06c0f64700a0facd1cc..b6cd13b1775d93f5704989cd9bf1c4f4c0cc1147 100644 (file)
@@ -133,7 +133,6 @@ struct alc_spec {
        u8 alc_mute_keycode_map[1];
 
        /* component binding */
-       struct component_match *match;
        struct hda_component comps[HDA_MAX_COMPONENTS];
 };
 
@@ -6696,6 +6695,60 @@ static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec,
        }
 }
 
+static void alc285_fixup_hp_envy_x360(struct hda_codec *codec,
+                                     const struct hda_fixup *fix,
+                                     int action)
+{
+       static const struct coef_fw coefs[] = {
+               WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023),
+               WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03),
+               WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a),
+               WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014),
+               WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15),
+               WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489),
+               WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0),
+               WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000),
+               WRITE_COEF(0x6e, 0x1005), { }
+       };
+
+       static const struct hda_pintbl pincfgs[] = {
+               { 0x12, 0xb7a60130 },  /* Internal microphone*/
+               { 0x14, 0x90170150 },  /* B&O soundbar speakers */
+               { 0x17, 0x90170153 },  /* Side speakers */
+               { 0x19, 0x03a11040 },  /* Headset microphone */
+               { }
+       };
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               snd_hda_apply_pincfgs(codec, pincfgs);
+
+               /* Fixes volume control problem for side speakers */
+               alc295_fixup_disable_dac3(codec, fix, action);
+
+               /* Fixes no sound from headset speaker */
+               snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0);
+
+               /* Auto-enable headset mic when plugged */
+               snd_hda_jack_set_gating_jack(codec, 0x19, 0x21);
+
+               /* Headset mic volume enhancement */
+               snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50);
+               break;
+       case HDA_FIXUP_ACT_INIT:
+               alc_process_coef_fw(codec, coefs);
+               break;
+       case HDA_FIXUP_ACT_BUILD:
+               rename_ctl(codec, "Bass Speaker Playback Volume",
+                          "B&O-Tuned Playback Volume");
+               rename_ctl(codec, "Front Playback Switch",
+                          "B&O Soundbar Playback Switch");
+               rename_ctl(codec, "Bass Speaker Playback Switch",
+                          "Side Speaker Playback Switch");
+               break;
+       }
+}
+
 /* for hda_fixup_thinkpad_acpi() */
 #include "thinkpad_helper.c"
 
@@ -6720,79 +6773,16 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
        }
 }
 
-#ifdef CONFIG_ACPI
 static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
 {
        struct hda_codec *cdc = data;
        struct alc_spec *spec = cdc->spec;
-       int i;
 
        codec_info(cdc, "ACPI Notification %d\n", event);
 
-       for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
-               if (spec->comps[i].dev && spec->comps[i].acpi_notify)
-                       spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), event,
-                                                  spec->comps[i].dev);
-       }
-}
-
-static int comp_bind_acpi(struct device *dev)
-{
-       struct hda_codec *cdc = dev_to_hda_codec(dev);
-       struct alc_spec *spec = cdc->spec;
-       bool support_notifications = false;
-       struct acpi_device *adev;
-       int ret;
-       int i;
-
-       adev = spec->comps[0].adev;
-       if (!acpi_device_handle(adev))
-               return 0;
-
-       for (i = 0; i < HDA_MAX_COMPONENTS; i++)
-               support_notifications = support_notifications ||
-                       spec->comps[i].acpi_notifications_supported;
-
-       if (support_notifications) {
-               ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
-                                               comp_acpi_device_notify, cdc);
-               if (ret < 0) {
-                       codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
-                       return 0;
-               }
-
-               codec_dbg(cdc, "Notify handler installed\n");
-       }
-
-       return 0;
-}
-
-static void comp_unbind_acpi(struct device *dev)
-{
-       struct hda_codec *cdc = dev_to_hda_codec(dev);
-       struct alc_spec *spec = cdc->spec;
-       struct acpi_device *adev;
-       int ret;
-
-       adev = spec->comps[0].adev;
-       if (!acpi_device_handle(adev))
-               return;
-
-       ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
-                                        comp_acpi_device_notify);
-       if (ret < 0)
-               codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
+       hda_component_acpi_device_notify(spec->comps, ARRAY_SIZE(spec->comps),
+                                        handle, event, data);
 }
-#else
-static int comp_bind_acpi(struct device *dev)
-{
-       return 0;
-}
-
-static void comp_unbind_acpi(struct device *dev)
-{
-}
-#endif
 
 static int comp_bind(struct device *dev)
 {
@@ -6800,11 +6790,13 @@ static int comp_bind(struct device *dev)
        struct alc_spec *spec = cdc->spec;
        int ret;
 
-       ret = component_bind_all(dev, spec->comps);
+       ret = hda_component_manager_bind(cdc, spec->comps);
        if (ret)
                return ret;
 
-       return comp_bind_acpi(dev);
+       return hda_component_manager_bind_acpi_notifications(cdc,
+                                                            spec->comps, ARRAY_SIZE(spec->comps),
+                                                            comp_acpi_device_notify, cdc);
 }
 
 static void comp_unbind(struct device *dev)
@@ -6812,8 +6804,8 @@ static void comp_unbind(struct device *dev)
        struct hda_codec *cdc = dev_to_hda_codec(dev);
        struct alc_spec *spec = cdc->spec;
 
-       comp_unbind_acpi(dev);
-       component_unbind_all(dev, spec->comps);
+       hda_component_manager_unbind_acpi_notifications(cdc, spec->comps, comp_acpi_device_notify);
+       hda_component_manager_unbind(cdc, spec->comps);
 }
 
 static const struct component_master_ops comp_master_ops = {
@@ -6825,177 +6817,78 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_
                                       struct snd_pcm_substream *sub, int action)
 {
        struct alc_spec *spec = cdc->spec;
-       int i;
 
-       for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
-               if (spec->comps[i].dev && spec->comps[i].pre_playback_hook)
-                       spec->comps[i].pre_playback_hook(spec->comps[i].dev, action);
-       }
-       for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
-               if (spec->comps[i].dev && spec->comps[i].playback_hook)
-                       spec->comps[i].playback_hook(spec->comps[i].dev, action);
-       }
-       for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
-               if (spec->comps[i].dev && spec->comps[i].post_playback_hook)
-                       spec->comps[i].post_playback_hook(spec->comps[i].dev, action);
-       }
+       hda_component_manager_playback_hook(spec->comps, ARRAY_SIZE(spec->comps), action);
 }
 
-struct scodec_dev_name {
-       const char *bus;
-       const char *hid;
-       int index;
-};
-
-/* match the device name in a slightly relaxed manner */
-static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
+static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+                              const char *hid, const char *match_str, int count)
 {
-       struct scodec_dev_name *p = data;
-       const char *d = dev_name(dev);
-       int n = strlen(p->bus);
-       char tmp[32];
-
-       /* check the bus name */
-       if (strncmp(d, p->bus, n))
-               return 0;
-       /* skip the bus number */
-       if (isdigit(d[n]))
-               n++;
-       /* the rest must be exact matching */
-       snprintf(tmp, sizeof(tmp), "-%s:00-cs35l41-hda.%d", p->hid, p->index);
-       return !strcmp(d + n, tmp);
-}
-
-static int comp_match_tas2781_dev_name(struct device *dev,
-       void *data)
-{
-       struct scodec_dev_name *p = data;
-       const char *d = dev_name(dev);
-       int n = strlen(p->bus);
-       char tmp[32];
-
-       /* check the bus name */
-       if (strncmp(d, p->bus, n))
-               return 0;
-       /* skip the bus number */
-       if (isdigit(d[n]))
-               n++;
-       /* the rest must be exact matching */
-       snprintf(tmp, sizeof(tmp), "-%s:00", p->hid);
-
-       return !strcmp(d + n, tmp);
-}
-
-static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
-                                 const char *hid, int count)
-{
-       struct device *dev = hda_codec_dev(cdc);
        struct alc_spec *spec = cdc->spec;
-       struct scodec_dev_name *rec;
-       int ret, i;
-
-       switch (action) {
-       case HDA_FIXUP_ACT_PRE_PROBE:
-               for (i = 0; i < count; i++) {
-                       rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL);
-                       if (!rec)
-                               return;
-                       rec->bus = bus;
-                       rec->hid = hid;
-                       rec->index = i;
-                       spec->comps[i].codec = cdc;
-                       component_match_add(dev, &spec->match,
-                                           comp_match_cs35l41_dev_name, rec);
-               }
-               ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
-               if (ret)
-                       codec_err(cdc, "Fail to register component aggregator %d\n", ret);
-               else
-                       spec->gen.pcm_playback_hook = comp_generic_playback_hook;
-               break;
-       case HDA_FIXUP_ACT_FREE:
-               component_master_del(dev, &comp_master_ops);
-               break;
-       }
-}
-
-static void tas2781_generic_fixup(struct hda_codec *cdc, int action,
-       const char *bus, const char *hid)
-{
-       struct device *dev = hda_codec_dev(cdc);
-       struct alc_spec *spec = cdc->spec;
-       struct scodec_dev_name *rec;
        int ret;
 
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
-               rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL);
-               if (!rec)
-                       return;
-               rec->bus = bus;
-               rec->hid = hid;
-               rec->index = 0;
-               spec->comps[0].codec = cdc;
-               component_match_add(dev, &spec->match,
-                       comp_match_tas2781_dev_name, rec);
-               ret = component_master_add_with_match(dev, &comp_master_ops,
-                       spec->match);
+               ret = hda_component_manager_init(cdc, spec->comps, count, bus, hid,
+                                                match_str, &comp_master_ops);
                if (ret)
-                       codec_err(cdc,
-                               "Fail to register component aggregator %d\n",
-                               ret);
-               else
-                       spec->gen.pcm_playback_hook =
-                               comp_generic_playback_hook;
+                       return;
+
+               spec->gen.pcm_playback_hook = comp_generic_playback_hook;
                break;
        case HDA_FIXUP_ACT_FREE:
-               component_master_del(dev, &comp_master_ops);
+               hda_component_manager_free(cdc, &comp_master_ops);
                break;
        }
 }
 
 static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
 {
-       cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
+       comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
 }
 
 static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
 {
-       cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 4);
+       comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
 }
 
 static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
 {
-       cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2);
+       comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
 }
 
 static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
 {
-       cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 4);
+       comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
 }
 
 static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
                                                 int action)
 {
-       cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2);
+       comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2);
 }
 
 static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
                                                 int action)
 {
-       cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2);
+       comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void cs35l56_fixup_spi_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+       comp_generic_fixup(cdc, action, "spi", "CSC3556", "-%s:00-cs35l56-hda.%d", 4);
 }
 
 static void tas2781_fixup_i2c(struct hda_codec *cdc,
        const struct hda_fixup *fix, int action)
 {
-        tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781");
+       comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
 }
 
 static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
        const struct hda_fixup *fix, int action)
 {
-        tas2781_generic_fixup(cdc, action, "i2c", "INT8866");
+       comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1);
 }
 
 /* for alc295_fixup_hp_top_speakers */
@@ -7297,6 +7190,7 @@ enum {
        ALC280_FIXUP_HP_9480M,
        ALC245_FIXUP_HP_X360_AMP,
        ALC285_FIXUP_HP_SPECTRE_X360_EB1,
+       ALC285_FIXUP_HP_ENVY_X360,
        ALC288_FIXUP_DELL_HEADSET_MODE,
        ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
        ALC288_FIXUP_DELL_XPS_13,
@@ -7478,6 +7372,7 @@ enum {
        ALC2XX_FIXUP_HEADSET_MIC,
        ALC289_FIXUP_DELL_CS35L41_SPI_2,
        ALC294_FIXUP_CS35L41_I2C_2,
+       ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED,
 };
 
 /* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -9273,6 +9168,12 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc285_fixup_hp_spectre_x360_eb1
        },
+       [ALC285_FIXUP_HP_ENVY_X360] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc285_fixup_hp_envy_x360,
+               .chained = true,
+               .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT,
+       },
        [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc285_fixup_ideapad_s740_coef,
@@ -9655,6 +9556,12 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cs35l41_fixup_i2c_two,
        },
+       [ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs35l56_fixup_spi_four,
+               .chained = true,
+               .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -9861,6 +9768,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
        SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360),
        SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+       SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360),
        SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
@@ -9947,9 +9855,22 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+       SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
        SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+       SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
+       SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
@@ -9959,8 +9880,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+       SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
        SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
@@ -9988,11 +9911,35 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
        SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+       SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
        SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre 14", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2),
        SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
        SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
@@ -10006,6 +9953,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2),
+       SND_PCI_QUIRK(0x103c, 0x8cde, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2),
        SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
        SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
@@ -10127,6 +10076,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+       SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE),
        SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
@@ -10559,6 +10509,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"},
        {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"},
        {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"},
+       {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"},
        {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"},
        {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"},
        {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"},
@@ -12674,6 +12625,7 @@ MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Realtek HD-audio codec");
+MODULE_IMPORT_NS(SND_HDA_SCODEC_COMPONENT);
 
 static struct hda_codec_driver realtek_driver = {
        .id = snd_hda_id_realtek,
index 1bfb00102a77a4d49695efeaf7016eded961345b..4475cea8e9f70382113c3e6f496eebc854c92b40 100644 (file)
@@ -111,9 +111,7 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
 static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
 {
        struct acpi_device *adev;
-       struct device *physdev;
        LIST_HEAD(resources);
-       const char *sub;
        int ret;
 
        adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
@@ -129,18 +127,8 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
 
        acpi_dev_free_resource_list(&resources);
        strscpy(p->dev_name, hid, sizeof(p->dev_name));
-       physdev = get_device(acpi_get_first_physical_node(adev));
        acpi_dev_put(adev);
 
-       /* No side-effect to the playback even if subsystem_id is NULL*/
-       sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
-       if (IS_ERR(sub))
-               sub = NULL;
-
-       p->acpi_subsystem_id = sub;
-
-       put_device(physdev);
-
        return 0;
 
 err:
@@ -160,11 +148,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action)
                pm_runtime_get_sync(dev);
                mutex_lock(&tas_hda->priv->codec_lock);
                tasdevice_tuning_switch(tas_hda->priv, 0);
+               tas_hda->priv->playback_started = true;
                mutex_unlock(&tas_hda->priv->codec_lock);
                break;
        case HDA_GEN_PCM_ACT_CLOSE:
                mutex_lock(&tas_hda->priv->codec_lock);
                tasdevice_tuning_switch(tas_hda->priv, 1);
+               tas_hda->priv->playback_started = false;
                mutex_unlock(&tas_hda->priv->codec_lock);
 
                pm_runtime_mark_last_busy(dev);
@@ -666,6 +656,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
        tasdevice_save_calibration(tas_priv);
 
        tasdevice_tuning_switch(tas_hda->priv, 0);
+       tas_hda->priv->playback_started = true;
 
 out:
        mutex_unlock(&tas_hda->priv->codec_lock);
@@ -805,11 +796,8 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
        pm_runtime_use_autosuspend(tas_hda->dev);
        pm_runtime_mark_last_busy(tas_hda->dev);
        pm_runtime_set_active(tas_hda->dev);
-       pm_runtime_get_noresume(tas_hda->dev);
        pm_runtime_enable(tas_hda->dev);
 
-       pm_runtime_put_autosuspend(tas_hda->dev);
-
        tas2781_reset(tas_hda->priv);
 
        ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
@@ -832,23 +820,19 @@ static void tas2781_hda_i2c_remove(struct i2c_client *clt)
 static int tas2781_runtime_suspend(struct device *dev)
 {
        struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
-       int i;
 
        dev_dbg(tas_hda->dev, "Runtime Suspend\n");
 
        mutex_lock(&tas_hda->priv->codec_lock);
 
+       /* The driver powers up the amplifiers at module load time.
+        * Stop the playback if it's unused.
+        */
        if (tas_hda->priv->playback_started) {
                tasdevice_tuning_switch(tas_hda->priv, 1);
                tas_hda->priv->playback_started = false;
        }
 
-       for (i = 0; i < tas_hda->priv->ndev; i++) {
-               tas_hda->priv->tasdevice[i].cur_book = -1;
-               tas_hda->priv->tasdevice[i].cur_prog = -1;
-               tas_hda->priv->tasdevice[i].cur_conf = -1;
-       }
-
        mutex_unlock(&tas_hda->priv->codec_lock);
 
        return 0;
@@ -877,16 +861,16 @@ static int tas2781_runtime_resume(struct device *dev)
 static int tas2781_system_suspend(struct device *dev)
 {
        struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
-       int ret;
 
        dev_dbg(tas_hda->priv->dev, "System Suspend\n");
 
-       ret = pm_runtime_force_suspend(dev);
-       if (ret)
-               return ret;
+       mutex_lock(&tas_hda->priv->codec_lock);
 
        /* Shutdown chip before system suspend */
-       tasdevice_tuning_switch(tas_hda->priv, 1);
+       if (tas_hda->priv->playback_started)
+               tasdevice_tuning_switch(tas_hda->priv, 1);
+
+       mutex_unlock(&tas_hda->priv->codec_lock);
 
        /*
         * Reset GPIO may be shared, so cannot reset here.
@@ -898,13 +882,9 @@ static int tas2781_system_suspend(struct device *dev)
 static int tas2781_system_resume(struct device *dev)
 {
        struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
-       int i, ret;
-
-       dev_info(tas_hda->priv->dev, "System Resume\n");
+       int i;
 
-       ret = pm_runtime_force_resume(dev);
-       if (ret)
-               return ret;
+       dev_dbg(tas_hda->priv->dev, "System Resume\n");
 
        mutex_lock(&tas_hda->priv->codec_lock);
 
@@ -920,6 +900,10 @@ static int tas2781_system_resume(struct device *dev)
         * calibrated data inside algo.
         */
        tasdevice_apply_calibration(tas_hda->priv);
+
+       if (tas_hda->priv->playback_started)
+               tasdevice_tuning_switch(tas_hda->priv, 0);
+
        mutex_unlock(&tas_hda->priv->codec_lock);
 
        return 0;
index ae285c0a629c82a518af20812d681526b4e61a73..dae3e15ba534de63e6071f3b3831941687bd1fc3 100644 (file)
@@ -2555,7 +2555,6 @@ static void snd_intel8x0_free(struct snd_card *card)
                free_irq(chip->irq, chip);
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -2628,11 +2627,7 @@ static int intel8x0_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
-#define INTEL8X0_PM_OPS        &intel8x0_pm
-#else
-#define INTEL8X0_PM_OPS        NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
 
 #define INTEL8X0_TESTBUF_SIZE  32768   /* enough large for one shot */
 
@@ -3200,7 +3195,7 @@ static struct pci_driver intel8x0_driver = {
        .id_table = snd_intel8x0_ids,
        .probe = snd_intel8x0_probe,
        .driver = {
-               .pm = INTEL8X0_PM_OPS,
+               .pm = &intel8x0_pm,
        },
 };
 
index 653ecca782382028c520dbc6e32a0f09ca49cf2c..3d6f5b3cc73e173299f2f17d9ac3ac25cb08f4c3 100644 (file)
@@ -965,7 +965,6 @@ static void snd_intel8x0m_free(struct snd_card *card)
                free_irq(chip->irq, chip);
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -1006,11 +1005,7 @@ static int intel8x0m_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
-#define INTEL8X0M_PM_OPS       &intel8x0m_pm
-#else
-#define INTEL8X0M_PM_OPS       NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
 
 static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
                                   struct snd_info_buffer *buffer)
@@ -1236,7 +1231,7 @@ static struct pci_driver intel8x0m_driver = {
        .id_table = snd_intel8x0m_ids,
        .probe = snd_intel8x0m_probe,
        .driver = {
-               .pm = INTEL8X0M_PM_OPS,
+               .pm = &intel8x0m_pm,
        },
 };
 
index 305cbd24a3918eaddb7c9ff552fa5754d41d3c34..f4d211970d7ec98bb626f1187edc8842c44c13ae 100644 (file)
@@ -769,9 +769,7 @@ struct snd_m3 {
 
        unsigned int in_suspend;
 
-#ifdef CONFIG_PM_SLEEP
        u16 *suspend_mem;
-#endif
 
        const struct firmware *assp_kernel_image;
        const struct firmware *assp_minisrc_image;
@@ -2354,9 +2352,7 @@ static void snd_m3_free(struct snd_card *card)
                outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
        }
 
-#ifdef CONFIG_PM_SLEEP
        vfree(chip->suspend_mem);
-#endif
        release_firmware(chip->assp_kernel_image);
        release_firmware(chip->assp_minisrc_image);
 }
@@ -2365,7 +2361,6 @@ static void snd_m3_free(struct snd_card *card)
 /*
  * APM support
  */
-#ifdef CONFIG_PM_SLEEP
 static int m3_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -2439,11 +2434,7 @@ static int m3_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume);
-#define M3_PM_OPS      &m3_pm
-#else
-#define M3_PM_OPS      NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume);
 
 #ifdef CONFIG_SND_MAESTRO3_INPUT
 static int snd_m3_input_register(struct snd_m3 *chip)
@@ -2587,14 +2578,14 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
        chip->irq = pci->irq;
        card->sync_irq = chip->irq;
 
-#ifdef CONFIG_PM_SLEEP
-       chip->suspend_mem =
-               vmalloc(array_size(sizeof(u16),
-                                  REV_B_CODE_MEMORY_LENGTH +
-                                       REV_B_DATA_MEMORY_LENGTH));
-       if (chip->suspend_mem == NULL)
-               dev_warn(card->dev, "can't allocate apm buffer\n");
-#endif
+       if (IS_ENABLED(CONFIG_PM_SLEEP)) {
+               chip->suspend_mem =
+                       vmalloc(array_size(sizeof(u16),
+                                          REV_B_CODE_MEMORY_LENGTH +
+                                          REV_B_DATA_MEMORY_LENGTH));
+               if (!chip->suspend_mem)
+                       dev_warn(card->dev, "can't allocate apm buffer\n");
+       }
 
        err = snd_m3_mixer(chip);
        if (err < 0)
@@ -2706,7 +2697,7 @@ static struct pci_driver m3_driver = {
        .id_table = snd_m3_ids,
        .probe = snd_m3_probe,
        .driver = {
-               .pm = M3_PM_OPS,
+               .pm = &m3_pm,
        },
 };
        
index 34f90829e65692f3aa9779b4fe861048b39ff49a..11ba7d4eac2a4e02111ef4f0775f0a2449056c84 100644 (file)
@@ -1356,7 +1356,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * APM event handler, so the card is properly reinitialized after a power
  * event.
@@ -1400,11 +1399,7 @@ static int nm256_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume);
-#define NM256_PM_OPS   &nm256_pm
-#else
-#define NM256_PM_OPS   NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume);
 
 static void snd_nm256_free(struct snd_card *card)
 {
@@ -1660,7 +1655,7 @@ static struct pci_driver nm256_driver = {
        .id_table = snd_nm256_ids,
        .probe = snd_nm256_probe,
        .driver = {
-               .pm = NM256_PM_OPS,
+               .pm = &nm256_pm,
        },
 };
 
index 9dee0345f22cf1e550d38f1c3b3c7918e2f116c5..7e80686fb41a33ea09a71f4d95db612d89d89d50 100644 (file)
@@ -448,9 +448,7 @@ struct snd_riptide {
 
        unsigned long received_irqs;
        unsigned long handled_irqs;
-#ifdef CONFIG_PM_SLEEP
        int in_suspend;
-#endif
 };
 
 struct sgd {                   /* scatter gather desriptor */
@@ -1142,7 +1140,6 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int riptide_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -1166,11 +1163,7 @@ static int riptide_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume);
-#define RIPTIDE_PM_OPS &riptide_pm
-#else
-#define RIPTIDE_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume);
 
 static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
 {
@@ -2135,7 +2128,7 @@ static struct pci_driver driver = {
        .id_table = snd_riptide_ids,
        .probe = snd_card_riptide_probe,
        .driver = {
-               .pm = RIPTIDE_PM_OPS,
+               .pm = &riptide_pm,
        },
 };
 
index 6b5ffb18197b0d002ea9a4380df438f1ba86843e..d50ad25574adac15b68787dba9ac9fb5f1496538 100644 (file)
@@ -220,12 +220,10 @@ struct rme96 {
 
        u8 rev; /* card revision number */
 
-#ifdef CONFIG_PM_SLEEP
        u32 playback_pointer;
        u32 capture_pointer;
        void *playback_suspend_buffer;
        void *capture_suspend_buffer;
-#endif
 
        struct snd_pcm_substream *playback_substream;
        struct snd_pcm_substream *capture_substream;
@@ -1543,10 +1541,8 @@ snd_rme96_free(struct rme96 *rme96)
                rme96->areg &= ~RME96_AR_DAC_EN;
                writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
        }
-#ifdef CONFIG_PM_SLEEP
        vfree(rme96->playback_suspend_buffer);
        vfree(rme96->capture_suspend_buffer);
-#endif
 }
 
 static void
@@ -2329,8 +2325,6 @@ snd_rme96_create_switches(struct snd_card *card,
  * Card initialisation
  */
 
-#ifdef CONFIG_PM_SLEEP
-
 static int rme96_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -2392,11 +2386,7 @@ static int rme96_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume);
-#define RME96_PM_OPS   &rme96_pm
-#else
-#define RME96_PM_OPS   NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume);
 
 static void snd_rme96_card_free(struct snd_card *card)
 {
@@ -2432,14 +2422,14 @@ __snd_rme96_probe(struct pci_dev *pci,
        if (err)
                return err;
        
-#ifdef CONFIG_PM_SLEEP
-       rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
-       if (!rme96->playback_suspend_buffer)
-               return -ENOMEM;
-       rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
-       if (!rme96->capture_suspend_buffer)
-               return -ENOMEM;
-#endif
+       if (IS_ENABLED(CONFIG_PM_SLEEP)) {
+               rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+               if (!rme96->playback_suspend_buffer)
+                       return -ENOMEM;
+               rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+               if (!rme96->capture_suspend_buffer)
+                       return -ENOMEM;
+       }
 
        strcpy(card->driver, "Digi96");
        switch (rme96->pci->device) {
@@ -2483,7 +2473,7 @@ static struct pci_driver rme96_driver = {
        .id_table = snd_rme96_ids,
        .probe = snd_rme96_probe,
        .driver = {
-               .pm = RME96_PM_OPS,
+               .pm = &rme96_pm,
        },
 };
 
index fabe393607f8fa2862db6b73e5247d7bd1f63ccf..53206beb2cb50d704c54029bb9d5fc52ae16e653 100644 (file)
@@ -90,11 +90,7 @@ struct voice {
  * we're not doing power management, we still need to allocate a page
  * for the silence buffer.
  */
-#ifdef CONFIG_PM_SLEEP
 #define SIS_SUSPEND_PAGES      4
-#else
-#define SIS_SUSPEND_PAGES      1
-#endif
 
 struct sis7019 {
        unsigned long ioport;
@@ -1152,7 +1148,6 @@ static int sis_chip_init(struct sis7019 *sis)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int sis_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -1231,11 +1226,7 @@ error:
        return -EIO;
 }
 
-static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume);
-#define SIS_PM_OPS     &sis_pm
-#else
-#define SIS_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume);
 
 static int sis_alloc_suspend(struct sis7019 *sis)
 {
@@ -1397,7 +1388,7 @@ static struct pci_driver sis7019_driver = {
        .id_table = snd_sis7019_ids,
        .probe = snd_sis7019_probe,
        .driver = {
-               .pm = SIS_PM_OPS,
+               .pm = &sis_pm,
        },
 };
 
index d8666ff7bdfae02e8278f7e698fb7927d479c3b7..89838b4fb118a30f51924eca79568fbd4e1d61eb 100644 (file)
@@ -347,13 +347,11 @@ struct via82xx {
 
        unsigned char old_legacy;
        unsigned char old_legacy_cfg;
-#ifdef CONFIG_PM_SLEEP
        unsigned char legacy_saved;
        unsigned char legacy_cfg_saved;
        unsigned char spdif_ctrl_saved;
        unsigned char capture_src_saved[2];
        unsigned int mpu_port_saved;
-#endif
 
        unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */
        unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */
@@ -2031,9 +2029,7 @@ static int snd_via686_init_misc(struct via82xx *chip)
                if (mpu_port >= 0x200) {        /* force MIDI */
                        mpu_port &= 0xfffc;
                        pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01);
-#ifdef CONFIG_PM_SLEEP
                        chip->mpu_port_saved = mpu_port;
-#endif
                } else {
                        mpu_port = pci_resource_start(chip->pci, 2);
                }
@@ -2085,10 +2081,8 @@ static int snd_via686_init_misc(struct via82xx *chip)
 
        snd_via686_create_gameport(chip, &legacy);
 
-#ifdef CONFIG_PM_SLEEP
        chip->legacy_saved = legacy;
        chip->legacy_cfg_saved = legacy_cfg;
-#endif
 
        return 0;
 }
@@ -2234,7 +2228,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -2287,11 +2280,7 @@ static int snd_via82xx_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
-#define SND_VIA82XX_PM_OPS     &snd_via82xx_pm
-#else
-#define SND_VIA82XX_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
 
 static void snd_via82xx_free(struct snd_card *card)
 {
@@ -2576,7 +2565,7 @@ static struct pci_driver via82xx_driver = {
        .id_table = snd_via82xx_ids,
        .probe = snd_via82xx_probe,
        .driver = {
-               .pm = SND_VIA82XX_PM_OPS,
+               .pm = &snd_via82xx_pm,
        },
 };
 
index ca7f024bf8ec6efe5387d205512120cdbc8872aa..a0a49b8d151160482fef70a21e1b32e37575116b 100644 (file)
@@ -1008,7 +1008,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
 /*
  * power management
  */
@@ -1042,11 +1041,7 @@ static int snd_via82xx_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
-#define SND_VIA82XX_PM_OPS     &snd_via82xx_pm
-#else
-#define SND_VIA82XX_PM_OPS     NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
 
 static void snd_via82xx_free(struct snd_card *card)
 {
@@ -1168,7 +1163,7 @@ static struct pci_driver via82xx_modem_driver = {
        .id_table = snd_via82xx_modem_ids,
        .probe = snd_via82xx_probe,
        .driver = {
-               .pm = SND_VIA82XX_PM_OPS,
+               .pm = &snd_via82xx_pm,
        },
 };
 
index 273688c053172f267ca63e82eea4c1d778e41c04..fa74635cee08ea5bfc43ad589a6f579e1ca164a4 100644 (file)
@@ -132,9 +132,26 @@ config SND_SOC_AMD_RPL_ACP6x
           Say m if you have such a device.
           If unsure select "N".
 
+config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+       tristate
+       select SOUNDWIRE_AMD if SND_SOC_AMD_SOUNDWIRE != n
+       select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_AMD_SOUNDWIRE
+       tristate "Support for SoundWire based AMD platforms"
+       default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+       depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+       depends on ACPI && SOUNDWIRE
+       depends on !(SOUNDWIRE=m && SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE=y)
+       help
+         This adds support for SoundWire for AMD platforms.
+         Say Y if you want to enable SoundWire links with SOF.
+         If unsure select "N".
+
 config SND_SOC_AMD_PS
         tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
        select SND_AMD_ACP_CONFIG
+       select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
         depends on X86 && PCI && ACPI
         help
           This option enables Audio Coprocessor i.e ACP v6.3 support on
index 82e1cf864a40957a44ff056de1b91abb2346bb86..ebbe49c2bbff27ee042aadc70313d0efc2792a31 100644 (file)
@@ -15,7 +15,7 @@ obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
 obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
 obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
 obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
-obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/
 obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
 obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/
 obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
index 84c963241dc55dd435c60010d2c8ab6aaa369ed7..30590a23ad63b4687d436f1e731bfa5d39041d88 100644 (file)
@@ -116,3 +116,10 @@ config SND_SOC_AMD_SOF_MACH
          This option enables SOF sound card support for ACP audio.
 
 endif # SND_SOC_AMD_ACP_COMMON
+
+config SND_AMD_SOUNDWIRE_ACPI
+       tristate
+       depends on ACPI
+       help
+         This options enables ACPI helper functions for SoundWire
+         interface for AMD platforms.
index ff5f7893b81e434c7256a246993f254389e771e7..1fd581a2aa3387015a948409256c9aac50da6c03 100644 (file)
@@ -10,6 +10,7 @@ snd-acp-i2s-objs     := acp-i2s.o
 snd-acp-pdm-objs     := acp-pdm.o
 snd-acp-legacy-common-objs   := acp-legacy-common.o
 snd-acp-pci-objs     := acp-pci.o
+snd-amd-sdw-acpi-objs := amd-sdw-acpi.o
 
 #platform specific driver
 snd-acp-renoir-objs     := acp-renoir.o
@@ -33,6 +34,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_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
 obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
index 504d1b8c4cbb4f104a8b8e70adf10894f211ce6c..665a6ea0a2a8ceedefb143ce6778028c58b2d42a 100644 (file)
@@ -828,8 +828,8 @@ static const struct snd_soc_ops acp_card_maxim_ops = {
 };
 
 SND_SOC_DAILINK_DEF(max98388,
-                   DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"),
-                                      COMP_CODEC("i2c-ADS8388:01", "max98388-aif1")));
+                   DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI),
+                                      COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI)));
 
 static const struct snd_kcontrol_new max98388_controls[] = {
        SOC_DAPM_PIN_SWITCH("Left Spk"),
@@ -1280,7 +1280,7 @@ static const struct snd_soc_ops acp_8821_ops = {
 
 SND_SOC_DAILINK_DEF(nau8821,
                    DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
-                                                 "nau8821-hifi")));
+                                                 NAU8821_CODEC_DAI)));
 
 /* Declare DMIC codec components */
 SND_SOC_DAILINK_DEF(dmic_codec,
index 20b94814a0462147258fe94cd4219b772afa45f0..fc59ea34e687ad4d02fcab4df6dc322079f8d219 100644 (file)
@@ -28,7 +28,6 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = {
        .hs_codec_id = RT5682,
        .amp_codec_id = RT1019,
        .dmic_codec_id = DMIC,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_rt5682_max_data = {
@@ -38,7 +37,6 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
        .hs_codec_id = RT5682,
        .amp_codec_id = MAX98360A,
        .dmic_codec_id = DMIC,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
@@ -49,7 +47,6 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
        .amp_codec_id = RT1019,
        .dmic_codec_id = DMIC,
        .platform = RENOIR,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_rt5682s_max_data = {
@@ -60,7 +57,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
        .amp_codec_id = MAX98360A,
        .dmic_codec_id = DMIC,
        .platform = RENOIR,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_nau8825_data = {
@@ -72,7 +68,6 @@ static struct acp_card_drvdata sof_nau8825_data = {
        .dmic_codec_id = DMIC,
        .platform = REMBRANDT,
        .soc_mclk = true,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
@@ -84,20 +79,15 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
        .dmic_codec_id = DMIC,
        .platform = REMBRANDT,
        .soc_mclk = true,
-       .tdm_mode = false,
 };
 
 static struct acp_card_drvdata sof_nau8821_max98388_data = {
        .hs_cpu_id = I2S_SP,
        .amp_cpu_id = I2S_HS,
        .bt_cpu_id = I2S_BT,
-       .dmic_cpu_id = NONE,
        .hs_codec_id = NAU8821,
        .amp_codec_id = MAX98388,
-       .bt_codec_id = NONE,
-       .dmic_codec_id = NONE,
        .soc_mclk = true,
-       .tdm_mode = false,
 };
 
 static int acp_sof_probe(struct platform_device *pdev)
@@ -126,16 +116,14 @@ static int acp_sof_probe(struct platform_device *pdev)
        if (dmi_id && dmi_id->driver_data)
                acp_card_drvdata->tdm_mode = dmi_id->driver_data;
 
-       acp_sofdsp_dai_links_create(card);
+       ret = acp_sofdsp_dai_links_create(card);
+       if (ret)
+               return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n");
 
        ret = devm_snd_soc_register_card(&pdev->dev, card);
-       if (ret) {
-               dev_err(&pdev->dev,
-                               "devm_snd_soc_register_card(%s) failed: %d\n",
-                               card->name, ret);
-               return ret;
-       }
-
+       if (ret)
+               return dev_err_probe(&pdev->dev, ret,
+                                    "Failed to register card(%s)\n", card->name);
        return 0;
 }
 
@@ -182,7 +170,7 @@ static struct platform_driver acp_asoc_audio = {
 module_platform_driver(acp_asoc_audio);
 
 MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
-MODULE_DESCRIPTION("ACP chrome SOF audio support");
+MODULE_DESCRIPTION("ACP SOF Machine Driver");
 MODULE_ALIAS("platform:rt5682-rt1019");
 MODULE_ALIAS("platform:rt5682-max");
 MODULE_ALIAS("platform:rt5682s-max");
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
new file mode 100644 (file)
index 0000000..babd841
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+
+/*
+ * SDW AMD ACPI scan helper function
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fwnode.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw_amd.h>
+#include <linux/string.h>
+
+int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info)
+{
+       struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+       u32 sdw_bitmap = 0;
+       u8 count = 0;
+       int ret;
+
+       if (!adev)
+               return -EINVAL;
+
+       /* Found controller, find links supported */
+       ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev),
+                                            "mipi-sdw-manager-list", &sdw_bitmap, 1);
+       if (ret) {
+               dev_err(&adev->dev,
+                       "Failed to read mipi-sdw-manager-list: %d\n", ret);
+               return -EINVAL;
+       }
+       count = hweight32(sdw_bitmap);
+       /* Check count is within bounds */
+       if (count > info->count) {
+               dev_err(&adev->dev, "Manager count %d exceeds max %d\n",
+                       count, info->count);
+               return -EINVAL;
+       }
+
+       if (!count) {
+               dev_dbg(&adev->dev, "No SoundWire Managers detected\n");
+               return -EINVAL;
+       }
+       dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count);
+       info->link_mask = sdw_bitmap;
+       return 0;
+}
+EXPORT_SYMBOL_NS(amd_sdw_scan_controller, SND_AMD_SOUNDWIRE_ACPI);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("AMD SoundWire ACPI helpers");
index f2a5eaf2fa4d26f8b9e20808f4cfe086c1493338..b3c254886fd94f4bf7664ed642ffde7eba9084dc 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0+
+# SPDX-License-Identifier: GPL-2.0-only
 # Pink Sardine platform Support
 snd-pci-ps-objs := pci-ps.o
 snd-ps-pdm-dma-objs := ps-pdm-dma.o
index 8b853b8d02199232fa1d3f979f35c675ae8765ae..39208305dd6c3c64d88ea404e427b1720ffd2df7 100644 (file)
@@ -5,12 +5,12 @@
  * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved.
  */
 
+#include <linux/soundwire/sdw_amd.h>
 #include <sound/acp63_chip_offset_byte.h>
 
 #define ACP_DEVICE_ID 0x15E2
 #define ACP63_REG_START                0x1240000
-#define ACP63_REG_END          0x1250200
-#define ACP63_DEVS             5
+#define ACP63_REG_END          0x125C000
 
 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK  0x00010001
 #define ACP_PGFSM_CNTL_POWER_ON_MASK   1
 
 #define ACP_DMIC_DEV   2
 
-/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */
-#define ACP63_PDM_MODE_DEVS            3
-
-/*
- * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for
- * SW0 SoundWire manager instance configuration
- */
-#define ACP63_SDW0_MODE_DEVS           2
-
-/*
- * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager
- * instances configuration
- */
-#define ACP63_SDW0_SDW1_MODE_DEVS      3
-
-/*
- * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager
- * instance + ACP PDM controller configuration
- */
-#define ACP63_SDW0_PDM_MODE_DEVS       4
-
-/*
- * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for
- * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration
- */
-#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
 #define ACP63_DMIC_ADDR                        2
 #define ACP63_SDW_ADDR                 5
 #define AMD_SDW_MAX_MANAGERS           2
 /* time in ms for acp timeout */
 #define ACP_TIMEOUT            500
 
-/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */
-#define ACP63_PDM_DEV_CONFIG           BIT(0)
-
-/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */
-#define ACP63_SDW_DEV_CONFIG           BIT(1)
-
-/*
- * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire
- * manager instance combination.
- */
-#define ACP63_SDW_PDM_DEV_CONFIG       GENMASK(1, 0)
 #define ACP_SDW0_STAT                  BIT(21)
 #define ACP_SDW1_STAT                  BIT(2)
 #define ACP_ERROR_IRQ                  BIT(29)
@@ -253,38 +216,46 @@ struct sdw_dma_ring_buf_reg {
  * struct acp63_dev_data - acp pci driver context
  * @acp63_base: acp mmio base
  * @res: resource
- * @pdev: array of child platform device node structures
+ * @pdm_dev: ACP PDM controller platform device
+ * @dmic_codec: platform device for DMIC Codec
+ * sdw_dma_dev: platform device for SoundWire DMA controller
+ * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration
  * @acp_lock: used to protect acp common registers
- * @sdw_fw_node: SoundWire controller fw node handle
- * @pdev_config: platform device configuration
- * @pdev_count: platform devices count
- * @pdm_dev_index: pdm platform device index
- * @sdw_manager_count: SoundWire manager instance count
- * @sdw0_dev_index: SoundWire Manager-0 platform device index
- * @sdw1_dev_index: SoundWire Manager-1 platform device index
- * @sdw_dma_dev_index: SoundWire DMA controller platform device index
+ * @info: SoundWire AMD information found in ACPI tables
+ * @sdw: SoundWire context for all SoundWire manager instances
+ * @machine: ACPI machines for SoundWire interface
+ * @is_sdw_dev: flag set to true when any SoundWire manager instances are available
+ * @is_pdm_dev: flag set to true when ACP PDM controller exists
+ * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
+ * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
+ * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
+ * @addr: pci ioremap address
+ * @reg_range: ACP reigister range
  * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
  * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
- * @acp_reset: flag set to true when bus reset is applied across all
- * the active SoundWire manager instances
  */
 
 struct acp63_dev_data {
        void __iomem *acp63_base;
        struct resource *res;
-       struct platform_device *pdev[ACP63_DEVS];
+       struct platform_device *pdm_dev;
+       struct platform_device *dmic_codec_dev;
+       struct platform_device *sdw_dma_dev;
+       struct platform_device *mach_dev;
        struct mutex acp_lock; /* protect shared registers */
-       struct fwnode_handle *sdw_fw_node;
-       u16 pdev_config;
-       u16 pdev_count;
-       u16 pdm_dev_index;
-       u8 sdw_manager_count;
-       u16 sdw0_dev_index;
-       u16 sdw1_dev_index;
-       u16 sdw_dma_dev_index;
+       struct sdw_amd_acpi_info info;
+       /* sdw context allocated by SoundWire driver */
+       struct sdw_amd_ctx *sdw;
+       struct snd_soc_acpi_mach *machines;
+       bool is_sdw_dev;
+       bool is_pdm_dev;
+       bool is_pdm_config;
+       bool is_sdw_config;
+       bool sdw_en_stat;
+       u32 addr;
+       u32 reg_range;
        u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
        u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
-       bool acp_reset;
 };
 
 int snd_amd_acp_find_config(struct pci_dev *pci);
index 5927eef04170011d345729357950874eb8e5341f..c72d666d51bdf426897dca3b61379642543f09cb 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * AMD Pink Sardine ACP PCI Driver
  *
@@ -17,6 +17,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/iopoll.h>
 #include <linux/soundwire/sdw_amd.h>
+#include "../mach-config.h"
 
 #include "acp63.h"
 
@@ -104,10 +105,8 @@ static irqreturn_t acp63_irq_thread(int irq, void *context)
        struct sdw_dma_dev_data *sdw_dma_data;
        struct acp63_dev_data *adata = context;
        u32 stream_index;
-       u16 pdev_index;
 
-       pdev_index = adata->sdw_dma_dev_index;
-       sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+       sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
 
        for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
                if (adata->sdw0_dma_intr_stat[stream_index]) {
@@ -135,7 +134,6 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
        u32 stream_id = 0;
        u16 irq_flag = 0;
        u16 sdw_dma_irq_flag = 0;
-       u16 pdev_index;
        u16 index;
 
        adata = dev_id;
@@ -149,8 +147,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
        ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
        if (ext_intr_stat & ACP_SDW0_STAT) {
                writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
-               pdev_index = adata->sdw0_dev_index;
-               amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+               amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
                if (amd_manager)
                        schedule_work(&amd_manager->amd_sdw_irq_thread);
                irq_flag = 1;
@@ -159,8 +156,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
        ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
        if (ext_intr_stat1 & ACP_SDW1_STAT) {
                writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
-               pdev_index = adata->sdw1_dev_index;
-               amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+               amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
                if (amd_manager)
                        schedule_work(&amd_manager->amd_sdw_irq_thread);
                irq_flag = 1;
@@ -176,8 +172,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
        }
 
        if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
-               pdev_index = adata->pdm_dev_index;
-               ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+               ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
                writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
                if (ps_pdm_data->capture_stream)
                        snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
@@ -237,122 +232,165 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
                return IRQ_NONE;
 }
 
-static int sdw_amd_scan_controller(struct device *dev)
+#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE)
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
 {
+       struct acpi_device *sdw_dev;
        struct acp63_dev_data *acp_data;
-       struct fwnode_handle *link;
-       char name[32];
-       u32 sdw_manager_bitmap;
-       u8 count = 0;
-       u32 acp_sdw_power_mode = 0;
-       int index;
+
+       acp_data = dev_get_drvdata(dev);
+       if (!addr)
+               return -ENODEV;
+
+       sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0);
+       if (!sdw_dev)
+               return -ENODEV;
+
+       acp_data->info.handle = sdw_dev->handle;
+       acp_data->info.count = AMD_SDW_MAX_MANAGERS;
+       return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+       struct acp63_dev_data *acp_data;
+       struct sdw_amd_res sdw_res;
        int ret;
 
        acp_data = dev_get_drvdata(dev);
-       /*
-        * Current implementation is based on MIPI DisCo 2.0 spec.
-        * Found controller, find links supported.
-        */
-       ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list",
-                                            &sdw_manager_bitmap, 1);
+       memset(&sdw_res, 0, sizeof(sdw_res));
+       sdw_res.addr = acp_data->addr;
+       sdw_res.reg_range = acp_data->reg_range;
+       sdw_res.handle = acp_data->info.handle;
+       sdw_res.parent = dev;
+       sdw_res.dev = dev;
+       sdw_res.acp_lock = &acp_data->acp_lock;
+       sdw_res.count = acp_data->info.count;
+       sdw_res.mmio_base = acp_data->acp63_base;
+       sdw_res.link_mask = acp_data->info.link_mask;
+       ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+       if (ret)
+               dev_err(dev, "error: SoundWire probe failed\n");
+       return ret;
+}
 
-       if (ret) {
-               dev_dbg(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
-               return -EINVAL;
-       }
-       count = hweight32(sdw_manager_bitmap);
-       /* Check count is within bounds */
-       if (count > AMD_SDW_MAX_MANAGERS) {
-               dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS);
-               return -EINVAL;
-       }
-
-       if (!count) {
-               dev_dbg(dev, "No SoundWire Managers detected\n");
-               return -EINVAL;
-       }
-       dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count);
-       acp_data->sdw_manager_count = count;
-       for (index = 0; index < count; index++) {
-               scnprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
-               link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
-               if (!link) {
-                       dev_err(dev, "Manager node %s not found\n", name);
-                       return -EIO;
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+       if (acp_data->sdw)
+               sdw_amd_exit(acp_data->sdw);
+       acp_data->sdw = NULL;
+
+       return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+       struct snd_soc_acpi_mach *mach;
+       const struct snd_soc_acpi_link_adr *link;
+       struct acp63_dev_data *acp_data = dev_get_drvdata(dev);
+       int ret, i;
+
+       if (acp_data->info.count) {
+               ret = sdw_amd_get_slave_info(acp_data->sdw);
+               if (ret) {
+                       dev_dbg(dev, "failed to read slave information\n");
+                       return NULL;
+               }
+               for (mach = acp_data->machines; mach; mach++) {
+                       if (!mach->links)
+                               break;
+                       link = mach->links;
+                       for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+                               if (!snd_soc_acpi_sdw_link_slaves_found(dev, link,
+                                                                       acp_data->sdw->ids,
+                                                                       acp_data->sdw->num_slaves))
+                                       break;
+                       }
+                       if (i == acp_data->info.count || !link->num_adr)
+                               break;
+               }
+               if (mach && mach->link_mask) {
+                       mach->mach_params.links = mach->links;
+                       mach->mach_params.link_mask = mach->link_mask;
+                       return mach;
                }
+       }
+       dev_dbg(dev, "No SoundWire machine driver found\n");
+       return NULL;
+}
+#else
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+       return 0;
+}
 
-               ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode);
-               if (ret)
-                       return ret;
-               /*
-                * when SoundWire configuration is selected from acp pin config,
-                * based on manager instances count, acp init/de-init sequence should be
-                * executed as part of PM ops only when Bus reset is applied for the active
-                * SoundWire manager instances.
-                */
-               if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) {
-                       acp_data->acp_reset = false;
-                       return 0;
+static int amd_sdw_probe(struct device *dev)
+{
+       return 0;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+       return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+       return NULL;
+}
+#endif
+
+static int acp63_machine_register(struct device *dev)
+{
+       struct snd_soc_acpi_mach *mach;
+       struct acp63_dev_data *adata = dev_get_drvdata(dev);
+       int size;
+
+       if (adata->is_sdw_dev && adata->is_sdw_config) {
+               size = sizeof(*adata->machines);
+               mach = acp63_sdw_machine_select(dev);
+               if (mach) {
+                       adata->mach_dev = platform_device_register_data(dev, mach->drv_name,
+                                                                       PLATFORM_DEVID_NONE, mach,
+                                                                       size);
+                       if (IS_ERR(adata->mach_dev)) {
+                               dev_err(dev,
+                                       "cannot register Machine device for SoundWire Interface\n");
+                               return PTR_ERR(adata->mach_dev);
+                       }
+               }
+
+       } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) {
+               adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach",
+                                                               PLATFORM_DEVID_NONE, NULL, 0);
+               if (IS_ERR(adata->mach_dev)) {
+                       dev_err(dev, "cannot register amd_ps_mach device\n");
+                       return PTR_ERR(adata->mach_dev);
                }
        }
        return 0;
 }
 
-static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
+static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
 {
-       struct acpi_device *dmic_dev;
-       struct acpi_device *sdw_dev;
+       struct acpi_device *pdm_dev;
        const union acpi_object *obj;
+       u32 config;
        bool is_dmic_dev = false;
        bool is_sdw_dev = false;
        int ret;
 
-       dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
-       if (dmic_dev) {
-               /* is_dmic_dev flag will be set when ACP PDM controller device exists */
-               if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
-                                          ACPI_TYPE_INTEGER, &obj) &&
-                                          obj->integer.value == ACP_DMIC_DEV)
-                       is_dmic_dev = true;
-       }
-
-       sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
-       if (sdw_dev) {
-               acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
-               ret = sdw_amd_scan_controller(&pci->dev);
-               /* is_sdw_dev flag will be set when SoundWire Manager device exists */
-               if (!ret)
-                       is_sdw_dev = true;
-       }
-       if (!is_dmic_dev && !is_sdw_dev)
-               return -ENODEV;
-       dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+       config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
        switch (config) {
        case ACP_CONFIG_4:
        case ACP_CONFIG_5:
        case ACP_CONFIG_10:
        case ACP_CONFIG_11:
-               if (is_dmic_dev) {
-                       acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
-                       acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
-               }
+               acp_data->is_pdm_config = true;
                break;
        case ACP_CONFIG_2:
        case ACP_CONFIG_3:
-               if (is_sdw_dev) {
-                       switch (acp_data->sdw_manager_count) {
-                       case 1:
-                               acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
-                               break;
-                       case 2:
-                               acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-               }
+               acp_data->is_sdw_config = true;
                break;
        case ACP_CONFIG_6:
        case ACP_CONFIG_7:
@@ -360,40 +398,36 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63
        case ACP_CONFIG_8:
        case ACP_CONFIG_13:
        case ACP_CONFIG_14:
-               if (is_dmic_dev && is_sdw_dev) {
-                       switch (acp_data->sdw_manager_count) {
-                       case 1:
-                               acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
-                               break;
-                       case 2:
-                               acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-               } else if (is_dmic_dev) {
-                       acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
-                       acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
-               } else if (is_sdw_dev) {
-                       switch (acp_data->sdw_manager_count) {
-                       case 1:
-                               acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
-                               break;
-                       case 2:
-                               acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
-                               acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-               }
+               acp_data->is_pdm_config = true;
+               acp_data->is_sdw_config = true;
                break;
        default:
                break;
        }
+
+       if (acp_data->is_pdm_config) {
+               pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+               if (pdm_dev) {
+                       /* is_dmic_dev flag will be set when ACP PDM controller device exists */
+                       if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+                                                  ACPI_TYPE_INTEGER, &obj) &&
+                                                  obj->integer.value == ACP_DMIC_DEV)
+                               is_dmic_dev = true;
+               }
+       }
+
+       if (acp_data->is_sdw_config) {
+               ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR);
+               if (!ret && acp_data->info.link_mask)
+                       is_sdw_dev = true;
+       }
+
+       acp_data->is_pdm_dev = is_dmic_dev;
+       acp_data->is_sdw_dev = is_sdw_dev;
+       if (!is_dmic_dev && !is_sdw_dev) {
+               dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
+               return -ENODEV;
+       }
        return 0;
 }
 
@@ -418,17 +452,13 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
 
 static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
 {
-       struct acp_sdw_pdata *sdw_pdata;
-       struct platform_device_info pdevinfo[ACP63_DEVS];
+       struct platform_device_info pdevinfo;
        struct device *parent;
-       int index;
        int ret;
 
        parent = &pci->dev;
-       dev_dbg(&pci->dev,
-               "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config,
-               adata->pdev_count);
-       if (adata->pdev_config) {
+
+       if (adata->is_sdw_dev || adata->is_pdm_dev) {
                adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
                if (!adata->res) {
                        ret = -ENOMEM;
@@ -440,130 +470,57 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
                memset(&pdevinfo, 0, sizeof(pdevinfo));
        }
 
-       switch (adata->pdev_config) {
-       case ACP63_PDM_DEV_CONFIG:
-               adata->pdm_dev_index  = 0;
-               acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+       if (adata->is_pdm_dev && adata->is_pdm_config) {
+               acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma",
                                             0, adata->res, 1, NULL, 0);
-               acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec",
-                                            0, NULL, 0, NULL, 0);
-               acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
-                                            0, NULL, 0, NULL, 0);
-               break;
-       case ACP63_SDW_DEV_CONFIG:
-               if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) {
-                       sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
-                                                GFP_KERNEL);
-                       if (!sdw_pdata) {
-                               ret = -ENOMEM;
-                               goto de_init;
-                       }
 
-                       sdw_pdata->instance = 0;
-                       sdw_pdata->acp_sdw_lock = &adata->acp_lock;
-                       adata->sdw0_dev_index = 0;
-                       adata->sdw_dma_dev_index = 1;
-                       acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 0, adata->res, 1,
-                                                    sdw_pdata, sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma",
-                                                    0, adata->res, 1, NULL, 0);
-               } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) {
-                       sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
-                                                GFP_KERNEL);
-                       if (!sdw_pdata) {
-                               ret = -ENOMEM;
-                               goto de_init;
-                       }
-
-                       sdw_pdata[0].instance = 0;
-                       sdw_pdata[1].instance = 1;
-                       sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
-                       sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
-                       sdw_pdata->acp_sdw_lock = &adata->acp_lock;
-                       adata->sdw0_dev_index = 0;
-                       adata->sdw1_dev_index = 1;
-                       adata->sdw_dma_dev_index = 2;
-                       acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 0, adata->res, 1,
-                                                    &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 1, adata->res, 1,
-                                                    &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
-                                                    0, adata->res, 1, NULL, 0);
+               adata->pdm_dev = platform_device_register_full(&pdevinfo);
+               if (IS_ERR(adata->pdm_dev)) {
+                       dev_err(&pci->dev,
+                               "cannot register %s device\n", pdevinfo.name);
+                       ret = PTR_ERR(adata->pdm_dev);
+                       goto de_init;
                }
-               break;
-       case ACP63_SDW_PDM_DEV_CONFIG:
-               if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) {
-                       sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
-                                                GFP_KERNEL);
-                       if (!sdw_pdata) {
-                               ret = -ENOMEM;
-                               goto de_init;
-                       }
-
-                       sdw_pdata->instance = 0;
-                       sdw_pdata->acp_sdw_lock = &adata->acp_lock;
-                       adata->pdm_dev_index = 0;
-                       adata->sdw0_dev_index = 1;
-                       adata->sdw_dma_dev_index = 2;
-                       acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
-                                                    0, adata->res, 1, NULL, 0);
-                       acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 0, adata->res, 1,
-                                                    sdw_pdata, sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
-                                                    0, adata->res, 1, NULL, 0);
-                       acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec",
-                                                    0, NULL, 0, NULL, 0);
-               } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) {
-                       sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
-                                                GFP_KERNEL);
-                       if (!sdw_pdata) {
-                               ret = -ENOMEM;
-                               goto de_init;
-                       }
-                       sdw_pdata[0].instance = 0;
-                       sdw_pdata[1].instance = 1;
-                       sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
-                       sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
-                       adata->pdm_dev_index = 0;
-                       adata->sdw0_dev_index = 1;
-                       adata->sdw1_dev_index = 2;
-                       adata->sdw_dma_dev_index = 3;
-                       acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
-                                                    0, adata->res, 1, NULL, 0);
-                       acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 0, adata->res, 1,
-                                                    &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node,
-                                                    "amd_sdw_manager", 1, adata->res, 1,
-                                                    &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
-                       acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma",
-                                                    0, adata->res, 1, NULL, 0);
-                       acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec",
-                                                    0, NULL, 0, NULL, 0);
+               memset(&pdevinfo, 0, sizeof(pdevinfo));
+               acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec",
+                                            0, NULL, 0, NULL, 0);
+               adata->dmic_codec_dev = platform_device_register_full(&pdevinfo);
+               if (IS_ERR(adata->dmic_codec_dev)) {
+                       dev_err(&pci->dev,
+                               "cannot register %s device\n", pdevinfo.name);
+                       ret = PTR_ERR(adata->dmic_codec_dev);
+                       goto unregister_pdm_dev;
                }
-               break;
-       default:
-               dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
-               return 0;
        }
+       if (adata->is_sdw_dev && adata->is_sdw_config) {
+               ret = amd_sdw_probe(&pci->dev);
+               if (ret) {
+                       if (adata->is_pdm_dev)
+                               goto unregister_dmic_codec_dev;
+                       else
+                               goto de_init;
+               }
+               memset(&pdevinfo, 0, sizeof(pdevinfo));
+               acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma",
+                                            0, adata->res, 1, NULL, 0);
 
-       for (index = 0; index < adata->pdev_count; index++) {
-               adata->pdev[index] = platform_device_register_full(&pdevinfo[index]);
-               if (IS_ERR(adata->pdev[index])) {
+               adata->sdw_dma_dev = platform_device_register_full(&pdevinfo);
+               if (IS_ERR(adata->sdw_dma_dev)) {
                        dev_err(&pci->dev,
-                               "cannot register %s device\n", pdevinfo[index].name);
-                       ret = PTR_ERR(adata->pdev[index]);
-                       goto unregister_devs;
+                               "cannot register %s device\n", pdevinfo.name);
+                       ret = PTR_ERR(adata->sdw_dma_dev);
+                       if (adata->is_pdm_dev)
+                               goto unregister_dmic_codec_dev;
+                       else
+                               goto de_init;
                }
        }
+
        return 0;
-unregister_devs:
-       for (--index; index >= 0; index--)
-               platform_device_unregister(adata->pdev[index]);
+unregister_dmic_codec_dev:
+               platform_device_unregister(adata->dmic_codec_dev);
+unregister_pdm_dev:
+               platform_device_unregister(adata->pdm_dev);
 de_init:
        if (acp63_deinit(adata->acp63_base, &pci->dev))
                dev_err(&pci->dev, "ACP de-init failed\n");
@@ -576,7 +533,6 @@ static int snd_acp63_probe(struct pci_dev *pci,
        struct acp63_dev_data *adata;
        u32 addr;
        u32 irqflags, flag;
-       int val;
        int ret;
 
        irqflags = IRQF_SHARED;
@@ -618,13 +574,8 @@ static int snd_acp63_probe(struct pci_dev *pci,
                ret = -ENOMEM;
                goto release_regions;
        }
-       /*
-        * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init()
-        * will be invoked for all ACP configurations during suspend/resume callbacks.
-        * This flag should be set to false only when SoundWire manager power mode
-        * set to ClockStopMode.
-        */
-       adata->acp_reset = true;
+       adata->addr = addr;
+       adata->reg_range = ACP63_REG_END - ACP63_REG_START;
        pci_set_master(pci);
        pci_set_drvdata(pci, adata);
        mutex_init(&adata->acp_lock);
@@ -637,8 +588,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
                dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
                goto de_init;
        }
-       val = readl(adata->acp63_base + ACP_PIN_CONFIG);
-       ret = get_acp63_device_config(val, pci, adata);
+       ret = get_acp63_device_config(pci, adata);
        /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
        if (ret) {
                dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
@@ -649,6 +599,11 @@ static int snd_acp63_probe(struct pci_dev *pci,
                dev_err(&pci->dev, "ACP platform devices creation failed\n");
                goto de_init;
        }
+       ret = acp63_machine_register(&pci->dev);
+       if (ret) {
+               dev_err(&pci->dev, "ACP machine register failed\n");
+               goto de_init;
+       }
 skip_pdev_creation:
        device_set_wakeup_enable(&pci->dev, true);
        pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
@@ -667,47 +622,103 @@ disable_pci:
        return ret;
 }
 
+static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+{
+       u32 sdw0_en, sdw1_en;
+
+       sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
+       sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
+       return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
+{
+       u32 val;
+
+       val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
+       if (val && adata->sdw->pdev[0])
+               pm_request_resume(&adata->sdw->pdev[0]->dev);
+
+       val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
+       if (val && adata->sdw->pdev[1])
+               pm_request_resume(&adata->sdw->pdev[1]->dev);
+}
+
 static int __maybe_unused snd_acp63_suspend(struct device *dev)
 {
        struct acp63_dev_data *adata;
-       int ret = 0;
+       int ret;
 
        adata = dev_get_drvdata(dev);
-       if (adata->acp_reset) {
-               ret = acp63_deinit(adata->acp63_base, dev);
-               if (ret)
-                       dev_err(dev, "ACP de-init failed\n");
+       if (adata->is_sdw_dev) {
+               adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+               if (adata->sdw_en_stat)
+                       return 0;
        }
+       ret = acp63_deinit(adata->acp63_base, dev);
+       if (ret)
+               dev_err(dev, "ACP de-init failed\n");
+
        return ret;
 }
 
-static int __maybe_unused snd_acp63_resume(struct device *dev)
+static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
 {
        struct acp63_dev_data *adata;
-       int ret = 0;
+       int ret;
 
        adata = dev_get_drvdata(dev);
-       if (adata->acp_reset) {
-               ret = acp63_init(adata->acp63_base, dev);
-               if (ret)
-                       dev_err(dev, "ACP init failed\n");
+       if (adata->sdw_en_stat)
+               return 0;
+
+       ret = acp63_init(adata->acp63_base, dev);
+       if (ret) {
+               dev_err(dev, "ACP init failed\n");
+               return ret;
        }
+
+       if (!adata->sdw_en_stat)
+               handle_acp63_sdw_pme_event(adata);
+       return 0;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+       struct acp63_dev_data *adata;
+       int ret;
+
+       adata = dev_get_drvdata(dev);
+       if (adata->sdw_en_stat)
+               return 0;
+
+       ret = acp63_init(adata->acp63_base, dev);
+       if (ret)
+               dev_err(dev, "ACP init failed\n");
+
        return ret;
 }
 
 static const struct dev_pm_ops acp63_pm_ops = {
-       SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_resume, NULL)
+       SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL)
        SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume)
 };
 
 static void snd_acp63_remove(struct pci_dev *pci)
 {
        struct acp63_dev_data *adata;
-       int ret, index;
+       int ret;
 
        adata = pci_get_drvdata(pci);
-       for (index = 0; index < adata->pdev_count; index++)
-               platform_device_unregister(adata->pdev[index]);
+       if (adata->sdw) {
+               amd_sdw_exit(adata);
+               platform_device_unregister(adata->sdw_dma_dev);
+       }
+       if (adata->is_pdm_dev) {
+               platform_device_unregister(adata->pdm_dev);
+               platform_device_unregister(adata->dmic_codec_dev);
+       }
+       if (adata->mach_dev)
+               platform_device_unregister(adata->mach_dev);
        ret = acp63_deinit(adata->acp63_base, &pci->dev);
        if (ret)
                dev_err(&pci->dev, "ACP de-init failed\n");
@@ -740,4 +751,6 @@ module_pci_driver(ps_acp63_driver);
 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
 MODULE_AUTHOR("Syed.SabaKareem@amd.com");
 MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver");
+MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT);
+MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI);
 MODULE_LICENSE("GPL v2");
index 3ffbe4fdafdfccd1192e743b3659140230af06bf..e675b8f569eb3099095022b80b69b74aa9aafb6f 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Machine driver for AMD Pink Sardine platform using DMIC
  *
index d48f7c5af289c740396fe006f1aced620f8ad35b..7bbacbab10950114c5550fcfd71f60514c2a6a60 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * AMD ALSA SoC Pink Sardine PDM Driver
  *
index 9b59063798f2ef470f5d9093cc2ad255de58909a..66b800962f8cc4917ac236643ca699bb82499ff3 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * AMD ALSA SoC Pink Sardine SoundWire DMA Driver
  *
index 18a8760443ae6a66aa5b33942f5cee0c1c2b654c..8341a6e0649307f27239a1abd07cc571284806b3 100644 (file)
@@ -140,7 +140,7 @@ static int snd_proto_probe(struct platform_device *pdev)
 
 
        dai->dai_fmt = dai_fmt;
-       ret = snd_soc_register_card(&snd_proto);
+       ret = devm_snd_soc_register_card(&pdev->dev, &snd_proto);
        if (ret)
                dev_err_probe(&pdev->dev, ret,
                        "snd_soc_register_card() failed\n");
@@ -155,11 +155,6 @@ put_codec_node:
        return ret;
 }
 
-static void snd_proto_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&snd_proto);
-}
-
 static const struct of_device_id snd_proto_of_match[] = {
        { .compatible = "mikroe,mikroe-proto", },
        {},
@@ -172,7 +167,6 @@ static struct platform_driver snd_proto_driver = {
                .of_match_table = snd_proto_of_match,
        },
        .probe    = snd_proto_probe,
-       .remove_new      = snd_proto_remove,
 };
 
 module_platform_driver(snd_proto_driver);
index 59f9742e9ff49a81a6d2f5bf0b988a48283adac6..f78ea2f86fa64f5a0a2614d0734f71efded8862f 100644 (file)
@@ -114,6 +114,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_ES8328_I2C
        imply SND_SOC_ES7134
        imply SND_SOC_ES7241
+       imply SND_SOC_FRAMER
        imply SND_SOC_GTM601
        imply SND_SOC_HDAC_HDMI
        imply SND_SOC_HDAC_HDA
@@ -276,6 +277,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_WCD9335
        imply SND_SOC_WCD934X
        imply SND_SOC_WCD938X_SDW
+       imply SND_SOC_WCD939X_SDW
        imply SND_SOC_LPASS_MACRO_COMMON
        imply SND_SOC_LPASS_RX_MACRO
        imply SND_SOC_LPASS_TX_MACRO
@@ -727,6 +729,22 @@ config SND_SOC_CROS_EC_CODEC
          If you say yes here you will get support for the
          ChromeOS Embedded Controller's Audio Codec.
 
+config SND_SOC_CS_AMP_LIB
+       tristate
+
+config SND_SOC_CS_AMP_LIB_TEST
+       tristate "KUnit test for Cirrus Logic cs-amp-lib"
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       select SND_SOC_CS_AMP_LIB
+       help
+         This builds KUnit tests for the Cirrus Logic common
+         amplifier library.
+         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_CS35L32
        tristate "Cirrus Logic CS35L32 CODEC"
        depends on I2C
@@ -795,6 +813,7 @@ config SND_SOC_CS35L56
        tristate
 
 config SND_SOC_CS35L56_SHARED
+       select SND_SOC_CS_AMP_LIB
        tristate
 
 config SND_SOC_CS35L56_I2C
@@ -1101,6 +1120,20 @@ config SND_SOC_ES8328_SPI
        depends on SPI_MASTER
        select SND_SOC_ES8328
 
+config SND_SOC_FRAMER
+       tristate "Framer codec"
+       depends on GENERIC_FRAMER
+       help
+         Enable support for the framer codec.
+         The framer codec uses the generic framer infrastructure to transport
+         some audio data over an analog E1/T1/J1 line.
+         This codec allows to use some of the time slots available on the TDM
+         bus on which the framer is connected to transport the audio data.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-soc-framer.
+
+
 config SND_SOC_GTM601
        tristate 'GTM601 UMTS modem audio codec'
 
@@ -2059,6 +2092,25 @@ config SND_SOC_WCD938X_SDW
          The WCD9380/9385 is a audio codec IC Integrated in
          Qualcomm SoCs like SM8250.
 
+config SND_SOC_WCD939X
+       depends on SND_SOC_WCD939X_SDW
+       tristate
+       depends on SOUNDWIRE || !SOUNDWIRE
+       depends on TYPEC || !TYPEC
+       select SND_SOC_WCD_CLASSH
+
+config SND_SOC_WCD939X_SDW
+       tristate "WCD9390/WCD9395 Codec - SDW"
+       depends on TYPEC || !TYPEC
+       select SND_SOC_WCD939X
+       select SND_SOC_WCD_MBHC
+       select REGMAP_IRQ
+       depends on SOUNDWIRE
+       select REGMAP_SOUNDWIRE
+       help
+         The WCD9390/9395 is a audio codec IC Integrated in
+         Qualcomm SoCs like SM8650.
+
 config SND_SOC_WL1273
        tristate
 
@@ -2300,7 +2352,6 @@ config SND_SOC_WSA881X
        tristate "WSA881X Codec"
        depends on SOUNDWIRE
        select REGMAP_SOUNDWIRE
-       tristate
        help
          This enables support for Qualcomm WSA8810/WSA8815 Class-D
          Smart Speaker Amplifier.
@@ -2309,7 +2360,6 @@ config SND_SOC_WSA883X
        tristate "WSA883X Codec"
        depends on SOUNDWIRE
        select REGMAP_SOUNDWIRE
-       tristate
        help
          This enables support for Qualcomm WSA8830/WSA8835 Class-D
          Smart Speaker Amplifier.
@@ -2318,7 +2368,6 @@ config SND_SOC_WSA884X
        tristate "WSA884X Codec"
        depends on SOUNDWIRE
        select REGMAP_SOUNDWIRE
-       tristate
        help
          This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D
          Smart Speaker Amplifier.
index f53baa2b956519ecb224fd28031b56c8f50ec290..7c075539dc476c97f59399de23e1f536f0c99558 100644 (file)
@@ -59,6 +59,8 @@ snd-soc-chv3-codec-objs := chv3-codec.o
 snd-soc-cpcap-objs := cpcap.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cros-ec-codec-objs := cros_ec_codec.o
+snd-soc-cs-amp-lib-objs := cs-amp-lib.o
+snd-soc-cs-amp-lib-test-objs := cs-amp-lib-test.o
 snd-soc-cs35l32-objs := cs35l32.o
 snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs35l34-objs := cs35l34.o
@@ -122,6 +124,7 @@ snd-soc-es8326-objs := es8326.o
 snd-soc-es8328-objs := es8328.o
 snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
+snd-soc-framer-objs := framer-codec.o
 snd-soc-gtm601-objs := gtm601.o
 snd-soc-hdac-hdmi-objs := hdac_hdmi.o
 snd-soc-hdac-hda-objs := hdac_hda.o
@@ -313,6 +316,8 @@ snd-soc-wcd9335-objs := wcd9335.o
 snd-soc-wcd934x-objs := wcd934x.o
 snd-soc-wcd938x-objs := wcd938x.o
 snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o
+snd-soc-wcd939x-objs := wcd939x.o
+snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm-adsp-objs := wm_adsp.o
 snd-soc-wm0010-objs := wm0010.o
@@ -449,6 +454,8 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CPCAP)    += snd-soc-cpcap.o
 obj-$(CONFIG_SND_SOC_CROS_EC_CODEC)    += snd-soc-cros-ec-codec.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB)       += snd-soc-cs-amp-lib.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST)  += snd-soc-cs-amp-lib-test.o
 obj-$(CONFIG_SND_SOC_CS35L32)  += snd-soc-cs35l32.o
 obj-$(CONFIG_SND_SOC_CS35L33)  += snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS35L34)  += snd-soc-cs35l34.o
@@ -512,6 +519,7 @@ obj-$(CONFIG_SND_SOC_ES8326)    += snd-soc-es8326.o
 obj-$(CONFIG_SND_SOC_ES8328)   += snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_FRAMER)   += snd-soc-framer.o
 obj-$(CONFIG_SND_SOC_GTM601)    += snd-soc-gtm601.o
 obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
 obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
@@ -703,6 +711,11 @@ ifdef CONFIG_SND_SOC_WCD938X_SDW
 # avoid link failure by forcing sdw code built-in when needed
 obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o
 endif
+obj-$(CONFIG_SND_SOC_WCD939X)  += snd-soc-wcd939x.o
+ifdef CONFIG_SND_SOC_WCD939X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
+endif
 obj-$(CONFIG_SND_SOC_WL1273)   += snd-soc-wl1273.o
 obj-$(CONFIG_SND_SOC_WM0010)   += snd-soc-wm0010.o
 obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
new file mode 100644 (file)
index 0000000..15f991b
--- /dev/null
@@ -0,0 +1,709 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus common amplifier library.
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <sound/cs-amp-lib.h>
+
+struct cs_amp_lib_test_priv {
+       struct platform_device amp_pdev;
+
+       struct cirrus_amp_efi_data *cal_blob;
+       struct list_head ctl_write_list;
+};
+
+struct cs_amp_lib_test_ctl_write_entry {
+       struct list_head list;
+       unsigned int value;
+       char name[16];
+};
+
+struct cs_amp_lib_test_param {
+       int num_amps;
+       int amp_index;
+};
+
+static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       unsigned int blob_size;
+
+       blob_size = offsetof(struct cirrus_amp_efi_data, data) +
+                   sizeof(struct cirrus_amp_cal_data) * num_amps;
+
+       priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+
+       priv->cal_blob->size = blob_size;
+       priv->cal_blob->count = num_amps;
+
+       get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps);
+}
+
+static u64 cs_amp_lib_test_get_target_uid(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       u64 uid;
+
+       uid = priv->cal_blob->data[param->amp_index].calTarget[1];
+       uid <<= 32;
+       uid |= priv->cal_blob->data[param->amp_index].calTarget[0];
+
+       return uid;
+}
+
+/* Redirected get_efi_variable to simulate that the file is too short */
+static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name,
+                                                           efi_guid_t *guid,
+                                                           unsigned long *size,
+                                                           void *buf)
+{
+       if (!buf) {
+               *size = offsetof(struct cirrus_amp_efi_data, data) - 1;
+               return EFI_BUFFER_TOO_SMALL;
+       }
+
+       return EFI_NOT_FOUND;
+}
+
+/* Should return -EOVERFLOW if the header is larger than the EFI data */
+static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable_nohead);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the count is larger than the file */
+static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name,
+                                                              efi_guid_t *guid,
+                                                              unsigned long *size,
+                                                              void *buf)
+{
+       struct kunit *test = kunit_get_current_test();
+       struct cs_amp_lib_test_priv *priv = test->priv;
+
+       if (!buf) {
+               /*
+                * Return a size that is shorter than required for the
+                * declared number of entries.
+                */
+               *size = priv->cal_blob->size - 1;
+               return EFI_BUFFER_TOO_SMALL;
+       }
+
+       memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1);
+
+       return EFI_SUCCESS;
+}
+
+/* Should return -EOVERFLOW if the entry count is larger than the EFI data */
+static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable_bad_count);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the variable not found */
+static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name,
+                                                         efi_guid_t *guid,
+                                                         unsigned long *size,
+                                                         void *buf)
+{
+       return EFI_NOT_FOUND;
+}
+
+/* If EFI doesn't contain a cal data variable the result should be -ENOENT */
+static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable_none);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate reading a cal data blob */
+static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
+                                                    efi_guid_t *guid,
+                                                    unsigned long *size,
+                                                    void *buf)
+{
+       static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData";
+       static const efi_guid_t expected_guid =
+               EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+       struct kunit *test = kunit_get_current_test();
+       struct cs_amp_lib_test_priv *priv = test->priv;
+
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+       KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name));
+       KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid));
+
+       if (!buf) {
+               *size = priv->cal_blob->size;
+               return EFI_BUFFER_TOO_SMALL;
+       }
+
+       KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+       memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+       return EFI_SUCCESS;
+}
+
+/* Get cal data block for a given amp, matched by target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       struct cirrus_amp_cal_data result_data;
+       u64 target_uid;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       target_uid = cs_amp_lib_test_get_target_uid(test);
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, 0);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+       KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL);
+       KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32);
+       KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+                             priv->cal_blob->data[param->amp_index].calTime[0]);
+       KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+                             priv->cal_blob->data[param->amp_index].calTime[1]);
+       KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+                             priv->cal_blob->data[param->amp_index].calAmbient);
+       KUNIT_EXPECT_EQ(test, result_data.calStatus,
+                             priv->cal_blob->data[param->amp_index].calStatus);
+       KUNIT_EXPECT_EQ(test, result_data.calR,
+                             priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index without checking target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0,
+                                             param->amp_index, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, 0);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+       KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+                             priv->cal_blob->data[param->amp_index].calTime[0]);
+       KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+                             priv->cal_blob->data[param->amp_index].calTime[1]);
+       KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+                             priv->cal_blob->data[param->amp_index].calAmbient);
+       KUNIT_EXPECT_EQ(test, result_data.calStatus,
+                             priv->cal_blob->data[param->amp_index].calStatus);
+       KUNIT_EXPECT_EQ(test, result_data.calR,
+                             priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index with checked target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       struct cirrus_amp_cal_data result_data;
+       u64 target_uid;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       target_uid = cs_amp_lib_test_get_target_uid(test);
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
+                                             param->amp_index, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, 0);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+       KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+                             priv->cal_blob->data[param->amp_index].calTime[0]);
+       KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+                             priv->cal_blob->data[param->amp_index].calTime[1]);
+       KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+                             priv->cal_blob->data[param->amp_index].calAmbient);
+       KUNIT_EXPECT_EQ(test, result_data.calStatus,
+                             priv->cal_blob->data[param->amp_index].calStatus);
+       KUNIT_EXPECT_EQ(test, result_data.calR,
+                             priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * Get cal data block for a given amp index with checked target UID.
+ * The UID does not match so the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       struct cirrus_amp_cal_data result_data;
+       u64 target_uid;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       /* Get a target UID that won't match the entry */
+       target_uid = ~cs_amp_lib_test_get_target_uid(test);
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
+                                             param->amp_index, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * Get cal data block for a given amp, where the cal data does not
+ * specify calTarget so the lookup falls back to using the index
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       const struct cs_amp_lib_test_param *param = test->param_value;
+       struct cirrus_amp_cal_data result_data;
+       static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+       int i, ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+       /* Make all the target values zero so they are ignored */
+       for (i = 0; i < priv->cal_blob->count; ++i) {
+               priv->cal_blob->data[i].calTarget[0] = 0;
+               priv->cal_blob->data[i].calTarget[1] = 0;
+       }
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid,
+                                             param->amp_index, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, 0);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+       KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+                             priv->cal_blob->data[param->amp_index].calTime[0]);
+       KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+                             priv->cal_blob->data[param->amp_index].calTime[1]);
+       KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+                             priv->cal_blob->data[param->amp_index].calAmbient);
+       KUNIT_EXPECT_EQ(test, result_data.calStatus,
+                             priv->cal_blob->data[param->amp_index].calStatus);
+       KUNIT_EXPECT_EQ(test, result_data.calR,
+                             priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and there isn't an
+ * index to fall back do, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+       int i, ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Make all the target values != bad_target_uid */
+       for (i = 0; i < priv->cal_blob->count; ++i) {
+               priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+               priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+       }
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1,
+                                             &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and the index is
+ * out of range, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+       int i, ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Make all the target values != bad_target_uid */
+       for (i = 0; i < priv->cal_blob->count; ++i) {
+               priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+               priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+       }
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99,
+                                             &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't given, and the index is out of range, the
+ * result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* If neither the target UID or the index is given the result should be -ENOENT. */
+static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the UID is passed as 0 this must not match an entry with an
+ * unpopulated calTarget
+ */
+static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cirrus_amp_cal_data result_data;
+       int i, ret;
+
+       cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+       /* Make all the target values zero so they are ignored */
+       for (i = 0; i < priv->cal_blob->count; ++i) {
+               priv->cal_blob->data[i].calTarget[0] = 0;
+               priv->cal_blob->data[i].calTarget[1] = 0;
+       }
+
+       /* Redirect calls to get EFI data */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->get_efi_variable,
+                                  cs_amp_lib_test_get_efi_variable);
+
+       ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
+       KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = {
+       .alg_id =       0x9f210,
+       .mem_region =   WMFW_ADSP2_YM,
+       .ambient =      "CAL_AMBIENT",
+       .calr =         "CAL_R",
+       .status =       "CAL_STATUS",
+       .checksum =     "CAL_CHECKSUM",
+};
+
+static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp,
+                                          const struct cirrus_amp_cal_controls *controls,
+                                          const char *ctl_name, u32 val)
+{
+       struct kunit *test = kunit_get_current_test();
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cs_amp_lib_test_ctl_write_entry *entry;
+
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
+       KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
+
+       entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry);
+
+       INIT_LIST_HEAD(&entry->list);
+       strscpy(entry->name, ctl_name, sizeof(entry->name));
+       entry->value = val;
+
+       list_add_tail(&entry->list, &priv->ctl_write_list);
+
+       return 0;
+}
+
+static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+       struct cs_amp_lib_test_ctl_write_entry *entry;
+       struct cirrus_amp_cal_data data;
+       struct cs_dsp *dsp;
+       int ret;
+
+       dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+       dsp->dev = &priv->amp_pdev.dev;
+
+       get_random_bytes(&data, sizeof(data));
+
+       /* Redirect calls to write firmware controls */
+       kunit_activate_static_stub(test,
+                                  cs_amp_test_hooks->write_cal_coeff,
+                                  cs_amp_lib_test_write_cal_coeff);
+
+       ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
+       KUNIT_EXPECT_EQ(test, ret, 0);
+
+       kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff);
+
+       KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4);
+
+       /* Checksum control must be written last */
+       entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list);
+       KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum);
+       KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1);
+       list_del(&entry->list);
+
+       entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+       KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
+       KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient);
+       list_del(&entry->list);
+
+       entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+       KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr);
+       KUNIT_EXPECT_EQ(test, entry->value, data.calR);
+       list_del(&entry->list);
+
+       entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+       KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status);
+       KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
+}
+
+static void cs_amp_lib_test_dev_release(struct device *dev)
+{
+}
+
+static int cs_amp_lib_test_case_init(struct kunit *test)
+{
+       struct cs_amp_lib_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;
+       INIT_LIST_HEAD(&priv->ctl_write_list);
+
+       /* Create dummy amp driver dev */
+       priv->amp_pdev.name = "cs_amp_lib_test_drv";
+       priv->amp_pdev.id = -1;
+       priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release;
+       ret = platform_device_register(&priv->amp_pdev);
+       KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n");
+
+       return 0;
+}
+
+static void cs_amp_lib_test_case_exit(struct kunit *test)
+{
+       struct cs_amp_lib_test_priv *priv = test->priv;
+
+       if (priv->amp_pdev.name)
+               platform_device_unregister(&priv->amp_pdev);
+}
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
+       { .num_amps = 2, .amp_index = 0 },
+       { .num_amps = 2, .amp_index = 1 },
+
+       { .num_amps = 3, .amp_index = 0 },
+       { .num_amps = 3, .amp_index = 1 },
+       { .num_amps = 3, .amp_index = 2 },
+
+       { .num_amps = 4, .amp_index = 0 },
+       { .num_amps = 4, .amp_index = 1 },
+       { .num_amps = 4, .amp_index = 2 },
+       { .num_amps = 4, .amp_index = 3 },
+
+       { .num_amps = 5, .amp_index = 0 },
+       { .num_amps = 5, .amp_index = 1 },
+       { .num_amps = 5, .amp_index = 2 },
+       { .num_amps = 5, .amp_index = 3 },
+       { .num_amps = 5, .amp_index = 4 },
+
+       { .num_amps = 6, .amp_index = 0 },
+       { .num_amps = 6, .amp_index = 1 },
+       { .num_amps = 6, .amp_index = 2 },
+       { .num_amps = 6, .amp_index = 3 },
+       { .num_amps = 6, .amp_index = 4 },
+       { .num_amps = 6, .amp_index = 5 },
+
+       { .num_amps = 8, .amp_index = 0 },
+       { .num_amps = 8, .amp_index = 1 },
+       { .num_amps = 8, .amp_index = 2 },
+       { .num_amps = 8, .amp_index = 3 },
+       { .num_amps = 8, .amp_index = 4 },
+       { .num_amps = 8, .amp_index = 5 },
+       { .num_amps = 8, .amp_index = 6 },
+       { .num_amps = 8, .amp_index = 7 },
+};
+
+static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param,
+                                              char *desc)
+{
+       snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d",
+                param->num_amps, param->amp_index);
+}
+
+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 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),
+       KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test),
+       KUNIT_CASE(cs_amp_lib_test_no_cal_data_test),
+       KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test),
+       KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test),
+       KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
+       KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
+       KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+       KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
+                        cs_amp_lib_test_get_cal_gen_params),
+       KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
+                        cs_amp_lib_test_get_cal_gen_params),
+       KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test,
+                        cs_amp_lib_test_get_cal_gen_params),
+       KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test,
+                        cs_amp_lib_test_get_cal_gen_params),
+       KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test,
+                        cs_amp_lib_test_get_cal_gen_params),
+
+       /* Tests for writing calibration data */
+       KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
+
+       { } /* terminator */
+};
+
+static struct kunit_suite cs_amp_lib_test_suite = {
+       .name = "snd-soc-cs-amp-lib-test",
+       .init = cs_amp_lib_test_case_init,
+       .exit = cs_amp_lib_test_case_exit,
+       .test_cases = cs_amp_lib_test_cases,
+};
+
+kunit_test_suite(cs_amp_lib_test_suite);
+
+MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
new file mode 100644 (file)
index 0000000..01ef4db
--- /dev/null
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus Logic Smart Amplifiers
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+//               Cirrus Logic International Semiconductor Ltd.
+
+#include <asm/byteorder.h>
+#include <kunit/static_stub.h>
+#include <linux/dev_printk.h>
+#include <linux/efi.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#define CS_AMP_CAL_GUID \
+       EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
+
+#define CS_AMP_CAL_NAME        L"CirrusSmartAmpCalibrationData"
+
+static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
+                                 const struct cirrus_amp_cal_controls *controls,
+                                 const char *ctl_name, u32 val)
+{
+       struct cs_dsp_coeff_ctl *cs_ctl;
+       __be32 beval = cpu_to_be32(val);
+       int ret;
+
+       KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
+
+       if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
+               mutex_lock(&dsp->pwr_lock);
+               cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
+               ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
+               mutex_unlock(&dsp->pwr_lock);
+
+               if (ret < 0) {
+                       dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
+                       return ret;
+               }
+
+               return 0;
+       }
+
+       return -ENODEV;
+}
+
+static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+                                   const struct cirrus_amp_cal_controls *controls,
+                                   const struct cirrus_amp_cal_data *data)
+{
+       int ret;
+
+       dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
+               data->calAmbient, data->calStatus, data->calR);
+
+       ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
+       if (ret)
+               return ret;
+
+       ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
+       if (ret)
+               return ret;
+
+       ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
+       if (ret)
+               return ret;
+
+       ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/**
+ * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
+ * @dsp:       Pointer to struct cs_dsp.
+ * @controls:  Pointer to definition of firmware controls to be written.
+ * @data:      Pointer to calibration data.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+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))
+               return _cs_amp_write_cal_coeffs(dsp, controls, data);
+       else
+               return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, SND_SOC_CS_AMP_LIB);
+
+static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
+                                           efi_guid_t *guid,
+                                           unsigned long *size,
+                                           void *buf)
+{
+       u32 attr;
+
+       KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf);
+
+       if (IS_ENABLED(CONFIG_EFI))
+               return efi.get_variable(name, guid, &attr, size, buf);
+
+       return EFI_NOT_FOUND;
+}
+
+static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
+{
+       struct cirrus_amp_efi_data *efi_data;
+       unsigned long data_size = 0;
+       u8 *data;
+       efi_status_t status;
+       int ret;
+
+       /* Get real size of UEFI variable */
+       status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL);
+       if (status != EFI_BUFFER_TOO_SMALL)
+               return ERR_PTR(-ENOENT);
+
+       if (data_size < sizeof(*efi_data)) {
+               dev_err(dev, "EFI cal variable truncated\n");
+               return ERR_PTR(-EOVERFLOW);
+       }
+
+       /* Get variable contents into buffer */
+       data = kmalloc(data_size, GFP_KERNEL);
+       if (!data)
+               return ERR_PTR(-ENOMEM);
+
+       status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data);
+       if (status != EFI_SUCCESS) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       efi_data = (struct cirrus_amp_efi_data *)data;
+       dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
+
+       if ((efi_data->count > 128) ||
+           offsetof(struct cirrus_amp_efi_data, data[efi_data->count]) > data_size) {
+               dev_err(dev, "EFI cal variable truncated\n");
+               ret = -EOVERFLOW;
+               goto err;
+       }
+
+       return efi_data;
+
+err:
+       kfree(data);
+       dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
+
+       return ERR_PTR(ret);
+}
+
+static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
+{
+       return ((u64)data->calTarget[1] << 32) | data->calTarget[0];
+}
+
+static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+                                           struct cirrus_amp_cal_data *out_data)
+{
+       struct cirrus_amp_efi_data *efi_data;
+       struct cirrus_amp_cal_data *cal = NULL;
+       int i, ret;
+
+       efi_data = cs_amp_get_cal_efi_buffer(dev);
+       if (IS_ERR(efi_data))
+               return PTR_ERR(efi_data);
+
+       if (target_uid) {
+               for (i = 0; i < efi_data->count; ++i) {
+                       u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
+
+                       /* Skip entries with unpopulated silicon ID */
+                       if (cal_target == 0)
+                               continue;
+
+                       if (cal_target == target_uid) {
+                               cal = &efi_data->data[i];
+                               break;
+                       }
+               }
+       }
+
+       if (!cal && (amp_index >= 0) && (amp_index < efi_data->count)) {
+               u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
+
+               /*
+                * Treat unpopulated cal_target as a wildcard.
+                * If target_uid != 0 we can only get here if cal_target == 0
+                * or it didn't match any cal_target value.
+                * If target_uid == 0 it is a wildcard.
+                */
+               if ((cal_target == 0) || (target_uid == 0))
+                       cal = &efi_data->data[amp_index];
+               else
+                       dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
+       }
+
+       if (cal) {
+               memcpy(out_data, cal, sizeof(*out_data));
+               ret = 0;
+       } else {
+               dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
+               ret = -ENOENT;
+       }
+
+       kfree(efi_data);
+
+       return ret;
+}
+
+/**
+ * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
+ * @dev:       struct device of the caller.
+ * @target_uid:        UID to match, or zero to ignore UID matching.
+ * @amp_index: Entry index to use, or -1 to prevent lookup by index.
+ * @out_data:  struct cirrus_amp_cal_data where the entry will be copied.
+ *
+ * This function can perform 3 types of lookup:
+ *
+ * (target_uid > 0, amp_index >= 0)
+ *     UID search with fallback to using the array index.
+ *     Search the calibration data for a non-zero calTarget that matches
+ *     target_uid, and if found return that entry. Else, if the entry at
+ *     [amp_index] has calTarget == 0, return that entry. Else fail.
+ *
+ * (target_uid > 0, amp_index < 0)
+ *     UID search only.
+ *     Search the calibration data for a non-zero calTarget that matches
+ *     target_uid, and if found return that entry. Else fail.
+ *
+ * (target_uid == 0, amp_index >= 0)
+ *     Array index fetch only.
+ *     Return the entry at [amp_index].
+ *
+ * An array lookup will be skipped if amp_index exceeds the number of
+ * entries in the calibration array, and in this case the return will
+ * be -ENOENT. An out-of-range amp_index does not prevent matching by
+ * target_uid - it has the same effect as passing amp_index < 0.
+ *
+ * If the EFI data is too short to be a valid entry, or the entry count
+ * in the EFI data overflows the actual length of the data, this function
+ * returns -EOVERFLOW.
+ *
+ * Return: 0 if the entry was found, -ENOENT if no entry was found,
+ *        -EOVERFLOW if the EFI file is corrupt, else other error value.
+ */
+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))
+               return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
+       else
+               return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, SND_SOC_CS_AMP_LIB);
+
+static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
+       .get_efi_variable = cs_amp_get_efi_variable,
+       .write_cal_coeff = cs_amp_write_cal_coeff,
+};
+
+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);
+EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SOC_CS_AMP_LIB);
+
+MODULE_DESCRIPTION("Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(FW_CS_DSP);
index ab960a1c171e629e4becb57c653fdc4b917b026b..14a5f86019aad47390ee8f3962a596296dfbf437 100644 (file)
@@ -161,6 +161,20 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = {
        .val_format_endian_default = REGMAP_ENDIAN_BIG,
 };
 
+static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56)
+{
+       int ret;
+
+       /* SoundWire UniqueId is used to index the calibration array */
+       ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0);
+       if (ret < 0)
+               return ret;
+
+       cs35l56->base.cal_index = ret & 0xf;
+
+       return 0;
+}
+
 static void cs35l56_sdw_init(struct sdw_slave *peripheral)
 {
        struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
@@ -168,6 +182,12 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
 
        pm_runtime_get_noresume(cs35l56->base.dev);
 
+       if (cs35l56->base.cal_index < 0) {
+               ret = cs35l56_sdw_set_cal_index(cs35l56);
+               if (ret < 0)
+                       goto out;
+       }
+
        regcache_cache_only(cs35l56->base.regmap, false);
 
        ret = cs35l56_init(cs35l56);
@@ -366,7 +386,7 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
        dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n",
                __func__, sclk, params->col, params->row);
 
-       if (cs35l56->base.rev < 0xb0)
+       if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0))
                return cs35l56_a1_kick_divider(cs35l56, peripheral);
 
        return 0;
@@ -543,6 +563,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = {
 
 static const struct sdw_device_id cs35l56_sdw_id[] = {
        SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
+       SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0),
        {},
 };
 MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
index cb4e83126b085228fa196aeb7e635537c39cfc24..08cac58e3ab2223c82b4da10314f04b2f055b5b6 100644 (file)
@@ -5,10 +5,12 @@
 // Copyright (C) 2023 Cirrus Logic, Inc. and
 //                    Cirrus Logic International Semiconductor Ltd.
 
+#include <linux/firmware/cirrus/wmfw.h>
 #include <linux/gpio/consumer.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/types.h>
+#include <sound/cs-amp-lib.h>
 
 #include "cs35l56.h"
 
@@ -36,6 +38,8 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
 EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED);
 
 static const struct reg_default cs35l56_reg_defaults[] = {
+       /* no defaults for OTP_MEM - first read populates cache */
+
        { CS35L56_ASP1_ENABLES1,                0x00000000 },
        { CS35L56_ASP1_CONTROL1,                0x00000028 },
        { CS35L56_ASP1_CONTROL2,                0x18180200 },
@@ -91,6 +95,9 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
        case CS35L56_BLOCK_ENABLES2:
        case CS35L56_REFCLK_INPUT:
        case CS35L56_GLOBAL_SAMPLE_RATE:
+       case CS35L56_OTP_MEM_53:
+       case CS35L56_OTP_MEM_54:
+       case CS35L56_OTP_MEM_55:
        case CS35L56_ASP1_ENABLES1:
        case CS35L56_ASP1_CONTROL1:
        case CS35L56_ASP1_CONTROL2:
@@ -629,6 +636,82 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds
 }
 EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED);
 
+struct cs35l56_pte {
+       u8 x;
+       u8 wafer_id;
+       u8 pte[2];
+       u8 lot[3];
+       u8 y;
+       u8 unused[3];
+       u8 dvs;
+} __packed;
+static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0);
+
+static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+       struct cs35l56_pte pte;
+       u64 unique_id;
+       int ret;
+
+       ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte));
+       if (ret) {
+               dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret);
+               return ret;
+       }
+
+       unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16);
+       unique_id <<= 32;
+       unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
+                    ((u32)pte.dvs << 24);
+
+       dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id);
+
+       *uid = unique_id;
+
+       return 0;
+}
+
+/* Firmware calibration controls */
+const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
+       .alg_id =       0x9f210,
+       .mem_region =   WMFW_ADSP2_YM,
+       .ambient =      "CAL_AMBIENT",
+       .calr =         "CAL_R",
+       .status =       "CAL_STATUS",
+       .checksum =     "CAL_CHECKSUM",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
+{
+       u64 silicon_uid;
+       int ret;
+
+       /* Driver can't apply calibration to a secured part, so skip */
+       if (cs35l56_base->secured)
+               return 0;
+
+       ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
+       if (ret < 0)
+               return ret;
+
+       ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid,
+                                             cs35l56_base->cal_index,
+                                             &cs35l56_base->cal_data);
+
+       /* Only return an error status if probe should be aborted */
+       if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+               return 0;
+
+       if (ret < 0)
+               return ret;
+
+       cs35l56_base->cal_data_valid = true;
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, SND_SOC_CS35L56_SHARED);
+
 int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
                             bool *fw_missing, unsigned int *fw_version)
 {
@@ -693,13 +776,17 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
        devid &= CS35L56_DEVID_MASK;
 
        switch (devid) {
+       case 0x35A54:
        case 0x35A56:
+       case 0x35A57:
                break;
        default:
                dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
                return ret;
        }
 
+       cs35l56_base->type = devid & 0xFF;
+
        ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
        if (ret) {
                dev_err(cs35l56_base->dev, "Get Secure status failed\n");
@@ -720,8 +807,8 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
        if (ret)
                return ret;
 
-       dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
-                cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
+       dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
+                cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
                 fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
 
        /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
@@ -923,3 +1010,4 @@ MODULE_DESCRIPTION("ASoC CS35L56 Shared");
 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
 MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
index 6dd0319bc843cf5d1740222e57c380881b4b5b05..8d2f021fb362812a73d07d10565a23d2b77f6a38 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/soundwire/sdw.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
+#include <sound/cs-amp-lib.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
@@ -802,16 +803,44 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
        }
 };
 
+static int cs35l56_write_cal(struct cs35l56_private *cs35l56)
+{
+       int ret;
+
+       if (cs35l56->base.secured || !cs35l56->base.cal_data_valid)
+               return -ENODATA;
+
+       ret = wm_adsp_run(&cs35l56->dsp);
+       if (ret)
+               return ret;
+
+       ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
+                                     &cs35l56_calibration_controls,
+                                     &cs35l56->base.cal_data);
+
+       wm_adsp_stop(&cs35l56->dsp);
+
+       if (ret == 0)
+               dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+       return ret;
+}
+
 static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
 {
        int ret;
 
        /* Use wm_adsp to load and apply the firmware patch and coefficient files */
        ret = wm_adsp_power_up(&cs35l56->dsp, true);
-       if (ret)
+       if (ret) {
                dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
-       else
-               cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+               return;
+       }
+
+       cs35l56_write_cal(cs35l56);
+
+       /* Always REINIT after applying patch or coefficients */
+       cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
 }
 
 static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
@@ -874,6 +903,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
                          CS35L56_FIRMWARE_MISSING);
        cs35l56->base.fw_patched = true;
 
+       if (cs35l56_write_cal(cs35l56) == 0)
+               cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+
 err_unlock:
        mutex_unlock(&cs35l56->base.irq_lock);
 err:
@@ -972,6 +1004,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
                return -ENODEV;
        }
 
+       cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
+       if (!cs35l56->dsp.part)
+               return -ENOMEM;
+
        cs35l56->component = component;
        wm_adsp2_component_probe(&cs35l56->dsp, component);
 
@@ -1002,6 +1038,9 @@ static void cs35l56_component_remove(struct snd_soc_component *component)
 
        wm_adsp2_component_remove(&cs35l56->dsp, component);
 
+       kfree(cs35l56->dsp.part);
+       cs35l56->dsp.part = NULL;
+
        kfree(cs35l56->dsp.fwf_name);
        cs35l56->dsp.fwf_name = NULL;
 
@@ -1221,7 +1260,12 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
 
        dsp = &cs35l56->dsp;
        cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp);
-       dsp->part = "cs35l56";
+
+       /*
+        * dsp->part is filled in later as it is based on the DEVID. In a
+        * SoundWire system that cannot be read until enumeration has occurred
+        * and the device has attached.
+        */
        dsp->fw = 12;
        dsp->wmfw_optional = true;
 
@@ -1356,6 +1400,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
 
        init_completion(&cs35l56->init_completion);
        mutex_init(&cs35l56->base.irq_lock);
+       cs35l56->base.cal_index = -1;
        cs35l56->speaker_id = -ENOENT;
 
        dev_set_drvdata(cs35l56->base.dev, cs35l56);
@@ -1457,6 +1502,10 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
        if (ret)
                return ret;
 
+       ret = cs35l56_get_calibration(&cs35l56->base);
+       if (ret)
+               return ret;
+
        if (!cs35l56->base.reset_gpio) {
                dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
                cs35l56->soft_resetting = true;
@@ -1541,6 +1590,7 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = {
 
 MODULE_DESCRIPTION("ASoC CS35L56 driver");
 MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
 MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
 MODULE_LICENSE("GPL");
index 2d11c5125f732bac0c91e91c91d49c54613a8b5c..60d366e53526ff20a36eaea6322a8d8baf194446 100644 (file)
@@ -2195,7 +2195,6 @@ int cs42l42_suspend(struct device *dev)
        /* Discharge FILT+ */
        regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
                           CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK);
-       msleep(CS42L42_FILT_DISCHARGE_TIME_MS);
 
        regcache_cache_only(cs42l42->regmap, true);
        gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
index 24a598f2ed9a309d736df327bff80ff93b2d3450..901b9dbcf58543e9b923ff4b3f3992743c5af963 100644 (file)
@@ -6,29 +6,35 @@
 //                         Cirrus Logic International Semiconductor Ltd.
 
 #include <linux/build_bug.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/irq.h>
 #include <linux/jiffies.h>
 #include <linux/mfd/cs42l43.h>
 #include <linux/mfd/cs42l43-regs.h>
+#include <linux/mutex.h>
 #include <linux/pm_runtime.h>
 #include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
 #include <sound/control.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc-component.h>
+#include <sound/soc-jack.h>
 #include <sound/soc.h>
 
 #include "cs42l43.h"
 
 static const unsigned int cs42l43_accdet_us[] = {
-       20, 100, 1000, 10000, 50000, 75000, 100000, 200000
+       20, 100, 1000, 10000, 50000, 75000, 100000, 200000,
 };
 
 static const unsigned int cs42l43_accdet_db_ms[] = {
-       0, 125, 250, 500, 750, 1000, 1250, 1500
+       0, 125, 250, 500, 750, 1000, 1250, 1500,
 };
 
 static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 };
@@ -101,8 +107,13 @@ int cs42l43_set_jack(struct snd_soc_component *component,
                        goto error;
                }
 
-               device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
-                                              priv->buttons, ret);
+               ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
+                                                    priv->buttons, ret);
+               if (ret < 0) {
+                       dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n",
+                               ret);
+                       goto error;
+               }
        } else {
                priv->buttons[0] = 70;
                priv->buttons[1] = 185;
@@ -637,7 +648,7 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic)
 static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
 {
        struct cs42l43 *cs42l43 = priv->core;
-       int timeout_ms = ((2 * priv->detect_us) / 1000) + 200;
+       int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200;
        unsigned int type = 0xff;
        unsigned long time_left;
 
@@ -846,6 +857,9 @@ static const char * const cs42l43_jack_text[] = {
        "Line-In", "Microphone", "Optical",
 };
 
+static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) ==
+             ARRAY_SIZE(cs42l43_jack_text) - 1);
+
 SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text);
 
 int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -868,9 +882,6 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int override = ucontrol->value.integer.value[0];
 
-       BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) !=
-                    ARRAY_SIZE(cs42l43_jack_text) - 1);
-
        if (override >= e->items)
                return -EINVAL;
 
index 388f95853b6998c163905e58fa01a79896a971ca..60c00c05da055c8d9c258dd41b868c6127d7e421 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/mfd/cs42l43.h>
 #include <linux/mfd/cs42l43-regs.h>
 #include <linux/module.h>
+#include <linux/soundwire/sdw.h>
 #include <sound/pcm.h>
 #include <sound/sdw.h>
 #include <sound/soc-component.h>
index a97ccb512deba86305ff433fa5b639fdfbb8dcbe..860d5cda67bffe83c5c4934497cdf8c1c510922b 100644 (file)
@@ -6,17 +6,27 @@
 //                         Cirrus Logic International Semiconductor Ltd.
 
 #include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/find.h>
 #include <linux/gcd.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/jiffies.h>
 #include <linux/mfd/cs42l43.h>
 #include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/string.h>
+#include <linux/workqueue.h>
 #include <sound/control.h>
+#include <sound/cs42l43.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc-component.h>
@@ -539,23 +549,22 @@ static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
-static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots)
+static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask,
+                                 int *slots, unsigned int nslots)
 {
-       int i;
+       int i = 0;
+       int slot;
 
-       for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) {
-               int slot = ffs(mask) - 1;
-
-               if (slot < 0)
+       for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) {
+               if (i == nslots) {
+                       dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n",
+                                mask);
                        return;
+               }
 
-               slots[i] = slot;
-
-               mask &= ~(1 << slot);
+               slots[i++] = slot;
        }
 
-       if (mask)
-               dev_warn(priv->dev, "Too many channels in TDM mask\n");
 }
 
 static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -572,8 +581,10 @@ static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mas
                rx_mask = CS42L43_DEFAULT_SLOTS;
        }
 
-       cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots);
-       cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots);
+       cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots,
+                             ARRAY_SIZE(priv->tx_slots));
+       cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots,
+                             ARRAY_SIZE(priv->rx_slots));
 
        return 0;
 }
@@ -1051,12 +1062,10 @@ static int cs42l43_decim_get(struct snd_kcontrol *kcontrol,
        int ret;
 
        ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT);
-       if (ret < 0)
-               return ret;
+       if (ret > 0)
+               ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
        else if (!ret)
                ucontrol->value.integer.value[0] = ret;
-       else
-               ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
 
        return ret;
 }
@@ -1069,12 +1078,10 @@ static int cs42l43_spk_get(struct snd_kcontrol *kcontrol,
        int ret;
 
        ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT);
-       if (ret < 0)
-               return ret;
+       if (ret > 0)
+               ret = snd_soc_get_volsw(kcontrol, ucontrol);
        else if (!ret)
                ucontrol->value.integer.value[0] = ret;
-       else
-               ret = snd_soc_get_volsw(kcontrol, ucontrol);
 
        return ret;
 }
@@ -1331,10 +1338,9 @@ static int cs42l43_enable_pll(struct cs42l43_codec *priv)
 
        dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq);
 
-       while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) {
-               div++;
-               freq /= 2;
-       }
+       div = fls(freq) -
+             fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq);
+       freq >>= div;
 
        if (div <= CS42L43_PLL_REFCLK_DIV_MASK) {
                int i;
@@ -2094,8 +2100,10 @@ static int cs42l43_component_probe(struct snd_soc_component *component)
 
        snd_soc_component_init_regmap(component, cs42l43->regmap);
 
-       cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots);
-       cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots);
+       cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots,
+                             ARRAY_SIZE(priv->tx_slots));
+       cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots,
+                             ARRAY_SIZE(priv->rx_slots));
 
        priv->component = component;
        priv->constraint = cs42l43_constraint;
@@ -2103,10 +2111,28 @@ static int cs42l43_component_probe(struct snd_soc_component *component)
        return 0;
 }
 
+static void cs42l43_component_remove(struct snd_soc_component *component)
+{
+       struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+       cs42l43_set_jack(priv->component, NULL, NULL);
+
+       cancel_delayed_work_sync(&priv->bias_sense_timeout);
+       cancel_delayed_work_sync(&priv->tip_sense_work);
+       cancel_delayed_work_sync(&priv->button_press_work);
+       cancel_work_sync(&priv->button_release_work);
+
+       cancel_work_sync(&priv->hp_ilimit_work);
+       cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+       priv->component = NULL;
+}
+
 static const struct snd_soc_component_driver cs42l43_component_drv = {
        .name                   = "cs42l43-codec",
 
        .probe                  = cs42l43_component_probe,
+       .remove                 = cs42l43_component_remove,
        .set_sysclk             = cs42l43_set_sysclk,
        .set_jack               = cs42l43_set_jack,
 
@@ -2387,7 +2413,7 @@ MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table);
 static struct platform_driver cs42l43_codec_driver = {
        .driver = {
                .name   = "cs42l43-codec",
-               .pm     = &cs42l43_codec_pm_ops,
+               .pm     = pm_ptr(&cs42l43_codec_pm_ops),
        },
 
        .probe          = cs42l43_codec_probe,
index 125e36861d5d5b891664428152b935b764c3d61d..9924c13e1eb539a24efdcb2d5ed27c3f11cc9ed2 100644 (file)
@@ -6,19 +6,14 @@
  *                         Cirrus Logic International Semiconductor Ltd.
  */
 
-#include <linux/clk.h>
+#ifndef CS42L43_ASOC_INT_H
+#define CS42L43_ASOC_INT_H
+
 #include <linux/completion.h>
-#include <linux/device.h>
 #include <linux/mutex.h>
-#include <linux/regmap.h>
-#include <linux/soundwire/sdw.h>
 #include <linux/types.h>
-#include <sound/cs42l43.h>
+#include <linux/workqueue.h>
 #include <sound/pcm.h>
-#include <sound/soc-jack.h>
-
-#ifndef CS42L43_ASOC_INT_H
-#define CS42L43_ASOC_INT_H
 
 #define CS42L43_INTERNAL_SYSCLK                24576000
 #define CS42L43_DEFAULT_SLOTS          0x3F
 
 #define CS42L43_N_BUTTONS      6
 
+struct clk;
+struct device;
+
+struct snd_soc_component;
+struct snd_soc_jack;
+
+struct cs42l43;
+
 struct cs42l43_codec {
        struct device *dev;
        struct cs42l43 *core;
index 0e5c527687a27da497d924676e910d78232f36b6..369c620787800749c7599339ac4385c2d60a291a 100644 (file)
@@ -2101,18 +2101,14 @@ static int da7213_probe(struct snd_soc_component *component)
        pm_runtime_put_sync(component->dev);
 
        /* Check if MCLK provided */
-       da7213->mclk = devm_clk_get(component->dev, "mclk");
-       if (IS_ERR(da7213->mclk)) {
-               if (PTR_ERR(da7213->mclk) != -ENOENT)
-                       return PTR_ERR(da7213->mclk);
-               else
-                       da7213->mclk = NULL;
-       } else {
+       da7213->mclk = devm_clk_get_optional(component->dev, "mclk");
+       if (IS_ERR(da7213->mclk))
+               return PTR_ERR(da7213->mclk);
+       if (da7213->mclk)
                /* Do automatic PLL handling assuming fixed clock until
                 * set_pll() has been called. This makes the codec usable
                 * with the simple-audio-card driver. */
                da7213->fixed_clk_auto_pll = true;
-       }
 
        /* Default infinite tone gen, start/stop by Kcontrol */
        snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK);
index cbcd02ec6ba42edd9d22020294332cfec40945cf..15289dadafea091d2693149e600d72e0cbb975c0 100644 (file)
@@ -31,11 +31,11 @@ struct es8326_priv {
         * while enabling or disabling or during an irq.
         */
        struct mutex lock;
-       u8 mic1_src;
-       u8 mic2_src;
        u8 jack_pol;
        u8 interrupt_src;
        u8 interrupt_clk;
+       u8 hpl_vol;
+       u8 hpr_vol;
        bool jd_inverted;
        unsigned int sysclk;
 
@@ -121,6 +121,72 @@ static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static int es8326_hplvol_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = es8326->hpl_vol;
+
+       return 0;
+}
+
+static int es8326_hplvol_set(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+       unsigned int hp_vol;
+
+       hp_vol = ucontrol->value.integer.value[0];
+       if (hp_vol > 5)
+               return -EINVAL;
+       if (es8326->hpl_vol != hp_vol) {
+               es8326->hpl_vol = hp_vol;
+               if (hp_vol >= 3)
+                       hp_vol++;
+               regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+                               0x70, (hp_vol << 4));
+               return 1;
+       }
+
+       return 0;
+}
+
+static int es8326_hprvol_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = es8326->hpr_vol;
+
+       return 0;
+}
+
+static int es8326_hprvol_set(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+       unsigned int hp_vol;
+
+       hp_vol = ucontrol->value.integer.value[0];
+       if (hp_vol > 5)
+               return -EINVAL;
+       if (es8326->hpr_vol != hp_vol) {
+               es8326->hpr_vol = hp_vol;
+               if (hp_vol >= 3)
+                       hp_vol++;
+               regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+                               0x07, hp_vol);
+               return 1;
+       }
+
+       return 0;
+}
+
 static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
 static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
 static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
@@ -151,15 +217,24 @@ static const char *const winsize[] = {
 static const char *const dacpol_txt[] =        {
        "Normal", "R Invert", "L Invert", "L + R Invert" };
 
+static const char *const hp_spkvol_switch[] = {
+       "HPVOL: HPL+HPL, SPKVOL: HPL+HPL",
+       "HPVOL: HPL+HPR, SPKVOL: HPL+HPR",
+       "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR",
+       "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR",
+};
+
 static const struct soc_enum dacpol =
        SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
 static const struct soc_enum alc_winsize =
        SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
 static const struct soc_enum drc_winsize =
        SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize);
+static const struct soc_enum hpvol_spkvol_switch =
+       SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch);
 
 static const struct snd_kcontrol_new es8326_snd_controls[] = {
-       SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv),
+       SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
        SOC_ENUM("Playback Polarity", dacpol),
        SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
        SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv),
@@ -182,6 +257,17 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
                        es8326_crosstalk1_get, es8326_crosstalk1_set),
        SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
                        es8326_crosstalk2_get, es8326_crosstalk2_set),
+       SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0,
+                       es8326_hplvol_get, es8326_hplvol_set),
+       SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0,
+                       es8326_hprvol_get, es8326_hprvol_set),
+
+       SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+       SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+       SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+       SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+
+       SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch),
 };
 
 static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
@@ -972,6 +1058,8 @@ static int es8326_resume(struct snd_soc_component *component)
 
        es8326->jack_remove_retry = 0;
        es8326->hp = 0;
+       es8326->hpl_vol = 0x03;
+       es8326->hpr_vol = 0x03;
        return 0;
 }
 
@@ -1002,20 +1090,6 @@ static int es8326_probe(struct snd_soc_component *component)
        es8326->jd_inverted = device_property_read_bool(component->dev,
                                                        "everest,jack-detect-inverted");
 
-       ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src);
-       if (ret != 0) {
-               dev_dbg(component->dev, "mic1-src return %d", ret);
-               es8326->mic1_src = ES8326_ADC_AMIC;
-       }
-       dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src);
-
-       ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src);
-       if (ret != 0) {
-               dev_dbg(component->dev, "mic2-src return %d", ret);
-               es8326->mic2_src = ES8326_ADC_DMIC;
-       }
-       dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src);
-
        ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol);
        if (ret != 0) {
                dev_dbg(component->dev, "jack-pol return %d", ret);
@@ -1035,7 +1109,7 @@ static int es8326_probe(struct snd_soc_component *component)
                                      &es8326->interrupt_clk);
        if (ret != 0) {
                dev_dbg(component->dev, "interrupt-clk return %d", ret);
-               es8326->interrupt_clk = 0x45;
+               es8326->interrupt_clk = 0x00;
        }
        dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
 
index 4234bbb900c4530c6746fab5f75fcb0b049e4fdb..ee12caef810532380cdf1b5d6b0b204afef78e63 100644 (file)
@@ -69,7 +69,7 @@
 #define ES8326_DAC_DSM         0x4D
 #define ES8326_DAC_RAMPRATE    0x4E
 #define ES8326_DAC_VPPSCALE    0x4F
-#define ES8326_DAC_VOL         0x50
+#define ES8326_DACL_VOL                0x50
 #define ES8326_DRC_RECOVERY    0x53
 #define ES8326_DRC_WINSIZE     0x54
 #define ES8326_DAC_CROSSTALK   0x55
@@ -81,6 +81,9 @@
 #define ES8326_SDINOUT23_IO    0x5B
 #define ES8326_JACK_PULSE      0x5C
 
+#define ES8326_DACR_VOL                0xF4
+#define ES8326_SPKL_VOL                0xF5
+#define ES8326_SPKR_VOL                0xF6
 #define ES8326_HP_MISC         0xF7
 #define ES8326_CTIA_OMTP_STA   0xF8
 #define ES8326_PULLUP_CTL      0xF9
diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c
new file mode 100644 (file)
index 0000000..e5fcde9
--- /dev/null
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Framer ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/framer/framer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define FRAMER_NB_CHANNEL      32
+#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT)
+
+struct framer_codec {
+       struct framer *framer;
+       struct device *dev;
+       struct snd_soc_jack jack;
+       struct notifier_block nb;
+       struct work_struct carrier_work;
+       int max_chan_playback;
+       int max_chan_capture;
+};
+
+static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                                  unsigned int rx_mask, int slots, int width)
+{
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+       switch (width) {
+       case 0:
+               /* Not set -> default 8 */
+       case 8:
+               break;
+       default:
+               dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+               return -EINVAL;
+       }
+
+       framer->max_chan_playback = hweight32(tx_mask);
+       if (framer->max_chan_playback > FRAMER_NB_CHANNEL) {
+               dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n",
+                       tx_mask, FRAMER_NB_CHANNEL);
+               return -EINVAL;
+       }
+
+       framer->max_chan_capture = hweight32(rx_mask);
+       if (framer->max_chan_capture > FRAMER_NB_CHANNEL) {
+               dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n",
+                       rx_mask, FRAMER_NB_CHANNEL);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai,
+                                                struct snd_pcm_hw_params *params,
+                                                unsigned int nb_ts)
+{
+       struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       snd_pcm_format_t format = params_format(params);
+       struct snd_interval ch = {0};
+       int width;
+
+       width = snd_pcm_format_physical_width(format);
+       if (width == 8 || width == 16 || width == 32 || width == 64) {
+               ch.max = nb_ts * 8 / width;
+       } else {
+               dev_err(dai->dev, "format physical width %d not supported\n", width);
+               return -EINVAL;
+       }
+
+       ch.min = ch.max ? 1 : 0;
+
+       return snd_interval_refine(c, &ch);
+}
+
+static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+                                                         struct snd_pcm_hw_rule *rule)
+{
+       struct snd_soc_dai *dai = rule->private;
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+       return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+                                                        struct snd_pcm_hw_rule *rule)
+{
+       struct snd_soc_dai *dai = rule->private;
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+       return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture);
+}
+
+static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai,
+                                                struct snd_pcm_hw_params *params,
+                                                unsigned int nb_ts)
+{
+       struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       unsigned int channels = params_channels(params);
+       unsigned int slot_width;
+       snd_pcm_format_t format;
+       struct snd_mask f_new;
+
+       if (!channels || channels > nb_ts) {
+               dev_err(dai->dev, "channels %u not supported\n", nb_ts);
+               return -EINVAL;
+       }
+
+       slot_width = (nb_ts / channels) * 8;
+
+       snd_mask_none(&f_new);
+       pcm_for_each_format(format) {
+               if (snd_mask_test_format(f_old, format)) {
+                       if (snd_pcm_format_physical_width(format) <= slot_width)
+                               snd_mask_set_format(&f_new, format);
+               }
+       }
+
+       return snd_mask_refine(f_old, &f_new);
+}
+
+static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+                                                         struct snd_pcm_hw_rule *rule)
+{
+       struct snd_soc_dai *dai = rule->private;
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+       return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+                                                        struct snd_pcm_hw_rule *rule)
+{
+       struct snd_soc_dai *dai = rule->private;
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+       return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture);
+}
+
+static u64 framer_formats(u8 nb_ts)
+{
+       unsigned int format_width;
+       unsigned int chan_width;
+       snd_pcm_format_t format;
+       u64 formats_mask;
+
+       if (!nb_ts)
+               return 0;
+
+       formats_mask = 0;
+       chan_width = nb_ts * 8;
+       pcm_for_each_format(format) {
+               /* Support physical width multiple of 8bit */
+               format_width = snd_pcm_format_physical_width(format);
+               if (format_width == 0 || format_width % 8)
+                       continue;
+
+               /*
+                * And support physical width that can fit N times in the
+                * channel
+                */
+               if (format_width > chan_width || chan_width % format_width)
+                       continue;
+
+               formats_mask |= pcm_format_to_bits(format);
+       }
+       return formats_mask;
+}
+
+static int framer_dai_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+       snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+       snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+       unsigned int frame_bits;
+       u64 format;
+       int ret;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               format = framer_formats(framer->max_chan_capture);
+               hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format;
+               hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels;
+               frame_bits = framer->max_chan_capture * 8;
+       } else {
+               format = framer_formats(framer->max_chan_playback);
+               hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format;
+               hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels;
+               frame_bits = framer->max_chan_playback * 8;
+       }
+
+       ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_FORMAT, format);
+       if (ret) {
+               dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret);
+               return ret;
+       }
+
+       ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels_by_format, dai,
+                                 SNDRV_PCM_HW_PARAM_FORMAT, -1);
+       if (ret) {
+               dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+               return ret;
+       }
+
+       ret = snd_pcm_hw_rule_add(substream->runtime, 0,  SNDRV_PCM_HW_PARAM_FORMAT,
+                                 hw_rule_format_by_channels, dai,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (ret) {
+               dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+               return ret;
+       }
+
+       ret = snd_pcm_hw_constraint_single(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                                          frame_bits);
+       if (ret < 0) {
+               dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static u64 framer_dai_formats[] = {
+       SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops framer_dai_ops = {
+       .startup        = framer_dai_startup,
+       .set_tdm_slot   = framer_dai_set_tdm_slot,
+       .auto_selectable_formats     = framer_dai_formats,
+       .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
+};
+
+static struct snd_soc_dai_driver framer_dai_driver = {
+       .name = "framer",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = FRAMER_NB_CHANNEL,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = U64_MAX, /* Will be refined on DAI .startup() */
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = FRAMER_NB_CHANNEL,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = U64_MAX, /* Will be refined on DAI .startup() */
+       },
+       .ops = &framer_dai_ops,
+};
+
+static void framer_carrier_work(struct work_struct *work)
+{
+       struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work);
+       struct framer_status framer_status;
+       int jack_status;
+       int ret;
+
+       ret = framer_get_status(framer->framer, &framer_status);
+       if (ret) {
+               dev_err(framer->dev, "get framer status failed (%d)\n", ret);
+               return;
+       }
+
+       jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0;
+       snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK);
+}
+
+static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action,
+                                  void *data)
+{
+       struct framer_codec *framer = container_of(nb, struct framer_codec, nb);
+
+       switch (action) {
+       case FRAMER_EVENT_STATUS:
+               queue_work(system_power_efficient_wq, &framer->carrier_work);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int framer_component_probe(struct snd_soc_component *component)
+{
+       struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+       struct framer_status status;
+       char *name;
+       int ret;
+
+       INIT_WORK(&framer->carrier_work, framer_carrier_work);
+
+       name = "carrier";
+       if (component->name_prefix) {
+               name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix);
+               if (!name)
+                       return -ENOMEM;
+       }
+
+       ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack);
+       if (component->name_prefix)
+               kfree(name); /* A copy is done by snd_soc_card_jack_new */
+       if (ret) {
+               dev_err(component->dev, "Cannot create jack\n");
+               return ret;
+       }
+
+       ret = framer_init(framer->framer);
+       if (ret) {
+               dev_err(component->dev, "framer init failed (%d)\n", ret);
+               return ret;
+       }
+
+       ret = framer_power_on(framer->framer);
+       if (ret) {
+               dev_err(component->dev, "framer power-on failed (%d)\n", ret);
+               goto framer_exit;
+       }
+
+       /* Be sure that get_status is supported */
+       ret = framer_get_status(framer->framer, &status);
+       if (ret) {
+               dev_err(component->dev, "get framer status failed (%d)\n", ret);
+               goto framer_power_off;
+       }
+
+       framer->nb.notifier_call = framer_carrier_notifier;
+       ret = framer_notifier_register(framer->framer, &framer->nb);
+       if (ret) {
+               dev_err(component->dev, "Cannot register event notifier\n");
+               goto framer_power_off;
+       }
+
+       /* Queue work to set the initial value */
+       queue_work(system_power_efficient_wq, &framer->carrier_work);
+
+       return 0;
+
+framer_power_off:
+       framer_power_off(framer->framer);
+framer_exit:
+       framer_exit(framer->framer);
+       return ret;
+}
+
+static void framer_component_remove(struct snd_soc_component *component)
+{
+       struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+
+       framer_notifier_unregister(framer->framer, &framer->nb);
+       cancel_work_sync(&framer->carrier_work);
+       framer_power_off(framer->framer);
+       framer_exit(framer->framer);
+}
+
+static const struct snd_soc_component_driver framer_component_driver = {
+       .probe          = framer_component_probe,
+       .remove         = framer_component_remove,
+       .endianness     = 1,
+};
+
+static int framer_codec_probe(struct platform_device *pdev)
+{
+       struct framer_codec *framer;
+
+       framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL);
+       if (!framer)
+               return -ENOMEM;
+
+       framer->dev = &pdev->dev;
+
+       /* Get framer from parents node */
+       framer->framer = devm_framer_get(&pdev->dev, NULL);
+       if (IS_ERR(framer->framer))
+               return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n");
+
+       platform_set_drvdata(pdev, framer);
+
+       return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver,
+                                              &framer_dai_driver, 1);
+}
+
+static struct platform_driver framer_codec_driver = {
+       .driver = {
+               .name = "framer-codec",
+       },
+       .probe = framer_codec_probe,
+};
+module_platform_driver(framer_codec_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("FRAMER ALSA SoC driver");
+MODULE_LICENSE("GPL");
index d2117e36ddd107704e7685d783e777eaf93b6b4b..5a58723dc0e96b8309b60a8b71ef0f30b2c7ea46 100644 (file)
@@ -198,19 +198,19 @@ static int hda_codec_probe(struct snd_soc_component *component)
        ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec,
                                       false);
        if (ret < 0) {
-               dev_err(&hdev->dev, "create hda codec failed: %d\n", ret);
+               dev_err(&hdev->dev, "codec create failed: %d\n", ret);
                goto device_new_err;
        }
 
        ret = snd_hda_codec_set_name(codec, codec->preset->name);
        if (ret < 0) {
-               dev_err(&hdev->dev, "name failed %s\n", codec->preset->name);
+               dev_err(&hdev->dev, "set name: %s failed: %d\n", codec->preset->name, ret);
                goto err;
        }
 
        ret = snd_hdac_regmap_init(&codec->core);
        if (ret < 0) {
-               dev_err(&hdev->dev, "regmap init failed\n");
+               dev_err(&hdev->dev, "regmap init failed: %d\n", ret);
                goto err;
        }
 
@@ -223,13 +223,13 @@ static int hda_codec_probe(struct snd_soc_component *component)
 
        ret = patch(codec);
        if (ret < 0) {
-               dev_err(&hdev->dev, "patch failed %d\n", ret);
+               dev_err(&hdev->dev, "codec init failed: %d\n", ret);
                goto err;
        }
 
        ret = snd_hda_codec_parse_pcms(codec);
        if (ret < 0) {
-               dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
+               dev_err(&hdev->dev, "unable to map pcms to dai: %d\n", ret);
                goto parse_pcms_err;
        }
 
@@ -350,6 +350,11 @@ static int hda_hdev_attach(struct hdac_device *hdev)
        struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
        struct snd_soc_component_driver *comp_drv;
 
+       if (hda_codec_is_display(codec) && !hdev->bus->audio_component) {
+               dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id);
+               return -ENODEV;
+       }
+
        comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL);
        if (!comp_drv)
                return -ENOMEM;
index d3684c7ab9305d4f5aefbfd8bc599953abf84964..d98718b3dc4b07e14f0494dabb5ecae7ffde090e 100644 (file)
 /* The soundwire block should be internally reset at probe */
 #define LPASS_MACRO_FLAG_RESET_SWR             BIT(1)
 
+enum lpass_version {
+       LPASS_VER_9_0_0,
+       LPASS_VER_9_2_0,
+       LPASS_VER_10_0_0,
+       LPASS_VER_11_0_0,
+};
+
 struct lpass_macro {
        struct device *macro_pd;
        struct device *dcodec_pd;
index 124c2e144f337357af954e1ee4bc9d7b1e3c4b37..c98b0b747a92513636a93dc09db6b079b6ccb74c 100644 (file)
@@ -38,6 +38,8 @@
 #define CDC_TX_TOP_CSR_I2S_RESET       (0x00AC)
 #define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n)        (0x00C0 + n * 0x4)
 #define CDC_TX_TOP_CSR_SWR_DMIC0_CTL   (0x00C0)
+/* Default divider for AMIC and DMIC clock: DIV2 */
+#define CDC_TX_SWR_MIC_CLK_DEFAULT     0
 #define CDC_TX_SWR_DMIC_CLK_SEL_MASK   GENMASK(3, 1)
 #define CDC_TX_TOP_CSR_SWR_DMIC1_CTL   (0x00C4)
 #define CDC_TX_TOP_CSR_SWR_DMIC2_CTL   (0x00C8)
@@ -253,8 +255,18 @@ struct hpf_work {
        struct delayed_work dwork;
 };
 
+struct tx_macro_data {
+       unsigned int flags;
+       unsigned int ver;
+       const struct snd_soc_dapm_widget *extra_widgets;
+       size_t extra_widgets_num;
+       const struct snd_soc_dapm_route *extra_routes;
+       size_t extra_routes_num;
+};
+
 struct tx_macro {
        struct device *dev;
+       const struct tx_macro_data *data;
        struct snd_soc_component *component;
        struct hpf_work tx_hpf_work[NUM_DECIMATORS];
        struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS];
@@ -270,7 +282,6 @@ struct tx_macro {
        struct clk_hw hw;
        bool dec_active[NUM_DECIMATORS];
        int tx_mclk_users;
-       u16 dmic_clk_div;
        bool bcs_enable;
        int dec_mode[NUM_DECIMATORS];
        struct lpass_macro *pds;
@@ -431,6 +442,8 @@ static bool tx_is_volatile_register(struct device *dev, unsigned int reg)
        case CDC_TX_TOP_CSR_SWR_DMIC1_CTL:
        case CDC_TX_TOP_CSR_SWR_DMIC2_CTL:
        case CDC_TX_TOP_CSR_SWR_DMIC3_CTL:
+       case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+       case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
                return true;
        }
        return false;
@@ -635,13 +648,18 @@ exit:
        return 0;
 }
 
-static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator)
+static bool is_amic_enabled(struct snd_soc_component *component,
+                           struct tx_macro *tx, u8 decimator)
 {
        u16 adc_mux_reg, adc_reg, adc_n;
 
        adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator);
 
        if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) {
+               if (tx->data->ver > LPASS_VER_9_0_0)
+                       return true;
+
+               /* else: LPASS <= v9.0.0 */
                adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator);
                adc_n = snd_soc_component_read_field(component, adc_reg,
                                             CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK);
@@ -670,7 +688,7 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
        dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator);
        hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator);
 
-       if (is_amic_enabled(component, hpf_work->decimator)) {
+       if (is_amic_enabled(component, tx, hpf_work->decimator)) {
                snd_soc_component_write_field(component,
                                dec_cfg_reg,
                                CDC_TXn_HPF_CUT_FREQ_MASK,
@@ -734,16 +752,61 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static void tx_macro_update_smic_sel_v9(struct snd_soc_component *component,
+                                       struct snd_soc_dapm_widget *widget,
+                                       struct tx_macro *tx, u16 mic_sel_reg,
+                                       unsigned int val)
+{
+       unsigned int dmic;
+       u16 dmic_clk_reg;
+
+       if (val < 5) {
+               snd_soc_component_write_field(component, mic_sel_reg,
+                                             CDC_TXn_ADC_DMIC_SEL_MASK, 0);
+       } else {
+               snd_soc_component_write_field(component, mic_sel_reg,
+                                             CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+               dmic = TX_ADC_TO_DMIC(val);
+               dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+               snd_soc_component_write_field(component, dmic_clk_reg,
+                                             CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+                                             CDC_TX_SWR_MIC_CLK_DEFAULT);
+       }
+}
+
+static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component,
+                                         struct snd_soc_dapm_widget *widget,
+                                         struct tx_macro *tx, u16 mic_sel_reg,
+                                         unsigned int val)
+{
+       unsigned int dmic;
+       u16 dmic_clk_reg;
+
+       if (widget->shift) {
+               /* MSM DMIC */
+               snd_soc_component_write_field(component, mic_sel_reg,
+                                             CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+
+               dmic = TX_ADC_TO_DMIC(val);
+               dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+               snd_soc_component_write_field(component, dmic_clk_reg,
+                                             CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+                                             CDC_TX_SWR_MIC_CLK_DEFAULT);
+       } else {
+               snd_soc_component_write_field(component, mic_sel_reg,
+                                             CDC_TXn_ADC_DMIC_SEL_MASK, 0);
+       }
+}
+
 static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
        struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, dmic;
-       u16 mic_sel_reg;
-       u16 dmic_clk_reg;
        struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+       unsigned int val;
+       u16 mic_sel_reg;
 
        val = ucontrol->value.enumerated.item[0];
        if (val >= e->items)
@@ -780,21 +843,15 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
        }
 
        if (val != 0) {
-               if (widget->shift) { /* MSM DMIC */
-                       snd_soc_component_write_field(component, mic_sel_reg,
-                                                     CDC_TXn_ADC_DMIC_SEL_MASK, 1);
-               } else if (val < 5) {
-                       snd_soc_component_write_field(component, mic_sel_reg,
-                                                     CDC_TXn_ADC_DMIC_SEL_MASK, 0);
-               } else {
+               if (widget->shift) /* MSM DMIC */
                        snd_soc_component_write_field(component, mic_sel_reg,
                                                      CDC_TXn_ADC_DMIC_SEL_MASK, 1);
-                       dmic = TX_ADC_TO_DMIC(val);
-                       dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
-                       snd_soc_component_write_field(component, dmic_clk_reg,
-                                               CDC_TX_SWR_DMIC_CLK_SEL_MASK,
-                                               tx->dmic_clk_div);
-               }
+               else if (tx->data->ver <= LPASS_VER_9_0_0)
+                       tx_macro_update_smic_sel_v9(component, widget, tx,
+                                                   mic_sel_reg, val);
+               else
+                       tx_macro_update_smic_sel_v9_2(component, widget, tx,
+                                                     mic_sel_reg, val);
        }
 
        return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
@@ -882,7 +939,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
 
                                snd_soc_component_write_field(component, dmic_clk_reg,
                                                        CDC_TX_SWR_DMIC_CLK_SEL_MASK,
-                                                       tx->dmic_clk_div);
+                                                       CDC_TX_SWR_MIC_CLK_DEFAULT);
                        }
                }
                snd_soc_component_write_field(component, dec_cfg_reg,
@@ -895,7 +952,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_POST_PMU:
                snd_soc_component_write_field(component, tx_vol_ctl_reg,
                                             CDC_TXn_CLK_EN_MASK, 0x1);
-               if (!is_amic_enabled(component, decimator)) {
+               if (!is_amic_enabled(component, tx, decimator)) {
                        snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00);
                        /* Minimum 1 clk cycle delay is required as per HW spec */
                        usleep_range(1000, 1010);
@@ -911,7 +968,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
                                                      CDC_TXn_HPF_CUT_FREQ_MASK,
                                                      CF_MIN_3DB_150HZ);
 
-               if (is_amic_enabled(component, decimator)) {
+               if (is_amic_enabled(component, tx, decimator)) {
                        hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS;
                        unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS;
                }
@@ -927,7 +984,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
                                              CDC_TXn_HPF_F_CHANGE_MASK |
                                              CDC_TXn_HPF_ZERO_GATE_MASK,
                                              0x02);
-                       if (!is_amic_enabled(component, decimator))
+                       if (!is_amic_enabled(component, tx, decimator))
                                snd_soc_component_update_bits(component, hpf_gate_reg,
                                                      CDC_TXn_HPF_F_CHANGE_MASK |
                                                      CDC_TXn_HPF_ZERO_GATE_MASK,
@@ -964,7 +1021,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
                                                component, dec_cfg_reg,
                                                CDC_TXn_HPF_CUT_FREQ_MASK,
                                                hpf_cut_off_freq);
-                               if (is_amic_enabled(component, decimator))
+                               if (is_amic_enabled(component, tx, decimator))
                                        snd_soc_component_update_bits(component,
                                              hpf_gate_reg,
                                              CDC_TXn_HPF_F_CHANGE_MASK |
@@ -1235,53 +1292,6 @@ static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_d
 static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum);
 static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum);
 
-static const char * const smic_mux_text[] = {
-       "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0",
-       "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4",
-       "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7"
-};
-
-static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
-                       0, smic_mux_text);
-
-static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
-                       0, smic_mux_text);
-
-static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum,
-                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
-
 static const char * const dmic_mux_text[] = {
        "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
        "DMIC4", "DMIC5", "DMIC6", "DMIC7"
@@ -1427,15 +1437,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = {
        SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0,
                tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)),
 
-       SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux),
-       SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux),
-
        SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux),
        SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux),
        SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux),
@@ -1445,18 +1446,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux),
        SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux),
 
-       SND_SOC_DAPM_INPUT("TX SWR_ADC0"),
-       SND_SOC_DAPM_INPUT("TX SWR_ADC1"),
-       SND_SOC_DAPM_INPUT("TX SWR_ADC2"),
-       SND_SOC_DAPM_INPUT("TX SWR_ADC3"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC0"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC1"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC2"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC3"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC4"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC5"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC6"),
-       SND_SOC_DAPM_INPUT("TX SWR_DMIC7"),
        SND_SOC_DAPM_INPUT("TX DMIC0"),
        SND_SOC_DAPM_INPUT("TX DMIC1"),
        SND_SOC_DAPM_INPUT("TX DMIC2"),
@@ -1578,6 +1567,150 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX DMIC MUX0", "DMIC6", "TX DMIC6"},
        {"TX DMIC MUX0", "DMIC7", "TX DMIC7"},
 
+       {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"},
+       {"TX DMIC MUX1", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX1", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX1", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX1", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX1", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX1", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX1", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX1", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"},
+       {"TX DMIC MUX2", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX2", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX2", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX2", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX2", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX2", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX2", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX2", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"},
+       {"TX DMIC MUX3", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX3", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX3", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX3", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX3", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX3", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX3", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX3", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"},
+       {"TX DMIC MUX4", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX4", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX4", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX4", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX4", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX4", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX4", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX4", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"},
+       {"TX DMIC MUX5", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX5", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX5", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX5", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX5", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX5", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX5", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX5", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"},
+       {"TX DMIC MUX6", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX6", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX6", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX6", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX6", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX6", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX6", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX6", "DMIC7", "TX DMIC7"},
+
+       {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"},
+       {"TX DMIC MUX7", "DMIC0", "TX DMIC0"},
+       {"TX DMIC MUX7", "DMIC1", "TX DMIC1"},
+       {"TX DMIC MUX7", "DMIC2", "TX DMIC2"},
+       {"TX DMIC MUX7", "DMIC3", "TX DMIC3"},
+       {"TX DMIC MUX7", "DMIC4", "TX DMIC4"},
+       {"TX DMIC MUX7", "DMIC5", "TX DMIC5"},
+       {"TX DMIC MUX7", "DMIC6", "TX DMIC6"},
+       {"TX DMIC MUX7", "DMIC7", "TX DMIC7"},
+};
+
+/* Controls and routes specific to LPASS <= v9.0.0 */
+static const char * const smic_mux_text_v9[] = {
+       "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0",
+       "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4",
+       "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+                       0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+                       0, smic_mux_text_v9);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = {
+       SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9),
+       SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9),
+
+       SND_SOC_DAPM_INPUT("TX SWR_ADC0"),
+       SND_SOC_DAPM_INPUT("TX SWR_ADC1"),
+       SND_SOC_DAPM_INPUT("TX SWR_ADC2"),
+       SND_SOC_DAPM_INPUT("TX SWR_ADC3"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC0"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC1"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC2"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC3"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC4"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC5"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC6"),
+       SND_SOC_DAPM_INPUT("TX SWR_DMIC7"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9[] = {
        {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
        {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"},
@@ -1593,16 +1726,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"},
-       {"TX DMIC MUX1", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX1", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX1", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX1", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX1", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX1", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX1", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX1", "DMIC7", "TX DMIC7"},
-
        {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
        {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"},
@@ -1618,16 +1741,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"},
-       {"TX DMIC MUX2", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX2", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX2", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX2", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX2", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX2", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX2", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX2", "DMIC7", "TX DMIC7"},
-
        {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
        {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"},
@@ -1643,16 +1756,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"},
-       {"TX DMIC MUX3", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX3", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX3", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX3", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX3", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX3", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX3", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX3", "DMIC7", "TX DMIC7"},
-
        {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
        {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"},
@@ -1668,16 +1771,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"},
-       {"TX DMIC MUX4", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX4", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX4", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX4", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX4", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX4", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX4", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX4", "DMIC7", "TX DMIC7"},
-
        {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
        {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"},
@@ -1693,16 +1786,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"},
-       {"TX DMIC MUX5", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX5", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX5", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX5", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX5", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX5", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX5", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX5", "DMIC7", "TX DMIC7"},
-
        {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
        {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"},
@@ -1718,16 +1801,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"},
-       {"TX DMIC MUX6", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX6", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX6", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX6", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX6", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX6", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX6", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX6", "DMIC7", "TX DMIC7"},
-
        {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
        {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"},
@@ -1743,16 +1816,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"},
        {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"},
 
-       {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"},
-       {"TX DMIC MUX7", "DMIC0", "TX DMIC0"},
-       {"TX DMIC MUX7", "DMIC1", "TX DMIC1"},
-       {"TX DMIC MUX7", "DMIC2", "TX DMIC2"},
-       {"TX DMIC MUX7", "DMIC3", "TX DMIC3"},
-       {"TX DMIC MUX7", "DMIC4", "TX DMIC4"},
-       {"TX DMIC MUX7", "DMIC5", "TX DMIC5"},
-       {"TX DMIC MUX7", "DMIC6", "TX DMIC6"},
-       {"TX DMIC MUX7", "DMIC7", "TX DMIC7"},
-
        {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
        {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
        {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"},
@@ -1769,6 +1832,200 @@ static const struct snd_soc_dapm_route tx_audio_map[] = {
        {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"},
 };
 
+/* Controls and routes specific to LPASS >= v9.2.0 */
+static const char * const smic_mux_text_v9_2[] = {
+       "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3",
+       "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7",
+       "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+                       0, smic_mux_text_v9_2);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2,
+                       snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = {
+       SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2),
+       SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2),
+
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT0"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT1"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT2"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT3"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT4"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT5"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT6"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT7"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT8"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT9"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT10"),
+       SND_SOC_DAPM_INPUT("TX SWR_INPUT11"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = {
+       {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+       {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"},
+       {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"},
+
+       {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+       {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+       {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+       {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+       {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+       {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+       {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"},
+
+       {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+       {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+       {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"},
+       {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"},
+       {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"},
+       {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"},
+       {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"},
+       {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"},
+       {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"},
+       {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"},
+       {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"},
+       {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"},
+       {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"},
+       {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"},
+};
+
 static const struct snd_kcontrol_new tx_macro_snd_controls[] = {
        SOC_SINGLE_S8_TLV("TX_DEC0 Volume",
                          CDC_TX0_TX_VOL_CTL,
@@ -1823,10 +2080,41 @@ static const struct snd_kcontrol_new tx_macro_snd_controls[] = {
                       tx_macro_get_bcs, tx_macro_set_bcs),
 };
 
+static int tx_macro_component_extend(struct snd_soc_component *comp)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp);
+       struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+       int ret;
+
+       if (tx->data->extra_widgets_num) {
+               ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets,
+                                               tx->data->extra_widgets_num);
+               if (ret) {
+                       dev_err(tx->dev, "failed to add extra widgets: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (tx->data->extra_routes_num) {
+               ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes,
+                                             tx->data->extra_routes_num);
+               if (ret) {
+                       dev_err(tx->dev, "failed to add extra routes: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int tx_macro_component_probe(struct snd_soc_component *comp)
 {
        struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
-       int i;
+       int i, ret;
+
+       ret = tx_macro_component_extend(comp);
+       if (ret)
+               return ret;
 
        snd_soc_component_init_regmap(comp, tx->regmap);
 
@@ -1848,8 +2136,10 @@ static int tx_macro_component_probe(struct snd_soc_component *comp)
        snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F,
                                      0x0A);
        /* Enable swr mic0 and mic1 clock */
-       snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00);
-       snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00);
+       snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL,
+                               CDC_TX_SWR_MIC_CLK_DEFAULT);
+       snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL,
+                               CDC_TX_SWR_MIC_CLK_DEFAULT);
 
        return 0;
 }
@@ -1954,17 +2244,16 @@ static int tx_macro_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
-       kernel_ulong_t flags;
        struct tx_macro *tx;
        void __iomem *base;
        int ret, reg;
 
-       flags = (kernel_ulong_t)device_get_match_data(dev);
-
        tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
        if (!tx)
                return -ENOMEM;
 
+       tx->data = device_get_match_data(dev);
+
        tx->macro = devm_clk_get_optional(dev, "macro");
        if (IS_ERR(tx->macro))
                return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n");
@@ -1977,7 +2266,7 @@ static int tx_macro_probe(struct platform_device *pdev)
        if (IS_ERR(tx->mclk))
                return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n");
 
-       if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+       if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
                tx->npl = devm_clk_get(dev, "npl");
                if (IS_ERR(tx->npl))
                        return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n");
@@ -2052,7 +2341,7 @@ static int tx_macro_probe(struct platform_device *pdev)
 
 
        /* reset soundwire block */
-       if (flags & LPASS_MACRO_FLAG_RESET_SWR)
+       if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
                regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
                                   CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
 
@@ -2060,7 +2349,7 @@ static int tx_macro_probe(struct platform_device *pdev)
                           CDC_TX_SWR_CLK_EN_MASK,
                           CDC_TX_SWR_CLK_ENABLE);
 
-       if (flags & LPASS_MACRO_FLAG_RESET_SWR)
+       if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
                regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
                                   CDC_TX_SWR_RESET_MASK, 0x0);
 
@@ -2164,25 +2453,75 @@ static const struct dev_pm_ops tx_macro_pm_ops = {
        SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
 };
 
+static const struct tx_macro_data lpass_ver_9 = {
+       .flags                  = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+                                 LPASS_MACRO_FLAG_RESET_SWR,
+       .ver                    = LPASS_VER_9_0_0,
+       .extra_widgets          = tx_macro_dapm_widgets_v9,
+       .extra_widgets_num      = ARRAY_SIZE(tx_macro_dapm_widgets_v9),
+       .extra_routes           = tx_audio_map_v9,
+       .extra_routes_num       = ARRAY_SIZE(tx_audio_map_v9),
+};
+
+static const struct tx_macro_data lpass_ver_9_2 = {
+       .flags                  = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+                                 LPASS_MACRO_FLAG_RESET_SWR,
+       .ver                    = LPASS_VER_9_2_0,
+       .extra_widgets          = tx_macro_dapm_widgets_v9_2,
+       .extra_widgets_num      = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+       .extra_routes           = tx_audio_map_v9_2,
+       .extra_routes_num       = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_10_sm6115 = {
+       .flags                  = LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       .ver                    = LPASS_VER_10_0_0,
+       .extra_widgets          = tx_macro_dapm_widgets_v9_2,
+       .extra_widgets_num      = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+       .extra_routes           = tx_audio_map_v9_2,
+       .extra_routes_num       = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_11 = {
+       .flags                  = LPASS_MACRO_FLAG_RESET_SWR,
+       .ver                    = LPASS_VER_11_0_0,
+       .extra_widgets          = tx_macro_dapm_widgets_v9_2,
+       .extra_widgets_num      = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+       .extra_routes           = tx_audio_map_v9_2,
+       .extra_routes_num       = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
 static const struct of_device_id tx_macro_dt_match[] = {
        {
+               /*
+                * The block is actually LPASS v9.4, but keep LPASS v9 match
+                * data and audio widgets, due to compatibility reasons.
+                * Microphones are working on SC7280 fine, so apparently the fix
+                * is not necessary.
+                */
                .compatible = "qcom,sc7280-lpass-tx-macro",
-               .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR),
+               .data = &lpass_ver_9,
        }, {
                .compatible = "qcom,sm6115-lpass-tx-macro",
-               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+               .data = &lpass_ver_10_sm6115,
        }, {
                .compatible = "qcom,sm8250-lpass-tx-macro",
-               .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR),
+               .data = &lpass_ver_9,
        }, {
                .compatible = "qcom,sm8450-lpass-tx-macro",
-               .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR),
+               .data = &lpass_ver_9_2,
        }, {
                .compatible = "qcom,sm8550-lpass-tx-macro",
-               .data = (void *)LPASS_MACRO_FLAG_RESET_SWR,
+               .data = &lpass_ver_11,
        }, {
                .compatible = "qcom,sc8280xp-lpass-tx-macro",
-               .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR),
+               /*
+                * The block is actually LPASS v9.3, but keep LPASS v9 match
+                * data and audio widgets, due to compatibility reasons.
+                * Microphones are working on SC8280xp fine, so apparently the
+                * fix is not necessary.
+                */
+               .data = &lpass_ver_9,
        },
        { }
 };
index b71ef03c4aef024b257a0d6ffea50cc556521458..6eceeff10bf6dca64fa39960499ca402e62402bd 100644 (file)
@@ -201,10 +201,12 @@ struct va_macro {
        unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS];
        u16 dmic_clk_div;
        bool has_swr_master;
+       bool has_npl_clk;
 
        int dec_mode[VA_MACRO_NUM_DECIMATORS];
        struct regmap *regmap;
        struct clk *mclk;
+       struct clk *npl;
        struct clk *macro;
        struct clk *dcodec;
        struct clk *fsgen;
@@ -225,14 +227,22 @@ struct va_macro {
 
 struct va_macro_data {
        bool has_swr_master;
+       bool has_npl_clk;
 };
 
 static const struct va_macro_data sm8250_va_data = {
        .has_swr_master = false,
+       .has_npl_clk = false,
 };
 
 static const struct va_macro_data sm8450_va_data = {
        .has_swr_master = true,
+       .has_npl_clk = true,
+};
+
+static const struct va_macro_data sm8550_va_data = {
+       .has_swr_master = true,
+       .has_npl_clk = false,
 };
 
 static bool va_is_volatile_register(struct device *dev, unsigned int reg)
@@ -1332,6 +1342,12 @@ static int fsgen_gate_enable(struct clk_hw *hw)
        struct regmap *regmap = va->regmap;
        int ret;
 
+       if (va->has_swr_master) {
+               ret = clk_prepare_enable(va->mclk);
+               if (ret)
+                       return ret;
+       }
+
        ret = va_macro_mclk_enable(va, true);
        if (va->has_swr_master)
                regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
@@ -1350,6 +1366,8 @@ static void fsgen_gate_disable(struct clk_hw *hw)
                           CDC_VA_SWR_CLK_EN_MASK, 0x0);
 
        va_macro_mclk_enable(va, false);
+       if (va->has_swr_master)
+               clk_disable_unprepare(va->mclk);
 }
 
 static int fsgen_gate_is_enabled(struct clk_hw *hw)
@@ -1378,6 +1396,9 @@ static int va_macro_register_fsgen_output(struct va_macro *va)
        struct clk_init_data init;
        int ret;
 
+       if (va->has_npl_clk)
+               parent = va->npl;
+
        parent_clk_name = __clk_get_name(parent);
 
        of_property_read_string(np, "clock-output-names", &clk_name);
@@ -1500,10 +1521,21 @@ static int va_macro_probe(struct platform_device *pdev)
 
        data = of_device_get_match_data(dev);
        va->has_swr_master = data->has_swr_master;
+       va->has_npl_clk = data->has_npl_clk;
 
        /* mclk rate */
        clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
 
+       if (va->has_npl_clk) {
+               va->npl = devm_clk_get(dev, "npl");
+               if (IS_ERR(va->npl)) {
+                       ret = PTR_ERR(va->npl);
+                       goto err;
+               }
+
+               clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ);
+       }
+
        ret = clk_prepare_enable(va->macro);
        if (ret)
                goto err;
@@ -1516,6 +1548,12 @@ static int va_macro_probe(struct platform_device *pdev)
        if (ret)
                goto err_mclk;
 
+       if (va->has_npl_clk) {
+               ret = clk_prepare_enable(va->npl);
+               if (ret)
+                       goto err_npl;
+       }
+
        if (va->has_swr_master) {
                /* Set default CLK div to 1 */
                regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0,
@@ -1564,6 +1602,9 @@ static int va_macro_probe(struct platform_device *pdev)
        return 0;
 
 err_clkout:
+       if (va->has_npl_clk)
+               clk_disable_unprepare(va->npl);
+err_npl:
        clk_disable_unprepare(va->mclk);
 err_mclk:
        clk_disable_unprepare(va->dcodec);
@@ -1579,6 +1620,9 @@ static void va_macro_remove(struct platform_device *pdev)
 {
        struct va_macro *va = dev_get_drvdata(&pdev->dev);
 
+       if (va->has_npl_clk)
+               clk_disable_unprepare(va->npl);
+
        clk_disable_unprepare(va->mclk);
        clk_disable_unprepare(va->dcodec);
        clk_disable_unprepare(va->macro);
@@ -1593,6 +1637,9 @@ static int __maybe_unused va_macro_runtime_suspend(struct device *dev)
        regcache_cache_only(va->regmap, true);
        regcache_mark_dirty(va->regmap);
 
+       if (va->has_npl_clk)
+               clk_disable_unprepare(va->npl);
+
        clk_disable_unprepare(va->mclk);
 
        return 0;
@@ -1609,6 +1656,15 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev)
                return ret;
        }
 
+       if (va->has_npl_clk) {
+               ret = clk_prepare_enable(va->npl);
+               if (ret) {
+                       clk_disable_unprepare(va->mclk);
+                       dev_err(va->dev, "unable to prepare npl\n");
+                       return ret;
+               }
+       }
+
        regcache_cache_only(va->regmap, false);
        regcache_sync(va->regmap);
 
@@ -1624,6 +1680,7 @@ static const struct of_device_id va_macro_dt_match[] = {
        { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data },
        { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data },
        { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data },
+       { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data },
        { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data },
        {}
 };
index a2cca3436c68ef14e6f2897cb53408a1be431c89..950105e5bffdc2c7606e10e7af6f3cf97ece5b4c 100644 (file)
@@ -314,7 +314,7 @@ static int max98363_update_status(struct sdw_slave *slave,
        return max98363_io_init(slave);
 }
 
-static struct sdw_slave_ops max98363_slave_ops = {
+static const struct sdw_slave_ops max98363_slave_ops = {
        .read_prop = max98363_read_prop,
        .update_status = max98363_update_status,
 };
index b5cb951af57040b93a9ea9f01538c1bf23f599a9..383e551f3bc7b86648d67367b721f474269fdf2c 100644 (file)
@@ -821,7 +821,7 @@ static int max98373_bus_config(struct sdw_slave *slave,
  * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
  * port_prep are not defined for now
  */
-static struct sdw_slave_ops max98373_slave_ops = {
+static const struct sdw_slave_ops max98373_slave_ops = {
        .read_prop = max98373_read_prop,
        .update_status = max98373_update_status,
        .bus_config = max98373_bus_config,
index f66417a0f29f636ef8b6776697c081a808a9f08a..22251fb2fa1f8b13eaf23fc40fc9b2bb8316b6fa 100644 (file)
@@ -26,7 +26,6 @@
 #include <sound/tlv.h>
 #include "nau8540.h"
 
-
 #define NAU_FREF_MAX 13500000
 #define NAU_FVCO_MAX 100000000
 #define NAU_FVCO_MIN 90000000
@@ -230,6 +229,49 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new digital_ch1_mux =
        SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
 
+static int nau8540_fepga_event(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+                                  NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF |
+                                  NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF |
+                                  NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF |
+                                  NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF |
+                                  NAU8540_ACDC_CTL_MIC4N_VREF);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int nau8540_precharge_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+                                  NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN);
+               msleep(40);
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+                                  NAU8540_DISCHRG_EN, 0);
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+                                  NAU8540_ACDC_CTL_MASK, 0);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
 static int adc_power_control(struct snd_soc_dapm_widget *w,
                struct snd_kcontrol *k, int  event)
 {
@@ -237,8 +279,10 @@ static int adc_power_control(struct snd_soc_dapm_widget *w,
        struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
-               msleep(300);
+               msleep(160);
                /* DO12 and DO34 pad output enable */
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+                                  NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN);
                regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
                        NAU8540_I2S_DO12_TRI, 0);
                regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
@@ -248,6 +292,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w,
                        NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI);
                regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
                        NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+                                  NAU8540_ADC_ALL_EN, 0);
        }
        return 0;
 }
@@ -274,28 +320,26 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
        SND_SOC_DAPM_INPUT("MIC3"),
        SND_SOC_DAPM_INPUT("MIC4"),
 
-       SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
-
-       SND_SOC_DAPM_ADC_E("ADC1", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control,
-               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-       SND_SOC_DAPM_ADC_E("ADC2", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control,
-               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-       SND_SOC_DAPM_ADC_E("ADC3", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control,
-               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-       SND_SOC_DAPM_ADC_E("ADC4", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control,
-               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-
-       SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0,
+                          nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0,
+                          nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0,
+                          nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0,
+                          nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+
+       SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0,
+                          nau8540_precharge_event, SND_SOC_DAPM_POST_PMU),
+
+       SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0,
+                          adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0,
+                          adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0,
+                          adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0,
+                          adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
        SND_SOC_DAPM_MUX("Digital CH4 Mux",
                SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
@@ -316,20 +360,20 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
        {"Frontend PGA3", NULL, "MIC3"},
        {"Frontend PGA4", NULL, "MIC4"},
 
-       {"ADC1", NULL, "Frontend PGA1"},
-       {"ADC2", NULL, "Frontend PGA2"},
-       {"ADC3", NULL, "Frontend PGA3"},
-       {"ADC4", NULL, "Frontend PGA4"},
+       {"Precharge", NULL, "Frontend PGA1"},
+       {"Precharge", NULL, "Frontend PGA2"},
+       {"Precharge", NULL, "Frontend PGA3"},
+       {"Precharge", NULL, "Frontend PGA4"},
 
-       {"ADC CH1", NULL, "ADC1"},
-       {"ADC CH2", NULL, "ADC2"},
-       {"ADC CH3", NULL, "ADC3"},
-       {"ADC CH4", NULL, "ADC4"},
+       {"ADC CH1", NULL, "Precharge"},
+       {"ADC CH2", NULL, "Precharge"},
+       {"ADC CH3", NULL, "Precharge"},
+       {"ADC CH4", NULL, "Precharge"},
 
-       {"ADC1", NULL, "MICBIAS1"},
-       {"ADC2", NULL, "MICBIAS1"},
-       {"ADC3", NULL, "MICBIAS2"},
-       {"ADC4", NULL, "MICBIAS2"},
+       {"ADC CH1", NULL, "MICBIAS1"},
+       {"ADC CH2", NULL, "MICBIAS1"},
+       {"ADC CH3", NULL, "MICBIAS2"},
+       {"ADC CH4", NULL, "MICBIAS2"},
 
        {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
        {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
index 2ce6063d462b916343fae8f0fcac3ccbf018b83d..762bb93b06fdbe03c47e4b26d719ee5ab2370085 100644 (file)
@@ -78,6 +78,7 @@
 
 
 /* POWER_MANAGEMENT (0x01) */
+#define NAU8540_ADC_ALL_EN     0xf
 #define NAU8540_ADC4_EN                (0x1 << 3)
 #define NAU8540_ADC3_EN                (0x1 << 2)
 #define NAU8540_ADC2_EN                (0x1 << 1)
 /* REFERENCE (0x68) */
 #define NAU8540_PRECHARGE_DIS          (0x1 << 13)
 #define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_DISCHRG_EN             (0x1 << 11)
 
 /* FEPGA1 (0x69) */
 #define NAU8540_FEPGA1_MODCH2_SHT_SFT  7
 #define NAU8540_FEPGA2_MODCH4_SHT      (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT)
 #define NAU8540_FEPGA2_MODCH3_SHT_SFT  3
 #define NAU8540_FEPGA2_MODCH3_SHT      (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT)
-
+#define NAU8540_ACDC_CTL_SFT           8
+#define NAU8540_ACDC_CTL_MASK          (0xff << NAU8540_ACDC_CTL_SFT)
+#define NAU8540_ACDC_CTL_MIC4N_VREF    (0x1 << 15)
+#define NAU8540_ACDC_CTL_MIC4P_VREF    (0x1 << 14)
+#define NAU8540_ACDC_CTL_MIC3N_VREF    (0x1 << 13)
+#define NAU8540_ACDC_CTL_MIC3P_VREF    (0x1 << 12)
+#define NAU8540_ACDC_CTL_MIC2N_VREF    (0x1 << 11)
+#define NAU8540_ACDC_CTL_MIC2P_VREF    (0x1 << 10)
+#define NAU8540_ACDC_CTL_MIC1N_VREF    (0x1 << 9)
+#define NAU8540_ACDC_CTL_MIC1P_VREF    (0x1 << 8)
 
 /* System Clock Source */
 enum {
index 5cb0de648bd3c30b8e2bd0fb8b97b7c9933221d0..cd30ad649bae04fc85f358a865cd82f7fba5aa9e 100644 (file)
@@ -2836,16 +2836,12 @@ static int nau8825_read_device_properties(struct device *dev,
        if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500)
                dev_warn(dev, "Please set the suitable delay time!\n");
 
-       nau8825->mclk = devm_clk_get(dev, "mclk");
-       if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
-               return -EPROBE_DEFER;
-       } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+       nau8825->mclk = devm_clk_get_optional(dev, "mclk");
+       if (IS_ERR(nau8825->mclk))
+               return PTR_ERR(nau8825->mclk);
+       if (!nau8825->mclk)
                /* The MCLK is managed externally or not used at all */
-               nau8825->mclk = NULL;
                dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
-       } else if (IS_ERR(nau8825->mclk)) {
-               return -EINVAL;
-       }
 
        return 0;
 }
index 7295f44c77eb8a64cf532a5163ec19b2fc450061..4dbbd8bdaaac2a35d3f9218e62415d599e7ad657 100644 (file)
@@ -520,7 +520,7 @@ static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = {
        { "DP2TX", NULL, "V Sense" },
 };
 
-static struct sdw_slave_ops rt1017_sdca_slave_ops = {
+static const struct sdw_slave_ops rt1017_sdca_slave_ops = {
        .read_prop = rt1017_sdca_read_prop,
        .update_status = rt1017_sdca_update_status,
 };
index 9a33e3776b55f1f346ba62f1d1760c653fff83fe..6e78434842509a4713f260b5b62c95dc5b8ade8c 100644 (file)
@@ -1192,7 +1192,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c)
                        IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
                if (ret != 0) {
                        dev_err(&i2c->dev,
-                               "Failed to reguest IRQ: %d\n", ret);
+                               "Failed to request IRQ: %d\n", ret);
                        return ret;
                }
        }
index 981155b046fd80757bf279d983d992bc562db097..f8994f4968c5c9b3771f1ef6706af795da925c60 100644 (file)
@@ -1237,7 +1237,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c)
                        IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
                if (ret != 0) {
                        dev_err(&i2c->dev,
-                               "Failed to reguest IRQ: %d\n", ret);
+                               "Failed to request IRQ: %d\n", ret);
                        return ret;
                }
        }
index ad3783ade1b54aefc8a1a42de706968803038521..03d9839a5de39fb6dbc0e66dd1563129e9140f02 100644 (file)
@@ -1284,7 +1284,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c)
                        IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
                if (ret != 0) {
                        dev_err(&i2c->dev,
-                               "Failed to reguest IRQ: %d\n", ret);
+                               "Failed to request IRQ: %d\n", ret);
                        return ret;
                }
        }
index 3ee6d85268ba59f7797e66483e407efaa3ce7a4d..f475c8cfadae11246d301c5f20bfc2d4d44e4b17 100644 (file)
@@ -279,7 +279,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
                        rt5514_dsp);
                if (ret)
                        dev_err(&rt5514_spi->dev,
-                               "%s Failed to reguest IRQ: %d\n", __func__,
+                               "%s Failed to request IRQ: %d\n", __func__,
                                ret);
                else
                        device_init_wakeup(rt5514_dsp->dev, true);
index 43fc7814fddeb538968525a344ea78b2c3a20173..a8cdc3d6994d565f7470750a4622f2bb57290f6e 100644 (file)
@@ -1054,9 +1054,6 @@ static int rt5514_set_bias_level(struct snd_soc_component *component,
 
        switch (level) {
        case SND_SOC_BIAS_PREPARE:
-               if (IS_ERR(rt5514->mclk))
-                       break;
-
                if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
                        clk_disable_unprepare(rt5514->mclk);
                } else {
@@ -1097,9 +1094,9 @@ static int rt5514_probe(struct snd_soc_component *component)
        struct platform_device *pdev = container_of(component->dev,
                                                   struct platform_device, dev);
 
-       rt5514->mclk = devm_clk_get(component->dev, "mclk");
-       if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
-               return -EPROBE_DEFER;
+       rt5514->mclk = devm_clk_get_optional(component->dev, "mclk");
+       if (IS_ERR(rt5514->mclk))
+               return PTR_ERR(rt5514->mclk);
 
        if (rt5514->pdata.dsp_calib_clk_name) {
                rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
index c13108b51eaf6216ae09ef9f25fbd6a155a5d93c..e7aa60e73961c8b40cff1c672ed50ee7411ea976 100644 (file)
@@ -1174,9 +1174,6 @@ static int rt5616_set_bias_level(struct snd_soc_component *component,
                 * away from ON. Disable the clock in that case, otherwise
                 * enable it.
                 */
-               if (IS_ERR(rt5616->mclk))
-                       break;
-
                if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
                        clk_disable_unprepare(rt5616->mclk);
                } else {
@@ -1225,9 +1222,9 @@ static int rt5616_probe(struct snd_soc_component *component)
        struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
 
        /* Check if MCLK provided */
-       rt5616->mclk = devm_clk_get(component->dev, "mclk");
-       if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
-               return -EPROBE_DEFER;
+       rt5616->mclk = devm_clk_get_optional(component->dev, "mclk");
+       if (IS_ERR(rt5616->mclk))
+               return PTR_ERR(rt5616->mclk);
 
        rt5616->component = component;
 
index e8cdc166bdaa99a948b03966d8cf67dbf49e7440..174872ef35d25c919712ec34842c200520b78e98 100644 (file)
@@ -1949,9 +1949,6 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
                 * away from ON. Disable the clock in that case, otherwise
                 * enable it.
                 */
-               if (IS_ERR(rt5640->mclk))
-                       break;
-
                if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
                        clk_disable_unprepare(rt5640->mclk);
                } else {
@@ -2661,9 +2658,9 @@ static int rt5640_probe(struct snd_soc_component *component)
        u32 val;
 
        /* Check if MCLK provided */
-       rt5640->mclk = devm_clk_get(component->dev, "mclk");
-       if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
-               return -EPROBE_DEFER;
+       rt5640->mclk = devm_clk_get_optional(component->dev, "mclk");
+       if (IS_ERR(rt5640->mclk))
+               return PTR_ERR(rt5640->mclk);
 
        rt5640->component = component;
 
index 20191a4473c2d2c7b4ae6bddf34109a403b84e3d..e3ba04484813a21904727a09725c608bf27c34a2 100644 (file)
@@ -4223,7 +4223,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c)
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                        | IRQF_ONESHOT, "rt5645", rt5645);
                if (ret) {
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
                        goto err_enable;
                }
        }
index 0cee4fd1c84b1f197e0234d08a75cd9e878f51fa..33a34bd0b4055a0ddf6beed368b319f222b11608 100644 (file)
@@ -2261,7 +2261,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c)
                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                               | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
        if (ret) {
-               dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n",
+               dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n",
                         rt5651->irq, ret);
                rt5651->irq = -ENXIO;
        }
index a061028a16d82b812ee2b318c63c942a96546edd..fb094c0fe740f8c1165f7392010a1a85bbf664ec 100644 (file)
@@ -4292,7 +4292,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
                        rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                        | IRQF_ONESHOT, "rt5659", rt5659);
                if (ret)
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
 
                /* Enable IRQ output for GPIO1 pin any way */
                regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1,
index 0cecfd6024158d3f068e6773de24aafce87b8141..d5c2f0f2df98354588bbff64a0c67dd3f3987309 100644 (file)
@@ -1079,9 +1079,6 @@ static int rt5660_set_bias_level(struct snd_soc_component *component,
                snd_soc_component_update_bits(component, RT5660_GEN_CTRL1,
                        RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL);
 
-               if (IS_ERR(rt5660->mclk))
-                       break;
-
                if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
                        clk_disable_unprepare(rt5660->mclk);
                } else {
@@ -1277,9 +1274,9 @@ static int rt5660_i2c_probe(struct i2c_client *i2c)
                return -ENOMEM;
 
        /* Check if MCLK provided */
-       rt5660->mclk = devm_clk_get(&i2c->dev, "mclk");
-       if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER)
-               return -EPROBE_DEFER;
+       rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+       if (IS_ERR(rt5660->mclk))
+               return PTR_ERR(rt5660->mclk);
 
        i2c_set_clientdata(i2c, rt5660);
 
index 9550492605ac1eda7d56de33770d6d28f9a27ff3..161dcb3915f9aa7b95396b023d875f7dfe7e188e 100644 (file)
@@ -3692,7 +3692,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c)
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                        | IRQF_ONESHOT, "rt5663", rt5663);
                if (ret) {
-                       dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n",
+                       dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n",
                                __func__, ret);
                        goto err_enable;
                }
index a39de4a7df002c99b3ee0b56e0b7c6060b9adaef..6f778c8f08328b5f02e156126bd78e8766bdf79a 100644 (file)
@@ -4929,7 +4929,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c)
                        rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                        | IRQF_ONESHOT, "rt5665", rt5665);
                if (ret)
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
 
        }
 
index 4623b3e62487064719175b4f2f8f375b758c26c2..6d8e228ccb5742b1897561d6395869f87d89208d 100644 (file)
@@ -2580,7 +2580,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
                        rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                        | IRQF_ONESHOT, "rt5668", rt5668);
                if (ret)
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
 
        }
 
index fbad1ed066260194285d2151769d0533527a0b65..62f26ce9d4760451320613dad001d65d71fde4ac 100644 (file)
@@ -266,7 +266,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c)
                if (!ret)
                        rt5682->irq = i2c->irq;
                else
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
        }
 
 #ifdef CONFIG_COMMON_CLK
index 3322056bbb3b56f30de817e4367dc8c08d46de9d..12741668fdb372f12a80e42ded29555c9218b767 100644 (file)
@@ -3283,7 +3283,7 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c)
                if (!ret)
                        rt5682s->irq = i2c->irq;
                else
-                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+                       dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
        }
 
        return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev,
index ba08d03e717cc0b660986e581d36a4432d48d604..0926b26619bd45b69f5b0b4b5508d677ca207766 100644 (file)
@@ -944,7 +944,7 @@ static const struct dev_pm_ops rt712_sdca_dmic_pm = {
 };
 
 
-static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
+static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
        .read_prop = rt712_sdca_dmic_read_prop,
        .update_status = rt712_sdca_dmic_update_status,
 };
index 6b644a89c5890bb0a802c97c9ecbcfd977d6b35d..01ac555cd79b84a0b1aabe57899b1fedc214a2b5 100644 (file)
@@ -331,7 +331,7 @@ io_error:
        return ret;
 }
 
-static struct sdw_slave_ops rt712_sdca_slave_ops = {
+static const struct sdw_slave_ops rt712_sdca_slave_ops = {
        .read_prop = rt712_sdca_read_prop,
        .interrupt_callback = rt712_sdca_interrupt_callback,
        .update_status = rt712_sdca_update_status,
index e24b9cbdc10c958ad6b6fd932a18a533d81d7dc9..eb76f4c675b67fd59df00cb41a17955c751bbd44 100644 (file)
@@ -362,7 +362,7 @@ io_error:
        return ret;
 }
 
-static struct sdw_slave_ops rt722_sdca_slave_ops = {
+static const struct sdw_slave_ops rt722_sdca_slave_ops = {
        .read_prop = rt722_sdca_read_prop,
        .interrupt_callback = rt722_sdca_interrupt_callback,
        .update_status = rt722_sdca_update_status,
index 5d0e5348b361a568475fd1fde3a299a57926b365..3aa81514dad76f319e3bd0fc86b1709855dde28c 100644 (file)
@@ -408,7 +408,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv)
 {
        if (gpio_is_valid(tas_priv->irq_info.irq_gpio))
                gpio_free(tas_priv->irq_info.irq_gpio);
-       kfree(tas_priv->acpi_subsystem_id);
        mutex_destroy(&tas_priv->codec_lock);
 }
 EXPORT_SYMBOL_GPL(tasdevice_remove);
index 85e14ff61769a475e50dc72f80b9d4825bfcc5ee..45760fe195237435f276655f84019908aa4b4dfe 100644 (file)
@@ -1531,7 +1531,7 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
        unsigned int sleep_time;
        unsigned int len;
        unsigned int nr_cmds;
-       unsigned char *data = block->data;
+       unsigned char *data;
        unsigned char crc_chksum = 0;
        unsigned char offset;
        unsigned char book;
index 4e365305827537bf2e82396a1d8a517baffdf0d9..eeb9bc5b01e2606d719d8e2f46c759bbb25a9283 100644 (file)
@@ -47,6 +47,7 @@ enum wcd_codec_version {
        /* New CLSH after this */
        WCD937X  = 2,
        WCD938X  = 3,
+       WCD939X  = 4,
 };
 struct wcd_clsh_ctrl;
 
index 5da1934527f347019bdc71bff8f00ea7e93307ca..0e6218ed0e5e013882a3d77b26c8f120ee3bad0f 100644 (file)
@@ -16,6 +16,7 @@
 #define HS_DETECT_PLUG_TIME_MS         (3 * 1000)
 #define MBHC_BUTTON_PRESS_THRESHOLD_MIN        250
 #define GND_MIC_SWAP_THRESHOLD         4
+#define GND_MIC_USBC_SWAP_THRESHOLD    2
 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
 #define HPHL_CROSS_CONN_THRESHOLD      100
 #define HS_VREF_MIN_VAL                        1400
@@ -52,12 +53,15 @@ struct wcd_mbhc {
        struct wcd_mbhc_field *fields;
        /* Delayed work to report long button press */
        struct delayed_work mbhc_btn_dwork;
+       /* Work to handle plug report */
+       struct work_struct mbhc_plug_detect_work;
        /* Work to correct accessory type */
        struct work_struct correct_plug_swch;
        struct mutex lock;
        int buttons_pressed;
        u32 hph_status; /* track headhpone status */
        u8 current_plug;
+       unsigned int swap_thr;
        bool is_btn_press;
        bool in_swch_irq_handler;
        bool hs_detect_work_stop;
@@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
        }
 }
 
-static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+static void mbhc_plug_detect_fn(struct work_struct *work)
 {
-       struct snd_soc_component *component;
+       struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work);
+       struct snd_soc_component *component = mbhc->component;
        enum snd_jack_types jack_type;
-       struct wcd_mbhc *mbhc = data;
        bool detection_type;
 
-       component = mbhc->component;
        mutex_lock(&mbhc->lock);
 
        mbhc->in_swch_irq_handler = true;
@@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
 exit:
        mbhc->in_swch_irq_handler = false;
        mutex_unlock(&mbhc->lock);
+}
+
+static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+{
+       struct wcd_mbhc *mbhc = data;
+
+       if (!mbhc->cfg->typec_analog_mux)
+               schedule_work(&mbhc->mbhc_plug_detect_work);
+
        return IRQ_HANDLED;
 }
 
+int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc)
+{
+
+       if (!mbhc || !mbhc->cfg->typec_analog_mux)
+               return -EINVAL;
+
+       if (mbhc->mbhc_cb->clk_setup)
+               mbhc->mbhc_cb->clk_setup(mbhc->component, false);
+
+       wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
+       wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0);
+
+       schedule_work(&mbhc->mbhc_plug_detect_work);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);
+
+int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc)
+{
+       if (!mbhc || !mbhc->cfg->typec_analog_mux)
+               return -EINVAL;
+
+       if (mbhc->mbhc_cb->clk_setup)
+               mbhc->mbhc_cb->clk_setup(mbhc->component, true);
+       wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
+
+       schedule_work(&mbhc->mbhc_plug_detect_work);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);
+
 static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
 {
        int mask = 0;
@@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
 
        mutex_lock(&mbhc->lock);
 
-       /* enable HS detection */
+       if (mbhc->cfg->typec_analog_mux)
+               mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
+       else
+               mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD;
+
+       /* setup HS detection */
        if (mbhc->mbhc_cb->hph_pull_up_control_v2)
                mbhc->mbhc_cb->hph_pull_up_control_v2(component,
-                                                     HS_PULLUP_I_DEFAULT);
+                               mbhc->cfg->typec_analog_mux ?
+                                       HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT);
        else if (mbhc->mbhc_cb->hph_pull_up_control)
-               mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
+               mbhc->mbhc_cb->hph_pull_up_control(component,
+                               mbhc->cfg->typec_analog_mux ?
+                                       I_OFF : I_DEFAULT);
        else
-               wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
+               wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+                               mbhc->cfg->typec_analog_mux ? 0 : 3);
 
        wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
        wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
@@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
                mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
        wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
 
-       wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
+       /* Plug detect is triggered manually if analog goes through USBCC */
+       if (mbhc->cfg->typec_analog_mux)
+               wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
+       else
+               wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
 
-       /* Insertion debounce set to 96ms */
-       wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
+       if (mbhc->cfg->typec_analog_mux)
+               /* Insertion debounce set to 48ms */
+               wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4);
+       else
+               /* Insertion debounce set to 96ms */
+               wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
 
        /* Button Debounce set to 16ms */
        wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
@@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
        mbhc->mbhc_cb->mbhc_bias(component, true);
        /* enable MBHC clock */
        if (mbhc->mbhc_cb->clk_setup)
-               mbhc->mbhc_cb->clk_setup(component, true);
+               mbhc->mbhc_cb->clk_setup(component,
+                               mbhc->cfg->typec_analog_mux ? false : true);
 
        /* program HS_VREF value */
        wcd_program_hs_vref(mbhc);
@@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
        do {
                cross_conn = wcd_check_cross_conn(mbhc);
                try++;
-       } while (try < GND_MIC_SWAP_THRESHOLD);
+       } while (try < mbhc->swap_thr);
 
        if (cross_conn > 0) {
                plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
@@ -1183,7 +1246,7 @@ correct_plug_type:
                        cross_conn = wcd_check_cross_conn(mbhc);
                        if (cross_conn > 0) { /* cross-connection */
                                pt_gnd_mic_swap_cnt++;
-                               if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
+                               if (pt_gnd_mic_swap_cnt < mbhc->swap_thr)
                                        continue;
                                else
                                        plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
@@ -1194,7 +1257,7 @@ correct_plug_type:
                        } else /* Error if (cross_conn < 0) */
                                continue;
 
-                       if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
+                       if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
                                /* US_EU gpio present, flip switch */
                                if (mbhc->cfg->swap_gnd_mic) {
                                        if (mbhc->cfg->swap_gnd_mic(component, true))
@@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
        mutex_init(&mbhc->lock);
 
        INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
+       INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn);
 
        ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
                                        wcd_mbhc_mech_plug_detect_irq,
@@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
 
        mutex_lock(&mbhc->lock);
        wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+       cancel_work_sync(&mbhc->mbhc_plug_detect_work);
        mutex_unlock(&mbhc->lock);
 
        kfree(mbhc);
index 006118f3e81f2a4c44c6f7375c5f64587401251a..df68e99c81a33766591ccfda5a31303715db2ab4 100644 (file)
@@ -193,6 +193,7 @@ struct wcd_mbhc_config {
        int v_hs_max;
        int num_btn;
        bool mono_stero_detection;
+       bool typec_analog_mux;
        bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active);
        bool hs_ext_micbias;
        bool gnd_det_en;
@@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg,
 void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
 void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type);
 int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc);
+int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc);
+int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc);
 struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
                      const struct wcd_mbhc_cb *mbhc_cb,
                      const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
new file mode 100644 (file)
index 0000000..8acb565
--- /dev/null
@@ -0,0 +1,1551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "wcd939x.h"
+
+#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
+
+static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = {
+       WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_COMP_L, WCD939X_COMP_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_COMP_R, WCD939X_COMP_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_LO, WCD939X_LO_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_DSD_L, WCD939X_DSD_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_DSD_R, WCD939X_DSD_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_HIFI_PCM_L, WCD939X_HIFI_PCM_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)),
+};
+
+static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = {
+       WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)),
+       WCD_SDW_CH(WCD939X_ADC4, WCD939X_ADC_1_4_PORT, BIT(3)),
+       WCD_SDW_CH(WCD939X_DMIC0, WCD939X_DMIC_0_3_MBHC_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_DMIC1, WCD939X_DMIC_0_3_MBHC_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_MBHC, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)),
+       WCD_SDW_CH(WCD939X_DMIC2, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)),
+       WCD_SDW_CH(WCD939X_DMIC3, WCD939X_DMIC_0_3_MBHC_PORT, BIT(3)),
+       WCD_SDW_CH(WCD939X_DMIC4, WCD939X_DMIC_3_7_PORT, BIT(0)),
+       WCD_SDW_CH(WCD939X_DMIC5, WCD939X_DMIC_3_7_PORT, BIT(1)),
+       WCD_SDW_CH(WCD939X_DMIC6, WCD939X_DMIC_3_7_PORT, BIT(2)),
+       WCD_SDW_CH(WCD939X_DMIC7, WCD939X_DMIC_3_7_PORT, BIT(3)),
+};
+
+static struct sdw_dpn_prop wcd939x_rx_dpn_prop[WCD939X_MAX_RX_SWR_PORTS] = {
+       {
+               .num = WCD939X_HPH_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 2,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_CLSH_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 1,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_COMP_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 2,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_LO_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 1,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_DSD_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 2,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_HIFI_PCM_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 2,
+               .simple_ch_prep_sm = true,
+       }
+};
+
+static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = {
+       {
+               .num = WCD939X_ADC_1_4_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 4,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_ADC_DMIC_1_2_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 4,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_DMIC_0_3_MBHC_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 4,
+               .simple_ch_prep_sm = true,
+       },
+       {
+               .num = WCD939X_DMIC_3_7_PORT,
+               .type = SDW_DPN_SIMPLE,
+               .min_ch = 1,
+               .max_ch = 4,
+               .simple_ch_prep_sm = true,
+       }
+};
+
+struct device *wcd939x_sdw_device_get(struct device_node *np)
+{
+       return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_device_get);
+
+unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev)
+{
+       return FIELD_GET(SDW_SCP_STAT_CURR_BANK,
+                        sdw_read(sdev, SDW_SCP_CTRL));
+}
+EXPORT_SYMBOL_GPL(wcd939x_swr_get_current_bank);
+
+int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+                         struct snd_pcm_substream *substream,
+                         struct snd_pcm_hw_params *params,
+                         struct snd_soc_dai *dai)
+{
+       struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS];
+       unsigned long ch_mask;
+       int i, j;
+
+       wcd->sconfig.ch_count = 1;
+       wcd->active_ports = 0;
+       for (i = 0; i < WCD939X_MAX_SWR_PORTS; i++) {
+               ch_mask = wcd->port_config[i].ch_mask;
+
+               if (!ch_mask)
+                       continue;
+
+               for_each_set_bit(j, &ch_mask, 4)
+                       wcd->sconfig.ch_count++;
+
+               port_config[wcd->active_ports] = wcd->port_config[i];
+               wcd->active_ports++;
+       }
+
+       wcd->sconfig.bps = 1;
+       wcd->sconfig.frame_rate = params_rate(params);
+       if (wcd->is_tx)
+               wcd->sconfig.direction = SDW_DATA_DIR_TX;
+       else
+               wcd->sconfig.direction = SDW_DATA_DIR_RX;
+
+       wcd->sconfig.type = SDW_STREAM_PCM;
+
+       return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, &port_config[0],
+                                   wcd->active_ports, wcd->sruntime);
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_hw_params);
+
+int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+                    struct snd_pcm_substream *substream,
+                    struct snd_soc_dai *dai)
+{
+       sdw_stream_remove_slave(wcd->sdev, wcd->sruntime);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_free);
+
+int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+                              struct snd_soc_dai *dai, void *stream,
+                              int direction)
+{
+       wcd->sruntime = stream;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream);
+
+struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd)
+{
+       if (wcd->regmap)
+               return wcd->regmap;
+
+       return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(wcd939x_swr_get_regmap);
+
+static int wcd9390_update_status(struct sdw_slave *slave,
+                                enum sdw_slave_status status)
+{
+       struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
+
+       if (wcd->regmap && status == SDW_SLAVE_ATTACHED) {
+               /* Write out any cached changes that happened between probe and attach */
+               regcache_cache_only(wcd->regmap, false);
+               return regcache_sync(wcd->regmap);
+       }
+
+       return 0;
+}
+
+static int wcd9390_bus_config(struct sdw_slave *slave,
+                             struct sdw_bus_params *params)
+{
+       sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank),
+                 0x01);
+
+       return 0;
+}
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering
+ * the first irq of the slave_irq irq domain, which then will
+ * be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int wcd9390_interrupt_callback(struct sdw_slave *slave,
+                                     struct sdw_slave_intr_status *status)
+{
+       struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
+       struct irq_domain *slave_irq = wcd->slave_irq;
+       u32 sts1, sts2, sts3;
+
+       do {
+               handle_nested_irq(irq_find_mapping(slave_irq, 0));
+               regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_0, &sts1);
+               regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_1, &sts2);
+               regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_2, &sts3);
+
+       } while (sts1 || sts2 || sts3);
+
+       return IRQ_HANDLED;
+}
+
+static const struct reg_default wcd939x_defaults[] = {
+       /* Default values except for Read-Only & Volatile registers */
+       { WCD939X_ANA_PAGE, 0x00 },
+       { WCD939X_ANA_BIAS, 0x00 },
+       { WCD939X_ANA_RX_SUPPLIES, 0x00 },
+       { WCD939X_ANA_HPH, 0x0c },
+       { WCD939X_ANA_EAR, 0x00 },
+       { WCD939X_ANA_EAR_COMPANDER_CTL, 0x02 },
+       { WCD939X_ANA_TX_CH1, 0x20 },
+       { WCD939X_ANA_TX_CH2, 0x00 },
+       { WCD939X_ANA_TX_CH3, 0x20 },
+       { WCD939X_ANA_TX_CH4, 0x00 },
+       { WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 },
+       { WCD939X_ANA_MICB3_DSP_EN_LOGIC, 0x00 },
+       { WCD939X_ANA_MBHC_MECH, 0x39 },
+       { WCD939X_ANA_MBHC_ELECT, 0x08 },
+       { WCD939X_ANA_MBHC_ZDET, 0x00 },
+       { WCD939X_ANA_MBHC_BTN0, 0x00 },
+       { WCD939X_ANA_MBHC_BTN1, 0x10 },
+       { WCD939X_ANA_MBHC_BTN2, 0x20 },
+       { WCD939X_ANA_MBHC_BTN3, 0x30 },
+       { WCD939X_ANA_MBHC_BTN4, 0x40 },
+       { WCD939X_ANA_MBHC_BTN5, 0x50 },
+       { WCD939X_ANA_MBHC_BTN6, 0x60 },
+       { WCD939X_ANA_MBHC_BTN7, 0x70 },
+       { WCD939X_ANA_MICB1, 0x10 },
+       { WCD939X_ANA_MICB2, 0x10 },
+       { WCD939X_ANA_MICB2_RAMP, 0x00 },
+       { WCD939X_ANA_MICB3, 0x00 },
+       { WCD939X_ANA_MICB4, 0x00 },
+       { WCD939X_BIAS_CTL, 0x2a },
+       { WCD939X_BIAS_VBG_FINE_ADJ, 0x55 },
+       { WCD939X_LDOL_VDDCX_ADJUST, 0x01 },
+       { WCD939X_LDOL_DISABLE_LDOL, 0x00 },
+       { WCD939X_MBHC_CTL_CLK, 0x00 },
+       { WCD939X_MBHC_CTL_ANA, 0x00 },
+       { WCD939X_MBHC_ZDET_VNEG_CTL, 0x00 },
+       { WCD939X_MBHC_ZDET_BIAS_CTL, 0x46 },
+       { WCD939X_MBHC_CTL_BCS, 0x00 },
+       { WCD939X_MBHC_TEST_CTL, 0x00 },
+       { WCD939X_LDOH_MODE, 0x2b },
+       { WCD939X_LDOH_BIAS, 0x68 },
+       { WCD939X_LDOH_STB_LOADS, 0x00 },
+       { WCD939X_LDOH_SLOWRAMP, 0x50 },
+       { WCD939X_MICB1_TEST_CTL_1, 0x1a },
+       { WCD939X_MICB1_TEST_CTL_2, 0x00 },
+       { WCD939X_MICB1_TEST_CTL_3, 0xa4 },
+       { WCD939X_MICB2_TEST_CTL_1, 0x1a },
+       { WCD939X_MICB2_TEST_CTL_2, 0x00 },
+       { WCD939X_MICB2_TEST_CTL_3, 0x24 },
+       { WCD939X_MICB3_TEST_CTL_1, 0x9a },
+       { WCD939X_MICB3_TEST_CTL_2, 0x80 },
+       { WCD939X_MICB3_TEST_CTL_3, 0x24 },
+       { WCD939X_MICB4_TEST_CTL_1, 0x1a },
+       { WCD939X_MICB4_TEST_CTL_2, 0x80 },
+       { WCD939X_MICB4_TEST_CTL_3, 0x24 },
+       { WCD939X_TX_COM_ADC_VCM, 0x39 },
+       { WCD939X_TX_COM_BIAS_ATEST, 0xe0 },
+       { WCD939X_TX_COM_SPARE1, 0x00 },
+       { WCD939X_TX_COM_SPARE2, 0x00 },
+       { WCD939X_TX_COM_TXFE_DIV_CTL, 0x22 },
+       { WCD939X_TX_COM_TXFE_DIV_START, 0x00 },
+       { WCD939X_TX_COM_SPARE3, 0x00 },
+       { WCD939X_TX_COM_SPARE4, 0x00 },
+       { WCD939X_TX_1_2_TEST_EN, 0xcc },
+       { WCD939X_TX_1_2_ADC_IB, 0xe9 },
+       { WCD939X_TX_1_2_ATEST_REFCTL, 0x0b },
+       { WCD939X_TX_1_2_TEST_CTL, 0x38 },
+       { WCD939X_TX_1_2_TEST_BLK_EN1, 0xff },
+       { WCD939X_TX_1_2_TXFE1_CLKDIV, 0x00 },
+       { WCD939X_TX_3_4_TEST_EN, 0xcc },
+       { WCD939X_TX_3_4_ADC_IB, 0xe9 },
+       { WCD939X_TX_3_4_ATEST_REFCTL, 0x0b },
+       { WCD939X_TX_3_4_TEST_CTL, 0x38 },
+       { WCD939X_TX_3_4_TEST_BLK_EN3, 0xff },
+       { WCD939X_TX_3_4_TXFE3_CLKDIV, 0x00 },
+       { WCD939X_TX_3_4_TEST_BLK_EN2, 0xfb },
+       { WCD939X_TX_3_4_TXFE2_CLKDIV, 0x00 },
+       { WCD939X_TX_3_4_SPARE1, 0x00 },
+       { WCD939X_TX_3_4_TEST_BLK_EN4, 0xfb },
+       { WCD939X_TX_3_4_TXFE4_CLKDIV, 0x00 },
+       { WCD939X_TX_3_4_SPARE2, 0x00 },
+       { WCD939X_CLASSH_MODE_1, 0x40 },
+       { WCD939X_CLASSH_MODE_2, 0x3a },
+       { WCD939X_CLASSH_MODE_3, 0xf0 },
+       { WCD939X_CLASSH_CTRL_VCL_1, 0x7c },
+       { WCD939X_CLASSH_CTRL_VCL_2, 0x82 },
+       { WCD939X_CLASSH_CTRL_CCL_1, 0x31 },
+       { WCD939X_CLASSH_CTRL_CCL_2, 0x80 },
+       { WCD939X_CLASSH_CTRL_CCL_3, 0x80 },
+       { WCD939X_CLASSH_CTRL_CCL_4, 0x51 },
+       { WCD939X_CLASSH_CTRL_CCL_5, 0x00 },
+       { WCD939X_CLASSH_BUCK_TMUX_A_D, 0x00 },
+       { WCD939X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 },
+       { WCD939X_CLASSH_SPARE, 0x80 },
+       { WCD939X_FLYBACK_EN, 0x4e },
+       { WCD939X_FLYBACK_VNEG_CTRL_1, 0x0b },
+       { WCD939X_FLYBACK_VNEG_CTRL_2, 0x45 },
+       { WCD939X_FLYBACK_VNEG_CTRL_3, 0x14 },
+       { WCD939X_FLYBACK_VNEG_CTRL_4, 0xdb },
+       { WCD939X_FLYBACK_VNEG_CTRL_5, 0x83 },
+       { WCD939X_FLYBACK_VNEG_CTRL_6, 0x98 },
+       { WCD939X_FLYBACK_VNEG_CTRL_7, 0xa9 },
+       { WCD939X_FLYBACK_VNEG_CTRL_8, 0x68 },
+       { WCD939X_FLYBACK_VNEG_CTRL_9, 0x66 },
+       { WCD939X_FLYBACK_VNEGDAC_CTRL_1, 0xed },
+       { WCD939X_FLYBACK_VNEGDAC_CTRL_2, 0xf8 },
+       { WCD939X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 },
+       { WCD939X_FLYBACK_CTRL_1, 0x65 },
+       { WCD939X_FLYBACK_TEST_CTL, 0x02 },
+       { WCD939X_RX_AUX_SW_CTL, 0x00 },
+       { WCD939X_RX_PA_AUX_IN_CONN, 0x01 },
+       { WCD939X_RX_TIMER_DIV, 0x32 },
+       { WCD939X_RX_OCP_CTL, 0x1f },
+       { WCD939X_RX_OCP_COUNT, 0x77 },
+       { WCD939X_RX_BIAS_EAR_DAC, 0xa0 },
+       { WCD939X_RX_BIAS_EAR_AMP, 0xaa },
+       { WCD939X_RX_BIAS_HPH_LDO, 0xa9 },
+       { WCD939X_RX_BIAS_HPH_PA, 0xaa },
+       { WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2, 0xca },
+       { WCD939X_RX_BIAS_HPH_RDAC_LDO, 0x88 },
+       { WCD939X_RX_BIAS_HPH_CNP1, 0x82 },
+       { WCD939X_RX_BIAS_HPH_LOWPOWER, 0x82 },
+       { WCD939X_RX_BIAS_AUX_DAC, 0xa0 },
+       { WCD939X_RX_BIAS_AUX_AMP, 0xaa },
+       { WCD939X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 },
+       { WCD939X_RX_BIAS_MISC, 0x00 },
+       { WCD939X_RX_BIAS_BUCK_RST, 0x08 },
+       { WCD939X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 },
+       { WCD939X_RX_BIAS_FLYB_ERRAMP, 0x40 },
+       { WCD939X_RX_BIAS_FLYB_BUFF, 0xaa },
+       { WCD939X_RX_BIAS_FLYB_MID_RST, 0x14 },
+       { WCD939X_HPH_CNP_EN, 0x80 },
+       { WCD939X_HPH_CNP_WG_CTL, 0x9a },
+       { WCD939X_HPH_CNP_WG_TIME, 0x14 },
+       { WCD939X_HPH_OCP_CTL, 0x28 },
+       { WCD939X_HPH_AUTO_CHOP, 0x56 },
+       { WCD939X_HPH_CHOP_CTL, 0x83 },
+       { WCD939X_HPH_PA_CTL1, 0x46 },
+       { WCD939X_HPH_PA_CTL2, 0x50 },
+       { WCD939X_HPH_L_EN, 0x80 },
+       { WCD939X_HPH_L_TEST, 0xe0 },
+       { WCD939X_HPH_L_ATEST, 0x50 },
+       { WCD939X_HPH_R_EN, 0x80 },
+       { WCD939X_HPH_R_TEST, 0xe0 },
+       { WCD939X_HPH_R_ATEST, 0x50 },
+       { WCD939X_HPH_RDAC_CLK_CTL1, 0x80 },
+       { WCD939X_HPH_RDAC_CLK_CTL2, 0x0b },
+       { WCD939X_HPH_RDAC_LDO_CTL, 0x33 },
+       { WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+       { WCD939X_HPH_REFBUFF_UHQA_CTL, 0x00 },
+       { WCD939X_HPH_REFBUFF_LP_CTL, 0x8e },
+       { WCD939X_HPH_L_DAC_CTL, 0x20 },
+       { WCD939X_HPH_R_DAC_CTL, 0x20 },
+       { WCD939X_HPH_SURGE_COMP_SEL, 0x55 },
+       { WCD939X_HPH_SURGE_EN, 0x19 },
+       { WCD939X_HPH_SURGE_MISC1, 0xa0 },
+       { WCD939X_EAR_EN, 0x22 },
+       { WCD939X_EAR_PA_CON, 0x44 },
+       { WCD939X_EAR_SP_CON, 0xdb },
+       { WCD939X_EAR_DAC_CON, 0x80 },
+       { WCD939X_EAR_CNP_FSM_CON, 0xb2 },
+       { WCD939X_EAR_TEST_CTL, 0x00 },
+       { WCD939X_FLYBACK_NEW_CTRL_2, 0x00 },
+       { WCD939X_FLYBACK_NEW_CTRL_3, 0x00 },
+       { WCD939X_FLYBACK_NEW_CTRL_4, 0x44 },
+       { WCD939X_ANA_NEW_PAGE, 0x00 },
+       { WCD939X_HPH_NEW_ANA_HPH2, 0x00 },
+       { WCD939X_HPH_NEW_ANA_HPH3, 0x00 },
+       { WCD939X_SLEEP_CTL, 0x18 },
+       { WCD939X_SLEEP_WATCHDOG_CTL, 0x00 },
+       { WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 },
+       { WCD939X_MBHC_NEW_CTL_1, 0x02 },
+       { WCD939X_MBHC_NEW_CTL_2, 0x05 },
+       { WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 },
+       { WCD939X_MBHC_NEW_ZDET_ANA_CTL, 0x0f },
+       { WCD939X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 },
+       { WCD939X_TX_NEW_CH12_MUX, 0x11 },
+       { WCD939X_TX_NEW_CH34_MUX, 0x23 },
+       { WCD939X_DIE_CRACK_DET_EN, 0x00 },
+       { WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
+       { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L, 0x00 },
+       { WCD939X_HPH_NEW_INT_RDAC_VREF_CTL, 0x08 },
+       { WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+       { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R, 0x00 },
+       { WCD939X_HPH_NEW_INT_PA_MISC1, 0x32 },
+       { WCD939X_HPH_NEW_INT_PA_MISC2, 0x00 },
+       { WCD939X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+       { WCD939X_HPH_NEW_INT_TIMER1, 0xfe },
+       { WCD939X_HPH_NEW_INT_TIMER2, 0x02 },
+       { WCD939X_HPH_NEW_INT_TIMER3, 0x4e },
+       { WCD939X_HPH_NEW_INT_TIMER4, 0x54 },
+       { WCD939X_HPH_NEW_INT_PA_RDAC_MISC2, 0x0b },
+       { WCD939X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+       { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xa0 },
+       { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xa0 },
+       { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x64 },
+       { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 },
+       { WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 },
+       { WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 },
+       { WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 },
+       { WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 },
+       { WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW, 0x47 },
+       { WCD939X_EAR_INT_NEW_CHOPPER_CON, 0xa8 },
+       { WCD939X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 },
+       { WCD939X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 },
+       { WCD939X_EAR_INT_NEW_DYNAMIC_BIAS, 0x00 },
+       { WCD939X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a },
+       { WCD939X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a },
+       { WCD939X_DIE_CRACK_INT_DET_INT1, 0x02 },
+       { WCD939X_DIE_CRACK_INT_DET_INT2, 0x60 },
+       { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2, 0xff },
+       { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1, 0x7f },
+       { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0, 0x3f },
+       { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M, 0x1f },
+       { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M, 0x0f },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1, 0xd7 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0, 0xc8 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP, 0xc6 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1, 0x95 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0, 0x6a },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, 0x05 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0, 0xa5 },
+       { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, 0x13 },
+       { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1, 0x88 },
+       { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP, 0x42 },
+       { WCD939X_TX_COM_NEW_INT_ADC_INT_L2, 0xff },
+       { WCD939X_TX_COM_NEW_INT_ADC_INT_L1, 0x64 },
+       { WCD939X_TX_COM_NEW_INT_ADC_INT_L0, 0x64 },
+       { WCD939X_TX_COM_NEW_INT_ADC_INT_ULP, 0x77 },
+       { WCD939X_DIGITAL_PAGE, 0x00 },
+       { WCD939X_DIGITAL_SWR_TX_CLK_RATE, 0x00 },
+       { WCD939X_DIGITAL_CDC_RST_CTL, 0x03 },
+       { WCD939X_DIGITAL_TOP_CLK_CFG, 0x00 },
+       { WCD939X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 },
+       { WCD939X_DIGITAL_CDC_DIG_CLK_CTL, 0xf0 },
+       { WCD939X_DIGITAL_SWR_RST_EN, 0x00 },
+       { WCD939X_DIGITAL_CDC_PATH_MODE, 0x55 },
+       { WCD939X_DIGITAL_CDC_RX_RST, 0x00 },
+       { WCD939X_DIGITAL_CDC_RX0_CTL, 0xfc },
+       { WCD939X_DIGITAL_CDC_RX1_CTL, 0xfc },
+       { WCD939X_DIGITAL_CDC_RX2_CTL, 0xfc },
+       { WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00 },
+       { WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00 },
+       { WCD939X_DIGITAL_CDC_COMP_CTL_0, 0x00 },
+       { WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1e },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R1, 0x4d },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R2, 0x29 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R3, 0x34 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R4, 0x59 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R5, 0x66 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R6, 0x87 },
+       { WCD939X_DIGITAL_CDC_HPH_DSM_R7, 0x64 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A1_0, 0x00 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A1_1, 0x01 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A2_0, 0x96 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A2_1, 0x09 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A3_0, 0xab },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A3_1, 0x05 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A4_0, 0x1c },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A4_1, 0x02 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A5_0, 0x17 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A5_1, 0x02 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A6_0, 0xaa },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_A7_0, 0xe3 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_C_0, 0x69 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_C_1, 0x54 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_C_2, 0x02 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_C_3, 0x15 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R1, 0xa4 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R2, 0xb5 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R3, 0x86 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R4, 0x85 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R5, 0xaa },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R6, 0xe2 },
+       { WCD939X_DIGITAL_CDC_EAR_DSM_R7, 0x62 },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 },
+       { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0, 0x00 },
+       { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1, 0xfc },
+       { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2, 0x01 },
+       { WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 },
+       { WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, 0x00 },
+       { WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 },
+       { WCD939X_DIGITAL_CDC_SWR_CLH, 0x00 },
+       { WCD939X_DIGITAL_SWR_CLH_BYP, 0x00 },
+       { WCD939X_DIGITAL_CDC_TX0_CTL, 0x68 },
+       { WCD939X_DIGITAL_CDC_TX1_CTL, 0x68 },
+       { WCD939X_DIGITAL_CDC_TX2_CTL, 0x68 },
+       { WCD939X_DIGITAL_CDC_TX_RST, 0x00 },
+       { WCD939X_DIGITAL_CDC_REQ_CTL, 0x01 },
+       { WCD939X_DIGITAL_CDC_RST, 0x00 },
+       { WCD939X_DIGITAL_CDC_AMIC_CTL, 0x0f },
+       { WCD939X_DIGITAL_CDC_DMIC_CTL, 0x04 },
+       { WCD939X_DIGITAL_CDC_DMIC1_CTL, 0x01 },
+       { WCD939X_DIGITAL_CDC_DMIC2_CTL, 0x01 },
+       { WCD939X_DIGITAL_CDC_DMIC3_CTL, 0x01 },
+       { WCD939X_DIGITAL_CDC_DMIC4_CTL, 0x01 },
+       { WCD939X_DIGITAL_EFUSE_PRG_CTL, 0x00 },
+       { WCD939X_DIGITAL_EFUSE_CTL, 0x2b },
+       { WCD939X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11 },
+       { WCD939X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11 },
+       { WCD939X_DIGITAL_PDM_WD_CTL0, 0x00 },
+       { WCD939X_DIGITAL_PDM_WD_CTL1, 0x00 },
+       { WCD939X_DIGITAL_PDM_WD_CTL2, 0x00 },
+       { WCD939X_DIGITAL_INTR_MODE, 0x00 },
+       { WCD939X_DIGITAL_INTR_MASK_0, 0xff },
+       { WCD939X_DIGITAL_INTR_MASK_1, 0xe7 },
+       { WCD939X_DIGITAL_INTR_MASK_2, 0x0e },
+       { WCD939X_DIGITAL_INTR_CLEAR_0, 0x00 },
+       { WCD939X_DIGITAL_INTR_CLEAR_1, 0x00 },
+       { WCD939X_DIGITAL_INTR_CLEAR_2, 0x00 },
+       { WCD939X_DIGITAL_INTR_LEVEL_0, 0x00 },
+       { WCD939X_DIGITAL_INTR_LEVEL_1, 0x00 },
+       { WCD939X_DIGITAL_INTR_LEVEL_2, 0x00 },
+       { WCD939X_DIGITAL_INTR_SET_0, 0x00 },
+       { WCD939X_DIGITAL_INTR_SET_1, 0x00 },
+       { WCD939X_DIGITAL_INTR_SET_2, 0x00 },
+       { WCD939X_DIGITAL_INTR_TEST_0, 0x00 },
+       { WCD939X_DIGITAL_INTR_TEST_1, 0x00 },
+       { WCD939X_DIGITAL_INTR_TEST_2, 0x00 },
+       { WCD939X_DIGITAL_TX_MODE_DBG_EN, 0x00 },
+       { WCD939X_DIGITAL_TX_MODE_DBG_0_1, 0x00 },
+       { WCD939X_DIGITAL_TX_MODE_DBG_2_3, 0x00 },
+       { WCD939X_DIGITAL_LB_IN_SEL_CTL, 0x00 },
+       { WCD939X_DIGITAL_LOOP_BACK_MODE, 0x00 },
+       { WCD939X_DIGITAL_SWR_DAC_TEST, 0x00 },
+       { WCD939X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 },
+       { WCD939X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 },
+       { WCD939X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 },
+       { WCD939X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 },
+       { WCD939X_DIGITAL_SWR_HM_TEST_TX_2, 0x00 },
+       { WCD939X_DIGITAL_PAD_CTL_SWR_0, 0x8f },
+       { WCD939X_DIGITAL_PAD_CTL_SWR_1, 0x06 },
+       { WCD939X_DIGITAL_I2C_CTL, 0x00 },
+       { WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00 },
+       { WCD939X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 },
+       { WCD939X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 },
+       { WCD939X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 },
+       { WCD939X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 },
+       { WCD939X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 },
+       { WCD939X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 },
+       { WCD939X_DIGITAL_PAD_CTL_PDM_TX2, 0xf1 },
+       { WCD939X_DIGITAL_PAD_INP_DIS_0, 0x00 },
+       { WCD939X_DIGITAL_PAD_INP_DIS_1, 0x00 },
+       { WCD939X_DIGITAL_DRIVE_STRENGTH_0, 0x00 },
+       { WCD939X_DIGITAL_DRIVE_STRENGTH_1, 0x00 },
+       { WCD939X_DIGITAL_DRIVE_STRENGTH_2, 0x00 },
+       { WCD939X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f },
+       { WCD939X_DIGITAL_TX_DATA_EDGE_CTL, 0x80 },
+       { WCD939X_DIGITAL_GPIO_MODE, 0x00 },
+       { WCD939X_DIGITAL_PIN_CTL_OE, 0x00 },
+       { WCD939X_DIGITAL_PIN_CTL_DATA_0, 0x00 },
+       { WCD939X_DIGITAL_PIN_CTL_DATA_1, 0x00 },
+       { WCD939X_DIGITAL_DIG_DEBUG_CTL, 0x00 },
+       { WCD939X_DIGITAL_DIG_DEBUG_EN, 0x00 },
+       { WCD939X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 },
+       { WCD939X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 },
+       { WCD939X_DIGITAL_SSP_DBG, 0x00 },
+       { WCD939X_DIGITAL_SPARE_0, 0x00 },
+       { WCD939X_DIGITAL_SPARE_1, 0x00 },
+       { WCD939X_DIGITAL_SPARE_2, 0x00 },
+       { WCD939X_DIGITAL_TX_REQ_FB_CTL_0, 0x88 },
+       { WCD939X_DIGITAL_TX_REQ_FB_CTL_1, 0x88 },
+       { WCD939X_DIGITAL_TX_REQ_FB_CTL_2, 0x88 },
+       { WCD939X_DIGITAL_TX_REQ_FB_CTL_3, 0x88 },
+       { WCD939X_DIGITAL_TX_REQ_FB_CTL_4, 0x88 },
+       { WCD939X_DIGITAL_DEM_BYPASS_DATA0, 0x55 },
+       { WCD939X_DIGITAL_DEM_BYPASS_DATA1, 0x55 },
+       { WCD939X_DIGITAL_DEM_BYPASS_DATA2, 0x55 },
+       { WCD939X_DIGITAL_DEM_BYPASS_DATA3, 0x01 },
+       { WCD939X_DIGITAL_DEM_SECOND_ORDER, 0x03 },
+       { WCD939X_DIGITAL_DSM_CTRL, 0x00 },
+       { WCD939X_DIGITAL_DSM_0_STATIC_DATA_0, 0x00 },
+       { WCD939X_DIGITAL_DSM_0_STATIC_DATA_1, 0x00 },
+       { WCD939X_DIGITAL_DSM_0_STATIC_DATA_2, 0x00 },
+       { WCD939X_DIGITAL_DSM_0_STATIC_DATA_3, 0x00 },
+       { WCD939X_DIGITAL_DSM_1_STATIC_DATA_0, 0x00 },
+       { WCD939X_DIGITAL_DSM_1_STATIC_DATA_1, 0x00 },
+       { WCD939X_DIGITAL_DSM_1_STATIC_DATA_2, 0x00 },
+       { WCD939X_DIGITAL_DSM_1_STATIC_DATA_3, 0x00 },
+       { WCD939X_RX_TOP_PAGE, 0x00 },
+       { WCD939X_RX_TOP_TOP_CFG0, 0x00 },
+       { WCD939X_RX_TOP_HPHL_COMP_WR_LSB, 0x00 },
+       { WCD939X_RX_TOP_HPHL_COMP_WR_MSB, 0x00 },
+       { WCD939X_RX_TOP_HPHL_COMP_LUT, 0x00 },
+       { WCD939X_RX_TOP_HPHR_COMP_WR_LSB, 0x00 },
+       { WCD939X_RX_TOP_HPHR_COMP_WR_MSB, 0x00 },
+       { WCD939X_RX_TOP_HPHR_COMP_LUT, 0x00 },
+       { WCD939X_RX_TOP_DSD0_DEBUG_CFG1, 0x05 },
+       { WCD939X_RX_TOP_DSD0_DEBUG_CFG2, 0x08 },
+       { WCD939X_RX_TOP_DSD0_DEBUG_CFG3, 0x00 },
+       { WCD939X_RX_TOP_DSD0_DEBUG_CFG4, 0x00 },
+       { WCD939X_RX_TOP_DSD1_DEBUG_CFG1, 0x03 },
+       { WCD939X_RX_TOP_DSD1_DEBUG_CFG2, 0x08 },
+       { WCD939X_RX_TOP_DSD1_DEBUG_CFG3, 0x00 },
+       { WCD939X_RX_TOP_DSD1_DEBUG_CFG4, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_CFG0, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_CFG1, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_CFG0, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_CFG1, 0x00 },
+       { WCD939X_RX_TOP_PATH_CFG2, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_SEC0, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_SEC1, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_SEC2, 0x00 },
+       { WCD939X_RX_TOP_HPHL_PATH_SEC3, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_SEC0, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_SEC1, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_SEC2, 0x00 },
+       { WCD939X_RX_TOP_HPHR_PATH_SEC3, 0x00 },
+       { WCD939X_RX_TOP_PATH_SEC4, 0x00 },
+       { WCD939X_RX_TOP_PATH_SEC5, 0x00 },
+       { WCD939X_COMPANDER_HPHL_CTL0, 0x60 },
+       { WCD939X_COMPANDER_HPHL_CTL1, 0xdb },
+       { WCD939X_COMPANDER_HPHL_CTL2, 0xff },
+       { WCD939X_COMPANDER_HPHL_CTL3, 0x35 },
+       { WCD939X_COMPANDER_HPHL_CTL4, 0xff },
+       { WCD939X_COMPANDER_HPHL_CTL5, 0x00 },
+       { WCD939X_COMPANDER_HPHL_CTL7, 0x08 },
+       { WCD939X_COMPANDER_HPHL_CTL8, 0x00 },
+       { WCD939X_COMPANDER_HPHL_CTL9, 0x00 },
+       { WCD939X_COMPANDER_HPHL_CTL10, 0x06 },
+       { WCD939X_COMPANDER_HPHL_CTL11, 0x12 },
+       { WCD939X_COMPANDER_HPHL_CTL12, 0x1e },
+       { WCD939X_COMPANDER_HPHL_CTL13, 0x2a },
+       { WCD939X_COMPANDER_HPHL_CTL14, 0x36 },
+       { WCD939X_COMPANDER_HPHL_CTL15, 0x3c },
+       { WCD939X_COMPANDER_HPHL_CTL16, 0xc4 },
+       { WCD939X_COMPANDER_HPHL_CTL17, 0x00 },
+       { WCD939X_COMPANDER_HPHL_CTL18, 0x0c },
+       { WCD939X_COMPANDER_HPHL_CTL19, 0x16 },
+       { WCD939X_R_CTL0, 0x60 },
+       { WCD939X_R_CTL1, 0xdb },
+       { WCD939X_R_CTL2, 0xff },
+       { WCD939X_R_CTL3, 0x35 },
+       { WCD939X_R_CTL4, 0xff },
+       { WCD939X_R_CTL5, 0x00 },
+       { WCD939X_R_CTL7, 0x08 },
+       { WCD939X_R_CTL8, 0x00 },
+       { WCD939X_R_CTL9, 0x00 },
+       { WCD939X_R_CTL10, 0x06 },
+       { WCD939X_R_CTL11, 0x12 },
+       { WCD939X_R_CTL12, 0x1e },
+       { WCD939X_R_CTL13, 0x2a },
+       { WCD939X_R_CTL14, 0x36 },
+       { WCD939X_R_CTL15, 0x3c },
+       { WCD939X_R_CTL16, 0xc4 },
+       { WCD939X_R_CTL17, 0x00 },
+       { WCD939X_R_CTL18, 0x0c },
+       { WCD939X_R_CTL19, 0x16 },
+       { WCD939X_E_PATH_CTL, 0x00 },
+       { WCD939X_E_CFG0, 0x07 },
+       { WCD939X_E_CFG1, 0x3c },
+       { WCD939X_E_CFG2, 0x00 },
+       { WCD939X_E_CFG3, 0x00 },
+       { WCD939X_DSD_HPHL_PATH_CTL, 0x00 },
+       { WCD939X_DSD_HPHL_CFG0, 0x00 },
+       { WCD939X_DSD_HPHL_CFG1, 0x00 },
+       { WCD939X_DSD_HPHL_CFG2, 0x22 },
+       { WCD939X_DSD_HPHL_CFG3, 0x00 },
+       { WCD939X_DSD_HPHL_CFG4, 0x1a },
+       { WCD939X_DSD_HPHL_CFG5, 0x00 },
+       { WCD939X_DSD_HPHR_PATH_CTL, 0x00 },
+       { WCD939X_DSD_HPHR_CFG0, 0x00 },
+       { WCD939X_DSD_HPHR_CFG1, 0x00 },
+       { WCD939X_DSD_HPHR_CFG2, 0x22 },
+       { WCD939X_DSD_HPHR_CFG3, 0x00 },
+       { WCD939X_DSD_HPHR_CFG4, 0x1a },
+       { WCD939X_DSD_HPHR_CFG5, 0x00 },
+};
+
+static bool wcd939x_rdwr_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case WCD939X_ANA_PAGE:
+       case WCD939X_ANA_BIAS:
+       case WCD939X_ANA_RX_SUPPLIES:
+       case WCD939X_ANA_HPH:
+       case WCD939X_ANA_EAR:
+       case WCD939X_ANA_EAR_COMPANDER_CTL:
+       case WCD939X_ANA_TX_CH1:
+       case WCD939X_ANA_TX_CH2:
+       case WCD939X_ANA_TX_CH3:
+       case WCD939X_ANA_TX_CH4:
+       case WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC:
+       case WCD939X_ANA_MICB3_DSP_EN_LOGIC:
+       case WCD939X_ANA_MBHC_MECH:
+       case WCD939X_ANA_MBHC_ELECT:
+       case WCD939X_ANA_MBHC_ZDET:
+       case WCD939X_ANA_MBHC_BTN0:
+       case WCD939X_ANA_MBHC_BTN1:
+       case WCD939X_ANA_MBHC_BTN2:
+       case WCD939X_ANA_MBHC_BTN3:
+       case WCD939X_ANA_MBHC_BTN4:
+       case WCD939X_ANA_MBHC_BTN5:
+       case WCD939X_ANA_MBHC_BTN6:
+       case WCD939X_ANA_MBHC_BTN7:
+       case WCD939X_ANA_MICB1:
+       case WCD939X_ANA_MICB2:
+       case WCD939X_ANA_MICB2_RAMP:
+       case WCD939X_ANA_MICB3:
+       case WCD939X_ANA_MICB4:
+       case WCD939X_BIAS_CTL:
+       case WCD939X_BIAS_VBG_FINE_ADJ:
+       case WCD939X_LDOL_VDDCX_ADJUST:
+       case WCD939X_LDOL_DISABLE_LDOL:
+       case WCD939X_MBHC_CTL_CLK:
+       case WCD939X_MBHC_CTL_ANA:
+       case WCD939X_MBHC_ZDET_VNEG_CTL:
+       case WCD939X_MBHC_ZDET_BIAS_CTL:
+       case WCD939X_MBHC_CTL_BCS:
+       case WCD939X_MBHC_TEST_CTL:
+       case WCD939X_LDOH_MODE:
+       case WCD939X_LDOH_BIAS:
+       case WCD939X_LDOH_STB_LOADS:
+       case WCD939X_LDOH_SLOWRAMP:
+       case WCD939X_MICB1_TEST_CTL_1:
+       case WCD939X_MICB1_TEST_CTL_2:
+       case WCD939X_MICB1_TEST_CTL_3:
+       case WCD939X_MICB2_TEST_CTL_1:
+       case WCD939X_MICB2_TEST_CTL_2:
+       case WCD939X_MICB2_TEST_CTL_3:
+       case WCD939X_MICB3_TEST_CTL_1:
+       case WCD939X_MICB3_TEST_CTL_2:
+       case WCD939X_MICB3_TEST_CTL_3:
+       case WCD939X_MICB4_TEST_CTL_1:
+       case WCD939X_MICB4_TEST_CTL_2:
+       case WCD939X_MICB4_TEST_CTL_3:
+       case WCD939X_TX_COM_ADC_VCM:
+       case WCD939X_TX_COM_BIAS_ATEST:
+       case WCD939X_TX_COM_SPARE1:
+       case WCD939X_TX_COM_SPARE2:
+       case WCD939X_TX_COM_TXFE_DIV_CTL:
+       case WCD939X_TX_COM_TXFE_DIV_START:
+       case WCD939X_TX_COM_SPARE3:
+       case WCD939X_TX_COM_SPARE4:
+       case WCD939X_TX_1_2_TEST_EN:
+       case WCD939X_TX_1_2_ADC_IB:
+       case WCD939X_TX_1_2_ATEST_REFCTL:
+       case WCD939X_TX_1_2_TEST_CTL:
+       case WCD939X_TX_1_2_TEST_BLK_EN1:
+       case WCD939X_TX_1_2_TXFE1_CLKDIV:
+       case WCD939X_TX_3_4_TEST_EN:
+       case WCD939X_TX_3_4_ADC_IB:
+       case WCD939X_TX_3_4_ATEST_REFCTL:
+       case WCD939X_TX_3_4_TEST_CTL:
+       case WCD939X_TX_3_4_TEST_BLK_EN3:
+       case WCD939X_TX_3_4_TXFE3_CLKDIV:
+       case WCD939X_TX_3_4_TEST_BLK_EN2:
+       case WCD939X_TX_3_4_TXFE2_CLKDIV:
+       case WCD939X_TX_3_4_SPARE1:
+       case WCD939X_TX_3_4_TEST_BLK_EN4:
+       case WCD939X_TX_3_4_TXFE4_CLKDIV:
+       case WCD939X_TX_3_4_SPARE2:
+       case WCD939X_CLASSH_MODE_1:
+       case WCD939X_CLASSH_MODE_2:
+       case WCD939X_CLASSH_MODE_3:
+       case WCD939X_CLASSH_CTRL_VCL_1:
+       case WCD939X_CLASSH_CTRL_VCL_2:
+       case WCD939X_CLASSH_CTRL_CCL_1:
+       case WCD939X_CLASSH_CTRL_CCL_2:
+       case WCD939X_CLASSH_CTRL_CCL_3:
+       case WCD939X_CLASSH_CTRL_CCL_4:
+       case WCD939X_CLASSH_CTRL_CCL_5:
+       case WCD939X_CLASSH_BUCK_TMUX_A_D:
+       case WCD939X_CLASSH_BUCK_SW_DRV_CNTL:
+       case WCD939X_CLASSH_SPARE:
+       case WCD939X_FLYBACK_EN:
+       case WCD939X_FLYBACK_VNEG_CTRL_1:
+       case WCD939X_FLYBACK_VNEG_CTRL_2:
+       case WCD939X_FLYBACK_VNEG_CTRL_3:
+       case WCD939X_FLYBACK_VNEG_CTRL_4:
+       case WCD939X_FLYBACK_VNEG_CTRL_5:
+       case WCD939X_FLYBACK_VNEG_CTRL_6:
+       case WCD939X_FLYBACK_VNEG_CTRL_7:
+       case WCD939X_FLYBACK_VNEG_CTRL_8:
+       case WCD939X_FLYBACK_VNEG_CTRL_9:
+       case WCD939X_FLYBACK_VNEGDAC_CTRL_1:
+       case WCD939X_FLYBACK_VNEGDAC_CTRL_2:
+       case WCD939X_FLYBACK_VNEGDAC_CTRL_3:
+       case WCD939X_FLYBACK_CTRL_1:
+       case WCD939X_FLYBACK_TEST_CTL:
+       case WCD939X_RX_AUX_SW_CTL:
+       case WCD939X_RX_PA_AUX_IN_CONN:
+       case WCD939X_RX_TIMER_DIV:
+       case WCD939X_RX_OCP_CTL:
+       case WCD939X_RX_OCP_COUNT:
+       case WCD939X_RX_BIAS_EAR_DAC:
+       case WCD939X_RX_BIAS_EAR_AMP:
+       case WCD939X_RX_BIAS_HPH_LDO:
+       case WCD939X_RX_BIAS_HPH_PA:
+       case WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2:
+       case WCD939X_RX_BIAS_HPH_RDAC_LDO:
+       case WCD939X_RX_BIAS_HPH_CNP1:
+       case WCD939X_RX_BIAS_HPH_LOWPOWER:
+       case WCD939X_RX_BIAS_AUX_DAC:
+       case WCD939X_RX_BIAS_AUX_AMP:
+       case WCD939X_RX_BIAS_VNEGDAC_BLEEDER:
+       case WCD939X_RX_BIAS_MISC:
+       case WCD939X_RX_BIAS_BUCK_RST:
+       case WCD939X_RX_BIAS_BUCK_VREF_ERRAMP:
+       case WCD939X_RX_BIAS_FLYB_ERRAMP:
+       case WCD939X_RX_BIAS_FLYB_BUFF:
+       case WCD939X_RX_BIAS_FLYB_MID_RST:
+       case WCD939X_HPH_CNP_EN:
+       case WCD939X_HPH_CNP_WG_CTL:
+       case WCD939X_HPH_CNP_WG_TIME:
+       case WCD939X_HPH_OCP_CTL:
+       case WCD939X_HPH_AUTO_CHOP:
+       case WCD939X_HPH_CHOP_CTL:
+       case WCD939X_HPH_PA_CTL1:
+       case WCD939X_HPH_PA_CTL2:
+       case WCD939X_HPH_L_EN:
+       case WCD939X_HPH_L_TEST:
+       case WCD939X_HPH_L_ATEST:
+       case WCD939X_HPH_R_EN:
+       case WCD939X_HPH_R_TEST:
+       case WCD939X_HPH_R_ATEST:
+       case WCD939X_HPH_RDAC_CLK_CTL1:
+       case WCD939X_HPH_RDAC_CLK_CTL2:
+       case WCD939X_HPH_RDAC_LDO_CTL:
+       case WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL:
+       case WCD939X_HPH_REFBUFF_UHQA_CTL:
+       case WCD939X_HPH_REFBUFF_LP_CTL:
+       case WCD939X_HPH_L_DAC_CTL:
+       case WCD939X_HPH_R_DAC_CTL:
+       case WCD939X_HPH_SURGE_COMP_SEL:
+       case WCD939X_HPH_SURGE_EN:
+       case WCD939X_HPH_SURGE_MISC1:
+       case WCD939X_EAR_EN:
+       case WCD939X_EAR_PA_CON:
+       case WCD939X_EAR_SP_CON:
+       case WCD939X_EAR_DAC_CON:
+       case WCD939X_EAR_CNP_FSM_CON:
+       case WCD939X_EAR_TEST_CTL:
+       case WCD939X_FLYBACK_NEW_CTRL_2:
+       case WCD939X_FLYBACK_NEW_CTRL_3:
+       case WCD939X_FLYBACK_NEW_CTRL_4:
+       case WCD939X_ANA_NEW_PAGE:
+       case WCD939X_HPH_NEW_ANA_HPH2:
+       case WCD939X_HPH_NEW_ANA_HPH3:
+       case WCD939X_SLEEP_CTL:
+       case WCD939X_SLEEP_WATCHDOG_CTL:
+       case WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL:
+       case WCD939X_MBHC_NEW_CTL_1:
+       case WCD939X_MBHC_NEW_CTL_2:
+       case WCD939X_MBHC_NEW_PLUG_DETECT_CTL:
+       case WCD939X_MBHC_NEW_ZDET_ANA_CTL:
+       case WCD939X_MBHC_NEW_ZDET_RAMP_CTL:
+       case WCD939X_TX_NEW_CH12_MUX:
+       case WCD939X_TX_NEW_CH34_MUX:
+       case WCD939X_DIE_CRACK_DET_EN:
+       case WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL:
+       case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L:
+       case WCD939X_HPH_NEW_INT_RDAC_VREF_CTL:
+       case WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL:
+       case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R:
+       case WCD939X_HPH_NEW_INT_PA_MISC1:
+       case WCD939X_HPH_NEW_INT_PA_MISC2:
+       case WCD939X_HPH_NEW_INT_PA_RDAC_MISC:
+       case WCD939X_HPH_NEW_INT_TIMER1:
+       case WCD939X_HPH_NEW_INT_TIMER2:
+       case WCD939X_HPH_NEW_INT_TIMER3:
+       case WCD939X_HPH_NEW_INT_TIMER4:
+       case WCD939X_HPH_NEW_INT_PA_RDAC_MISC2:
+       case WCD939X_HPH_NEW_INT_PA_RDAC_MISC3:
+       case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L:
+       case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R:
+       case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI:
+       case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP:
+       case WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP:
+       case WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL:
+       case WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL:
+       case WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT:
+       case WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW:
+       case WCD939X_EAR_INT_NEW_CHOPPER_CON:
+       case WCD939X_EAR_INT_NEW_CNP_VCM_CON1:
+       case WCD939X_EAR_INT_NEW_CNP_VCM_CON2:
+       case WCD939X_EAR_INT_NEW_DYNAMIC_BIAS:
+       case WCD939X_SLEEP_INT_WATCHDOG_CTL_1:
+       case WCD939X_SLEEP_INT_WATCHDOG_CTL_2:
+       case WCD939X_DIE_CRACK_INT_DET_INT1:
+       case WCD939X_DIE_CRACK_INT_DET_INT2:
+       case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2:
+       case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1:
+       case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0:
+       case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M:
+       case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0:
+       case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP:
+       case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1:
+       case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP:
+       case WCD939X_TX_COM_NEW_INT_ADC_INT_L2:
+       case WCD939X_TX_COM_NEW_INT_ADC_INT_L1:
+       case WCD939X_TX_COM_NEW_INT_ADC_INT_L0:
+       case WCD939X_TX_COM_NEW_INT_ADC_INT_ULP:
+       case WCD939X_DIGITAL_PAGE:
+       case WCD939X_DIGITAL_SWR_TX_CLK_RATE:
+       case WCD939X_DIGITAL_CDC_RST_CTL:
+       case WCD939X_DIGITAL_TOP_CLK_CFG:
+       case WCD939X_DIGITAL_CDC_ANA_CLK_CTL:
+       case WCD939X_DIGITAL_CDC_DIG_CLK_CTL:
+       case WCD939X_DIGITAL_SWR_RST_EN:
+       case WCD939X_DIGITAL_CDC_PATH_MODE:
+       case WCD939X_DIGITAL_CDC_RX_RST:
+       case WCD939X_DIGITAL_CDC_RX0_CTL:
+       case WCD939X_DIGITAL_CDC_RX1_CTL:
+       case WCD939X_DIGITAL_CDC_RX2_CTL:
+       case WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1:
+       case WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3:
+       case WCD939X_DIGITAL_CDC_COMP_CTL_0:
+       case WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A1_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A1_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A2_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A2_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A3_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A3_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A4_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A4_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A5_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A5_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A6_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_A7_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_C_0:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_C_1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_C_2:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_C_3:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R1:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R2:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R3:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R4:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R5:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R6:
+       case WCD939X_DIGITAL_CDC_HPH_DSM_R7:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A1_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A1_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A2_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A2_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A3_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A3_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A4_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A4_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A5_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A5_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A6_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_A7_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_C_0:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_C_1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_C_2:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_C_3:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R1:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R2:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R3:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R4:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R5:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R6:
+       case WCD939X_DIGITAL_CDC_EAR_DSM_R7:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2:
+       case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0:
+       case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1:
+       case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2:
+       case WCD939X_DIGITAL_CDC_HPH_GAIN_CTL:
+       case WCD939X_DIGITAL_CDC_EAR_GAIN_CTL:
+       case WCD939X_DIGITAL_CDC_EAR_PATH_CTL:
+       case WCD939X_DIGITAL_CDC_SWR_CLH:
+       case WCD939X_DIGITAL_SWR_CLH_BYP:
+       case WCD939X_DIGITAL_CDC_TX0_CTL:
+       case WCD939X_DIGITAL_CDC_TX1_CTL:
+       case WCD939X_DIGITAL_CDC_TX2_CTL:
+       case WCD939X_DIGITAL_CDC_TX_RST:
+       case WCD939X_DIGITAL_CDC_REQ_CTL:
+       case WCD939X_DIGITAL_CDC_RST:
+       case WCD939X_DIGITAL_CDC_AMIC_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC1_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC2_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC3_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC4_CTL:
+       case WCD939X_DIGITAL_EFUSE_PRG_CTL:
+       case WCD939X_DIGITAL_EFUSE_CTL:
+       case WCD939X_DIGITAL_CDC_DMIC_RATE_1_2:
+       case WCD939X_DIGITAL_CDC_DMIC_RATE_3_4:
+       case WCD939X_DIGITAL_PDM_WD_CTL0:
+       case WCD939X_DIGITAL_PDM_WD_CTL1:
+       case WCD939X_DIGITAL_PDM_WD_CTL2:
+       case WCD939X_DIGITAL_INTR_MODE:
+       case WCD939X_DIGITAL_INTR_MASK_0:
+       case WCD939X_DIGITAL_INTR_MASK_1:
+       case WCD939X_DIGITAL_INTR_MASK_2:
+       case WCD939X_DIGITAL_INTR_CLEAR_0:
+       case WCD939X_DIGITAL_INTR_CLEAR_1:
+       case WCD939X_DIGITAL_INTR_CLEAR_2:
+       case WCD939X_DIGITAL_INTR_LEVEL_0:
+       case WCD939X_DIGITAL_INTR_LEVEL_1:
+       case WCD939X_DIGITAL_INTR_LEVEL_2:
+       case WCD939X_DIGITAL_INTR_SET_0:
+       case WCD939X_DIGITAL_INTR_SET_1:
+       case WCD939X_DIGITAL_INTR_SET_2:
+       case WCD939X_DIGITAL_INTR_TEST_0:
+       case WCD939X_DIGITAL_INTR_TEST_1:
+       case WCD939X_DIGITAL_INTR_TEST_2:
+       case WCD939X_DIGITAL_TX_MODE_DBG_EN:
+       case WCD939X_DIGITAL_TX_MODE_DBG_0_1:
+       case WCD939X_DIGITAL_TX_MODE_DBG_2_3:
+       case WCD939X_DIGITAL_LB_IN_SEL_CTL:
+       case WCD939X_DIGITAL_LOOP_BACK_MODE:
+       case WCD939X_DIGITAL_SWR_DAC_TEST:
+       case WCD939X_DIGITAL_SWR_HM_TEST_RX_0:
+       case WCD939X_DIGITAL_SWR_HM_TEST_TX_0:
+       case WCD939X_DIGITAL_SWR_HM_TEST_RX_1:
+       case WCD939X_DIGITAL_SWR_HM_TEST_TX_1:
+       case WCD939X_DIGITAL_SWR_HM_TEST_TX_2:
+       case WCD939X_DIGITAL_PAD_CTL_SWR_0:
+       case WCD939X_DIGITAL_PAD_CTL_SWR_1:
+       case WCD939X_DIGITAL_I2C_CTL:
+       case WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE:
+       case WCD939X_DIGITAL_EFUSE_TEST_CTL_0:
+       case WCD939X_DIGITAL_EFUSE_TEST_CTL_1:
+       case WCD939X_DIGITAL_PAD_CTL_PDM_RX0:
+       case WCD939X_DIGITAL_PAD_CTL_PDM_RX1:
+       case WCD939X_DIGITAL_PAD_CTL_PDM_TX0:
+       case WCD939X_DIGITAL_PAD_CTL_PDM_TX1:
+       case WCD939X_DIGITAL_PAD_CTL_PDM_TX2:
+       case WCD939X_DIGITAL_PAD_INP_DIS_0:
+       case WCD939X_DIGITAL_PAD_INP_DIS_1:
+       case WCD939X_DIGITAL_DRIVE_STRENGTH_0:
+       case WCD939X_DIGITAL_DRIVE_STRENGTH_1:
+       case WCD939X_DIGITAL_DRIVE_STRENGTH_2:
+       case WCD939X_DIGITAL_RX_DATA_EDGE_CTL:
+       case WCD939X_DIGITAL_TX_DATA_EDGE_CTL:
+       case WCD939X_DIGITAL_GPIO_MODE:
+       case WCD939X_DIGITAL_PIN_CTL_OE:
+       case WCD939X_DIGITAL_PIN_CTL_DATA_0:
+       case WCD939X_DIGITAL_PIN_CTL_DATA_1:
+       case WCD939X_DIGITAL_DIG_DEBUG_CTL:
+       case WCD939X_DIGITAL_DIG_DEBUG_EN:
+       case WCD939X_DIGITAL_ANA_CSR_DBG_ADD:
+       case WCD939X_DIGITAL_ANA_CSR_DBG_CTL:
+       case WCD939X_DIGITAL_SSP_DBG:
+       case WCD939X_DIGITAL_SPARE_0:
+       case WCD939X_DIGITAL_SPARE_1:
+       case WCD939X_DIGITAL_SPARE_2:
+       case WCD939X_DIGITAL_TX_REQ_FB_CTL_0:
+       case WCD939X_DIGITAL_TX_REQ_FB_CTL_1:
+       case WCD939X_DIGITAL_TX_REQ_FB_CTL_2:
+       case WCD939X_DIGITAL_TX_REQ_FB_CTL_3:
+       case WCD939X_DIGITAL_TX_REQ_FB_CTL_4:
+       case WCD939X_DIGITAL_DEM_BYPASS_DATA0:
+       case WCD939X_DIGITAL_DEM_BYPASS_DATA1:
+       case WCD939X_DIGITAL_DEM_BYPASS_DATA2:
+       case WCD939X_DIGITAL_DEM_BYPASS_DATA3:
+       case WCD939X_DIGITAL_DEM_SECOND_ORDER:
+       case WCD939X_DIGITAL_DSM_CTRL:
+       case WCD939X_DIGITAL_DSM_0_STATIC_DATA_0:
+       case WCD939X_DIGITAL_DSM_0_STATIC_DATA_1:
+       case WCD939X_DIGITAL_DSM_0_STATIC_DATA_2:
+       case WCD939X_DIGITAL_DSM_0_STATIC_DATA_3:
+       case WCD939X_DIGITAL_DSM_1_STATIC_DATA_0:
+       case WCD939X_DIGITAL_DSM_1_STATIC_DATA_1:
+       case WCD939X_DIGITAL_DSM_1_STATIC_DATA_2:
+       case WCD939X_DIGITAL_DSM_1_STATIC_DATA_3:
+       case WCD939X_RX_TOP_PAGE:
+       case WCD939X_RX_TOP_TOP_CFG0:
+       case WCD939X_RX_TOP_HPHL_COMP_WR_LSB:
+       case WCD939X_RX_TOP_HPHL_COMP_WR_MSB:
+       case WCD939X_RX_TOP_HPHL_COMP_LUT:
+       case WCD939X_RX_TOP_HPHR_COMP_WR_LSB:
+       case WCD939X_RX_TOP_HPHR_COMP_WR_MSB:
+       case WCD939X_RX_TOP_HPHR_COMP_LUT:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG1:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG2:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG3:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG4:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG1:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG2:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG3:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG4:
+       case WCD939X_RX_TOP_HPHL_PATH_CFG0:
+       case WCD939X_RX_TOP_HPHL_PATH_CFG1:
+       case WCD939X_RX_TOP_HPHR_PATH_CFG0:
+       case WCD939X_RX_TOP_HPHR_PATH_CFG1:
+       case WCD939X_RX_TOP_PATH_CFG2:
+       case WCD939X_RX_TOP_HPHL_PATH_SEC0:
+       case WCD939X_RX_TOP_HPHL_PATH_SEC1:
+       case WCD939X_RX_TOP_HPHL_PATH_SEC2:
+       case WCD939X_RX_TOP_HPHL_PATH_SEC3:
+       case WCD939X_RX_TOP_HPHR_PATH_SEC0:
+       case WCD939X_RX_TOP_HPHR_PATH_SEC1:
+       case WCD939X_RX_TOP_HPHR_PATH_SEC2:
+       case WCD939X_RX_TOP_HPHR_PATH_SEC3:
+       case WCD939X_RX_TOP_PATH_SEC4:
+       case WCD939X_RX_TOP_PATH_SEC5:
+       case WCD939X_COMPANDER_HPHL_CTL0:
+       case WCD939X_COMPANDER_HPHL_CTL1:
+       case WCD939X_COMPANDER_HPHL_CTL2:
+       case WCD939X_COMPANDER_HPHL_CTL3:
+       case WCD939X_COMPANDER_HPHL_CTL4:
+       case WCD939X_COMPANDER_HPHL_CTL5:
+       case WCD939X_COMPANDER_HPHL_CTL7:
+       case WCD939X_COMPANDER_HPHL_CTL8:
+       case WCD939X_COMPANDER_HPHL_CTL9:
+       case WCD939X_COMPANDER_HPHL_CTL10:
+       case WCD939X_COMPANDER_HPHL_CTL11:
+       case WCD939X_COMPANDER_HPHL_CTL12:
+       case WCD939X_COMPANDER_HPHL_CTL13:
+       case WCD939X_COMPANDER_HPHL_CTL14:
+       case WCD939X_COMPANDER_HPHL_CTL15:
+       case WCD939X_COMPANDER_HPHL_CTL16:
+       case WCD939X_COMPANDER_HPHL_CTL17:
+       case WCD939X_COMPANDER_HPHL_CTL18:
+       case WCD939X_COMPANDER_HPHL_CTL19:
+       case WCD939X_R_CTL0:
+       case WCD939X_R_CTL1:
+       case WCD939X_R_CTL2:
+       case WCD939X_R_CTL3:
+       case WCD939X_R_CTL4:
+       case WCD939X_R_CTL5:
+       case WCD939X_R_CTL7:
+       case WCD939X_R_CTL8:
+       case WCD939X_R_CTL9:
+       case WCD939X_R_CTL10:
+       case WCD939X_R_CTL11:
+       case WCD939X_R_CTL12:
+       case WCD939X_R_CTL13:
+       case WCD939X_R_CTL14:
+       case WCD939X_R_CTL15:
+       case WCD939X_R_CTL16:
+       case WCD939X_R_CTL17:
+       case WCD939X_R_CTL18:
+       case WCD939X_R_CTL19:
+       case WCD939X_E_PATH_CTL:
+       case WCD939X_E_CFG0:
+       case WCD939X_E_CFG1:
+       case WCD939X_E_CFG2:
+       case WCD939X_E_CFG3:
+       case WCD939X_DSD_HPHL_PATH_CTL:
+       case WCD939X_DSD_HPHL_CFG0:
+       case WCD939X_DSD_HPHL_CFG1:
+       case WCD939X_DSD_HPHL_CFG2:
+       case WCD939X_DSD_HPHL_CFG3:
+       case WCD939X_DSD_HPHL_CFG4:
+       case WCD939X_DSD_HPHL_CFG5:
+       case WCD939X_DSD_HPHR_PATH_CTL:
+       case WCD939X_DSD_HPHR_CFG0:
+       case WCD939X_DSD_HPHR_CFG1:
+       case WCD939X_DSD_HPHR_CFG2:
+       case WCD939X_DSD_HPHR_CFG3:
+       case WCD939X_DSD_HPHR_CFG4:
+       case WCD939X_DSD_HPHR_CFG5:
+               return true;
+       }
+
+       return false;
+}
+
+static bool wcd939x_readable_register(struct device *dev, unsigned int reg)
+{
+       /* Read-Only Registers */
+       switch (reg) {
+       case WCD939X_ANA_MBHC_RESULT_1:
+       case WCD939X_ANA_MBHC_RESULT_2:
+       case WCD939X_ANA_MBHC_RESULT_3:
+       case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS:
+       case WCD939X_TX_1_2_SAR2_ERR:
+       case WCD939X_TX_1_2_SAR1_ERR:
+       case WCD939X_TX_3_4_SAR4_ERR:
+       case WCD939X_TX_3_4_SAR3_ERR:
+       case WCD939X_HPH_L_STATUS:
+       case WCD939X_HPH_R_STATUS:
+       case WCD939X_HPH_SURGE_STATUS:
+       case WCD939X_EAR_STATUS_REG_1:
+       case WCD939X_EAR_STATUS_REG_2:
+       case WCD939X_MBHC_NEW_FSM_STATUS:
+       case WCD939X_MBHC_NEW_ADC_RESULT:
+       case WCD939X_DIE_CRACK_DET_OUT:
+       case WCD939X_DIGITAL_CHIP_ID0:
+       case WCD939X_DIGITAL_CHIP_ID1:
+       case WCD939X_DIGITAL_CHIP_ID2:
+       case WCD939X_DIGITAL_CHIP_ID3:
+       case WCD939X_DIGITAL_INTR_STATUS_0:
+       case WCD939X_DIGITAL_INTR_STATUS_1:
+       case WCD939X_DIGITAL_INTR_STATUS_2:
+       case WCD939X_DIGITAL_SWR_HM_TEST_0:
+       case WCD939X_DIGITAL_SWR_HM_TEST_1:
+       case WCD939X_DIGITAL_EFUSE_T_DATA_0:
+       case WCD939X_DIGITAL_EFUSE_T_DATA_1:
+       case WCD939X_DIGITAL_PIN_STATUS_0:
+       case WCD939X_DIGITAL_PIN_STATUS_1:
+       case WCD939X_DIGITAL_MODE_STATUS_0:
+       case WCD939X_DIGITAL_MODE_STATUS_1:
+       case WCD939X_DIGITAL_EFUSE_REG_0:
+       case WCD939X_DIGITAL_EFUSE_REG_1:
+       case WCD939X_DIGITAL_EFUSE_REG_2:
+       case WCD939X_DIGITAL_EFUSE_REG_3:
+       case WCD939X_DIGITAL_EFUSE_REG_4:
+       case WCD939X_DIGITAL_EFUSE_REG_5:
+       case WCD939X_DIGITAL_EFUSE_REG_6:
+       case WCD939X_DIGITAL_EFUSE_REG_7:
+       case WCD939X_DIGITAL_EFUSE_REG_8:
+       case WCD939X_DIGITAL_EFUSE_REG_9:
+       case WCD939X_DIGITAL_EFUSE_REG_10:
+       case WCD939X_DIGITAL_EFUSE_REG_11:
+       case WCD939X_DIGITAL_EFUSE_REG_12:
+       case WCD939X_DIGITAL_EFUSE_REG_13:
+       case WCD939X_DIGITAL_EFUSE_REG_14:
+       case WCD939X_DIGITAL_EFUSE_REG_15:
+       case WCD939X_DIGITAL_EFUSE_REG_16:
+       case WCD939X_DIGITAL_EFUSE_REG_17:
+       case WCD939X_DIGITAL_EFUSE_REG_18:
+       case WCD939X_DIGITAL_EFUSE_REG_19:
+       case WCD939X_DIGITAL_EFUSE_REG_20:
+       case WCD939X_DIGITAL_EFUSE_REG_21:
+       case WCD939X_DIGITAL_EFUSE_REG_22:
+       case WCD939X_DIGITAL_EFUSE_REG_23:
+       case WCD939X_DIGITAL_EFUSE_REG_24:
+       case WCD939X_DIGITAL_EFUSE_REG_25:
+       case WCD939X_DIGITAL_EFUSE_REG_26:
+       case WCD939X_DIGITAL_EFUSE_REG_27:
+       case WCD939X_DIGITAL_EFUSE_REG_28:
+       case WCD939X_DIGITAL_EFUSE_REG_29:
+       case WCD939X_DIGITAL_EFUSE_REG_30:
+       case WCD939X_DIGITAL_EFUSE_REG_31:
+       case WCD939X_RX_TOP_HPHL_COMP_RD_LSB:
+       case WCD939X_RX_TOP_HPHL_COMP_RD_MSB:
+       case WCD939X_RX_TOP_HPHR_COMP_RD_LSB:
+       case WCD939X_RX_TOP_HPHR_COMP_RD_MSB:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG5:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG6:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG5:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG6:
+       case WCD939X_COMPANDER_HPHL_CTL6:
+       case WCD939X_R_CTL6:
+               return true;
+       }
+
+       return wcd939x_rdwr_register(dev, reg);
+}
+
+static bool wcd939x_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case WCD939X_ANA_MBHC_RESULT_1:
+       case WCD939X_ANA_MBHC_RESULT_2:
+       case WCD939X_ANA_MBHC_RESULT_3:
+       case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS:
+       case WCD939X_TX_1_2_SAR2_ERR:
+       case WCD939X_TX_1_2_SAR1_ERR:
+       case WCD939X_TX_3_4_SAR4_ERR:
+       case WCD939X_TX_3_4_SAR3_ERR:
+       case WCD939X_HPH_L_STATUS:
+       case WCD939X_HPH_R_STATUS:
+       case WCD939X_HPH_SURGE_STATUS:
+       case WCD939X_EAR_STATUS_REG_1:
+       case WCD939X_EAR_STATUS_REG_2:
+       case WCD939X_MBHC_NEW_FSM_STATUS:
+       case WCD939X_MBHC_NEW_ADC_RESULT:
+       case WCD939X_DIE_CRACK_DET_OUT:
+       case WCD939X_DIGITAL_INTR_STATUS_0:
+       case WCD939X_DIGITAL_INTR_STATUS_1:
+       case WCD939X_DIGITAL_INTR_STATUS_2:
+       case WCD939X_DIGITAL_SWR_HM_TEST_0:
+       case WCD939X_DIGITAL_SWR_HM_TEST_1:
+       case WCD939X_DIGITAL_PIN_STATUS_0:
+       case WCD939X_DIGITAL_PIN_STATUS_1:
+       case WCD939X_DIGITAL_MODE_STATUS_0:
+       case WCD939X_DIGITAL_MODE_STATUS_1:
+       case WCD939X_RX_TOP_HPHL_COMP_RD_LSB:
+       case WCD939X_RX_TOP_HPHL_COMP_RD_MSB:
+       case WCD939X_RX_TOP_HPHR_COMP_RD_LSB:
+       case WCD939X_RX_TOP_HPHR_COMP_RD_MSB:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG5:
+       case WCD939X_RX_TOP_DSD0_DEBUG_CFG6:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG5:
+       case WCD939X_RX_TOP_DSD1_DEBUG_CFG6:
+       case WCD939X_COMPANDER_HPHL_CTL6:
+       case WCD939X_R_CTL6:
+               return true;
+       }
+       return false;
+}
+
+static bool wcd939x_writeable_register(struct device *dev, unsigned int reg)
+{
+       return wcd939x_rdwr_register(dev, reg);
+}
+
+static const struct regmap_config wcd939x_regmap_config = {
+       .name = "wcd939x_csr",
+       .reg_bits = 32,
+       .val_bits = 8,
+       .cache_type = REGCACHE_MAPLE,
+       .reg_defaults = wcd939x_defaults,
+       .num_reg_defaults = ARRAY_SIZE(wcd939x_defaults),
+       .max_register = WCD939X_MAX_REGISTER,
+       .readable_reg = wcd939x_readable_register,
+       .writeable_reg = wcd939x_writeable_register,
+       .volatile_reg = wcd939x_volatile_register,
+};
+
+static const struct sdw_slave_ops wcd9390_slave_ops = {
+       .update_status = wcd9390_update_status,
+       .interrupt_callback = wcd9390_interrupt_callback,
+       .bus_config = wcd9390_bus_config,
+};
+
+static int wcd939x_sdw_component_bind(struct device *dev, struct device *master,
+                                     void *data)
+{
+       pm_runtime_set_autosuspend_delay(dev, 3000);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       return 0;
+}
+
+static void wcd939x_sdw_component_unbind(struct device *dev,
+                                        struct device *master, void *data)
+{
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct component_ops wcd939x_sdw_component_ops = {
+       .bind = wcd939x_sdw_component_bind,
+       .unbind = wcd939x_sdw_component_unbind,
+};
+
+static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+       struct device *dev = &pdev->dev;
+       struct wcd939x_sdw_priv *wcd;
+       int ret;
+
+       wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+       if (!wcd)
+               return -ENOMEM;
+
+       /*
+        * Port map index starts with 0, however the data port for this codec
+        * are from index 1
+        */
+       if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) {
+               wcd->is_tx = true;
+               ret = of_property_read_u32_array(dev->of_node,
+                                                "qcom,tx-port-mapping",
+                                                &pdev->m_port_map[1],
+                                                WCD939X_MAX_TX_SWR_PORTS);
+       } else {
+               ret = of_property_read_u32_array(dev->of_node,
+                                                "qcom,rx-port-mapping",
+                                                &pdev->m_port_map[1],
+                                                WCD939X_MAX_RX_SWR_PORTS);
+       }
+
+       if (ret < 0)
+               dev_info(dev, "Static Port mapping not specified\n");
+
+       wcd->sdev = pdev;
+       dev_set_drvdata(dev, wcd);
+
+       pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+                                  SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+       pdev->prop.lane_control_support = true;
+       pdev->prop.simple_clk_stop_capable = true;
+       if (wcd->is_tx) {
+               pdev->prop.source_ports = GENMASK(WCD939X_MAX_TX_SWR_PORTS, 0);
+               pdev->prop.src_dpn_prop = wcd939x_tx_dpn_prop;
+               wcd->ch_info = &wcd939x_sdw_tx_ch_info[0];
+               pdev->prop.wake_capable = true;
+       } else {
+               pdev->prop.sink_ports = GENMASK(WCD939X_MAX_RX_SWR_PORTS, 0);
+               pdev->prop.sink_dpn_prop = wcd939x_rx_dpn_prop;
+               wcd->ch_info = &wcd939x_sdw_rx_ch_info[0];
+       }
+
+       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);
+               if (IS_ERR(wcd->regmap))
+                       return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+                                            "Regmap init failed\n");
+
+               /* Start in cache-only until device is enumerated */
+               regcache_cache_only(wcd->regmap, true);
+       }
+
+       ret = component_add(dev, &wcd939x_sdw_component_ops);
+       if (ret)
+               return ret;
+
+       /* Set suspended until aggregate device is bind */
+       pm_runtime_set_suspended(dev);
+
+       return 0;
+}
+
+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, &wcd939x_sdw_component_ops);
+
+       if (wcd->regmap)
+               regmap_exit(wcd->regmap);
+
+       return 0;
+}
+
+static const struct sdw_device_id wcd9390_slave_id[] = {
+       SDW_SLAVE_ENTRY(0x0217, 0x10e, 0), /* WCD9390 & WCD9390 RX/TX Device ID */
+       {},
+};
+MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id);
+
+static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev)
+{
+       struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+       if (wcd->regmap) {
+               regcache_cache_only(wcd->regmap, true);
+               regcache_mark_dirty(wcd->regmap);
+       }
+
+       return 0;
+}
+
+static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev)
+{
+       struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+       if (wcd->regmap) {
+               regcache_cache_only(wcd->regmap, false);
+               regcache_sync(wcd->regmap);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops wcd939x_sdw_pm_ops = {
+       SET_RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver wcd9390_codec_driver = {
+       .probe = wcd9390_probe,
+       .remove = wcd9390_remove,
+       .ops = &wcd9390_slave_ops,
+       .id_table = wcd9390_slave_id,
+       .driver = {
+               .name = "wcd9390-codec",
+               .pm = &wcd939x_sdw_pm_ops,
+       }
+};
+module_sdw_driver(wcd9390_codec_driver);
+
+MODULE_DESCRIPTION("WCD939X SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c
new file mode 100644 (file)
index 0000000..c49894a
--- /dev/null
@@ -0,0 +1,3686 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <sound/tlv.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/of.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_altmode.h>
+
+#include "wcd-clsh-v2.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd939x.h"
+
+#define WCD939X_MAX_MICBIAS            (4)
+#define WCD939X_MAX_SUPPLY             (4)
+#define WCD939X_MBHC_MAX_BUTTONS       (8)
+#define TX_ADC_MAX                     (4)
+#define WCD_MBHC_HS_V_MAX              1600
+
+enum {
+       WCD939X_VERSION_1_0 = 0,
+       WCD939X_VERSION_1_1,
+       WCD939X_VERSION_2_0,
+};
+
+#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+                           SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+                           SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+                           SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+                                SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                        SNDRV_PCM_FMTBIT_S24_LE |\
+                        SNDRV_PCM_FMTBIT_S24_3LE |\
+                        SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define WCD_VOUT_CTL_TO_MICB(v)                (1000 + (v) * 50)
+#define SWR_CLK_RATE_0P6MHZ            (600000)
+#define SWR_CLK_RATE_1P2MHZ            (1200000)
+#define SWR_CLK_RATE_2P4MHZ            (2400000)
+#define SWR_CLK_RATE_4P8MHZ            (4800000)
+#define SWR_CLK_RATE_9P6MHZ            (9600000)
+#define SWR_CLK_RATE_11P2896MHZ                (1128960)
+
+#define ADC_MODE_VAL_HIFI              0x01
+#define ADC_MODE_VAL_LO_HIF            0x02
+#define ADC_MODE_VAL_NORMAL            0x03
+#define ADC_MODE_VAL_LP                        0x05
+#define ADC_MODE_VAL_ULP1              0x09
+#define ADC_MODE_VAL_ULP2              0x0B
+
+/* Z value defined in milliohm */
+#define WCD939X_ZDET_VAL_32            (32000)
+#define WCD939X_ZDET_VAL_400           (400000)
+#define WCD939X_ZDET_VAL_1200          (1200000)
+#define WCD939X_ZDET_VAL_100K          (100000000)
+
+/* Z floating defined in ohms */
+#define WCD939X_ZDET_FLOATING_IMPEDANCE        (0x0FFFFFFE)
+#define WCD939X_ZDET_NUM_MEASUREMENTS  (900)
+#define WCD939X_MBHC_GET_C1(c)         (((c) & 0xC000) >> 14)
+#define WCD939X_MBHC_GET_X1(x)         ((x) & 0x3FFF)
+
+/* Z value compared in milliOhm */
+#define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false
+#define WCD939X_ANA_MBHC_ZDET_CONST    (1018 * 1024)
+
+enum {
+       WCD9390 = 0,
+       WCD9395 = 5,
+};
+
+enum {
+       /* INTR_CTRL_INT_MASK_0 */
+       WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+       WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET,
+       WCD939X_IRQ_MBHC_ELECT_INS_REM_DET,
+       WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+       WCD939X_IRQ_MBHC_SW_DET,
+       WCD939X_IRQ_HPHR_OCP_INT,
+       WCD939X_IRQ_HPHR_CNP_INT,
+       WCD939X_IRQ_HPHL_OCP_INT,
+
+       /* INTR_CTRL_INT_MASK_1 */
+       WCD939X_IRQ_HPHL_CNP_INT,
+       WCD939X_IRQ_EAR_CNP_INT,
+       WCD939X_IRQ_EAR_SCD_INT,
+       WCD939X_IRQ_HPHL_PDM_WD_INT,
+       WCD939X_IRQ_HPHR_PDM_WD_INT,
+       WCD939X_IRQ_EAR_PDM_WD_INT,
+
+       /* INTR_CTRL_INT_MASK_2 */
+       WCD939X_IRQ_MBHC_MOISTURE_INT,
+       WCD939X_IRQ_HPHL_SURGE_DET_INT,
+       WCD939X_IRQ_HPHR_SURGE_DET_INT,
+       WCD939X_NUM_IRQS,
+};
+
+enum {
+       MICB_BIAS_DISABLE = 0,
+       MICB_BIAS_ENABLE,
+       MICB_BIAS_PULL_UP,
+       MICB_BIAS_PULL_DOWN,
+};
+
+enum {
+       WCD_ADC1 = 0,
+       WCD_ADC2,
+       WCD_ADC3,
+       WCD_ADC4,
+       HPH_PA_DELAY,
+};
+
+enum {
+       ADC_MODE_INVALID = 0,
+       ADC_MODE_HIFI,
+       ADC_MODE_LO_HIF,
+       ADC_MODE_NORMAL,
+       ADC_MODE_LP,
+       ADC_MODE_ULP1,
+       ADC_MODE_ULP2,
+};
+
+enum {
+       AIF1_PB = 0,
+       AIF1_CAP,
+       NUM_CODEC_DAIS,
+};
+
+static u8 tx_mode_bit[] = {
+       [ADC_MODE_INVALID] = 0x00,
+       [ADC_MODE_HIFI] = 0x01,
+       [ADC_MODE_LO_HIF] = 0x02,
+       [ADC_MODE_NORMAL] = 0x04,
+       [ADC_MODE_LP] = 0x08,
+       [ADC_MODE_ULP1] = 0x10,
+       [ADC_MODE_ULP2] = 0x20,
+};
+
+struct zdet_param {
+       u16 ldo_ctl;
+       u16 noff;
+       u16 nshift;
+       u16 btn5;
+       u16 btn6;
+       u16 btn7;
+};
+
+struct wcd939x_priv {
+       struct sdw_slave *tx_sdw_dev;
+       struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+       struct device *txdev;
+       struct device *rxdev;
+       struct device_node *rxnode, *txnode;
+       struct regmap *regmap;
+       struct snd_soc_component *component;
+       /* micb setup lock */
+       struct mutex micb_lock;
+       /* typec handling */
+       bool typec_analog_mux;
+#if IS_ENABLED(CONFIG_TYPEC)
+       struct typec_mux_dev *typec_mux;
+       struct typec_switch_dev *typec_sw;
+       enum typec_orientation typec_orientation;
+       unsigned long typec_mode;
+       struct typec_switch *typec_switch;
+#endif /* CONFIG_TYPEC */
+       /* mbhc module */
+       struct wcd_mbhc *wcd_mbhc;
+       struct wcd_mbhc_config mbhc_cfg;
+       struct wcd_mbhc_intr intr_ids;
+       struct wcd_clsh_ctrl *clsh_info;
+       struct irq_domain *virq;
+       struct regmap_irq_chip *wcd_regmap_irq_chip;
+       struct regmap_irq_chip_data *irq_chip;
+       struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY];
+       struct snd_soc_jack *jack;
+       unsigned long status_mask;
+       s32 micb_ref[WCD939X_MAX_MICBIAS];
+       s32 pullup_ref[WCD939X_MAX_MICBIAS];
+       u32 hph_mode;
+       u32 tx_mode[TX_ADC_MAX];
+       int variant;
+       int reset_gpio;
+       u32 micb1_mv;
+       u32 micb2_mv;
+       u32 micb3_mv;
+       u32 micb4_mv;
+       int hphr_pdm_wd_int;
+       int hphl_pdm_wd_int;
+       int ear_pdm_wd_int;
+       bool comp1_enable;
+       bool comp2_enable;
+       bool ldoh;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+       WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40),
+       WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20),
+       WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+       WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08),
+       WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F),
+       WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08),
+       WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01),
+       WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06),
+       WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+       WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03),
+       WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03),
+       WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08),
+       WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40),
+       WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07),
+       WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70),
+       WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF),
+       WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0),
+       WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0),
+       WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02),
+       WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01),
+       WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70),
+       WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20),
+       WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08),
+       WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40),
+       WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80),
+       WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF),
+       WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F),
+       WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10),
+       WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04),
+       WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = {
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01),
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02),
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04),
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08),
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01),
+       REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02),
+       REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40),
+       REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80),
+       REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04),
+       REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
+};
+
+static struct regmap_irq_chip wcd939x_regmap_irq_chip = {
+       .name = "wcd939x",
+       .irqs = wcd939x_irqs,
+       .num_irqs = ARRAY_SIZE(wcd939x_irqs),
+       .num_regs = 3,
+       .status_base = WCD939X_DIGITAL_INTR_STATUS_0,
+       .mask_base = WCD939X_DIGITAL_INTR_MASK_0,
+       .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0,
+       .use_ack = 1,
+       .runtime_pm = true,
+       .irq_drv_data = NULL,
+};
+
+static int wcd939x_get_clk_rate(int mode)
+{
+       int rate;
+
+       switch (mode) {
+       case ADC_MODE_ULP2:
+               rate = SWR_CLK_RATE_0P6MHZ;
+               break;
+       case ADC_MODE_ULP1:
+               rate = SWR_CLK_RATE_1P2MHZ;
+               break;
+       case ADC_MODE_LP:
+               rate = SWR_CLK_RATE_4P8MHZ;
+               break;
+       case ADC_MODE_NORMAL:
+       case ADC_MODE_LO_HIF:
+       case ADC_MODE_HIFI:
+       case ADC_MODE_INVALID:
+       default:
+               rate = SWR_CLK_RATE_9P6MHZ;
+               break;
+       }
+
+       return rate;
+}
+
+static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank)
+{
+       u8 mask = (bank ? 0xF0 : 0x0F);
+       u8 val = 0;
+
+       switch (rate) {
+       case SWR_CLK_RATE_0P6MHZ:
+               val = 6;
+               break;
+       case SWR_CLK_RATE_1P2MHZ:
+               val = 5;
+               break;
+       case SWR_CLK_RATE_2P4MHZ:
+               val = 3;
+               break;
+       case SWR_CLK_RATE_4P8MHZ:
+               val = 1;
+               break;
+       case SWR_CLK_RATE_9P6MHZ:
+       default:
+               val = 0;
+               break;
+       }
+
+       snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val);
+
+       return 0;
+}
+
+static int wcd939x_io_init(struct snd_soc_component *component)
+{
+       snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+                                     WCD939X_BIAS_ANALOG_BIAS_EN, true);
+       snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+                                     WCD939X_BIAS_PRECHRG_EN, true);
+
+       /* 10 msec delay as per HW requirement */
+       usleep_range(10000, 10010);
+       snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+                                     WCD939X_BIAS_PRECHRG_EN, false);
+
+       snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+                                     WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15);
+       snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+                                     WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15);
+       snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+                                     WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);
+
+       snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
+                                     WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1);
+       snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
+                                     WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4);
+
+       snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP,
+                                     WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8);
+
+       snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1,
+                                     WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+       snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1,
+                                     WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+       snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1,
+                                     WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+       snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1,
+                                     WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+       snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2,
+                                     WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false);
+
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false);
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false);
+
+       snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
+                                     WCD939X_OCP_CTL_OCP_FSM_EN, true);
+       snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
+                                     WCD939X_OCP_CTL_SCD_OP_EN, true);
+
+       snd_soc_component_write(component, WCD939X_E_CFG0,
+                               WCD939X_CFG0_IDLE_STEREO |
+                               WCD939X_CFG0_AUTO_DISABLE_ANC);
+
+       return 0;
+}
+
+static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info,
+                                   struct sdw_port_config *port_config,
+                                   u8 enable)
+{
+       u8 ch_mask, port_num;
+
+       port_num = ch_info->port_num;
+       ch_mask = ch_info->ch_mask;
+
+       port_config->num = port_num;
+
+       if (enable)
+               port_config->ch_mask |= ch_mask;
+       else
+               port_config->ch_mask &= ~ch_mask;
+
+       return 0;
+}
+
+static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable)
+{
+       return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id],
+                                       &wcd->port_config[port_num - 1],
+                                       enable);
+}
+
+static int wcd939x_codec_enable_rxclk(struct snd_soc_dapm_widget *w,
+                                     struct snd_kcontrol *kcontrol,
+                                     int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                             WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true);
+
+               /* Analog path clock controls */
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
+                                             true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
+                                             true);
+
+               /* Digital path clock controls */
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                             WCD939X_RX_SUPPLIES_VNEG_EN, false);
+               snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                             WCD939X_RX_SUPPLIES_VPOS_EN, false);
+
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                             WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false);
+
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
+                                             false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
+                                             false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false);
+
+               snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                             WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false);
+
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kcontrol,
+                                       int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
+                                             WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
+                                             false);
+
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+                                             WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+                                             WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d);
+               if (wcd939x->comp1_enable) {
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                     WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
+                                                     true);
+                        /* 5msec compander delay as per HW requirement */
+                       if (!wcd939x->comp2_enable ||
+                           snd_soc_component_read_field(component,
+                                                        WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                        WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN))
+                               usleep_range(5000, 5010);
+
+                       snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+                                                     WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
+                                                     false);
+               } else {
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                     WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
+                                                     false);
+                       snd_soc_component_write_field(component, WCD939X_HPH_L_EN,
+                                                     WCD939X_L_EN_GAIN_SOURCE_SEL, true);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+                                             WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+                                             WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kcontrol,
+                                       int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+               w->name, event);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
+                                             WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
+                                             false);
+
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+                                             WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+                                             WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d);
+               if (wcd939x->comp2_enable) {
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                     WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
+                                                     true);
+                       /* 5msec compander delay as per HW requirement */
+                       if (!wcd939x->comp1_enable ||
+                           snd_soc_component_read_field(component,
+                                                        WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                        WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN))
+                               usleep_range(5000, 5010);
+                       snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+                                                     WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
+                                                     false);
+               } else {
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_COMP_CTL_0,
+                                                     WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
+                                                     false);
+                       snd_soc_component_write_field(component, WCD939X_HPH_R_EN,
+                                                     WCD939X_R_EN_GAIN_SOURCE_SEL, true);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+                                             WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+                                             WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+                                      struct snd_kcontrol *kcontrol,
+                                      int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL,
+                                             WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true);
+
+               snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
+                                             WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false);
+
+               /* 5 msec delay as per HW requirement */
+               usleep_range(5000, 5010);
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+                                       WCD_CLSH_STATE_EAR, CLS_AB_HIFI);
+
+               snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+                                             WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
+                                             WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_enable_hphr_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int hph_mode = wcd939x->hph_mode;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (wcd939x->ldoh)
+                       snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+                                                     WCD939X_MODE_LDOH_EN, true);
+
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+                                       WCD_CLSH_STATE_HPHR, hph_mode);
+               wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);
+
+               if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
+                       snd_soc_component_write_field(component,
+                                       WCD939X_HPH_REFBUFF_LP_CTL,
+                                       WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true);
+               if (hph_mode == CLS_H_LOHIFI)
+                       snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                                      WCD939X_HPH_PWR_LEVEL, 0);
+
+               snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+                                             WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHR_REF_ENABLE, true);
+
+               if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
+                                                WCD939X_HPH_HPHL_REF_ENABLE))
+                       usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */
+
+               set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
+                                             WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+                       if (!wcd939x->comp2_enable)
+                               usleep_range(20000, 20100);
+                       else
+                               usleep_range(7000, 7100);
+
+                       if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+                           hph_mode == CLS_H_ULP)
+                               snd_soc_component_write_field(component,
+                                               WCD939X_HPH_REFBUFF_LP_CTL,
+                                               WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+                                               false);
+                       clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               }
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+                                             WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
+               if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+                   hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+                       snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                                     WCD939X_RX_SUPPLIES_REGULATOR_MODE,
+                                                     true);
+
+               enable_irq(wcd939x->hphr_pdm_wd_int);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               disable_irq_nosync(wcd939x->hphr_pdm_wd_int);
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (!wcd939x->comp2_enable)
+                       usleep_range(20000, 20100);
+               else
+                       usleep_range(7000, 7100);
+
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHR_ENABLE, false);
+
+               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                     WCD_EVENT_PRE_HPHR_PA_OFF);
+               set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+                       if (!wcd939x->comp2_enable)
+                               usleep_range(20000, 20100);
+                       else
+                               usleep_range(7000, 7100);
+                       clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               }
+               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                     WCD_EVENT_POST_HPHR_PA_OFF);
+
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHR_REF_ENABLE, false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
+                                             WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0);
+
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+                                       WCD_CLSH_STATE_HPHR, hph_mode);
+               if (wcd939x->ldoh)
+                       snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+                                                     WCD939X_MODE_LDOH_EN, false);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_enable_hphl_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int hph_mode = wcd939x->hph_mode;
+
+       dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+               w->name, event);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (wcd939x->ldoh)
+                       snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+                                                     WCD939X_MODE_LDOH_EN, true);
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+                                       WCD_CLSH_STATE_HPHL, hph_mode);
+               wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);
+
+               if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
+                       snd_soc_component_write_field(component,
+                                               WCD939X_HPH_REFBUFF_LP_CTL,
+                                               WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+                                               true);
+               if (hph_mode == CLS_H_LOHIFI)
+                       snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                                      WCD939X_HPH_PWR_LEVEL, 0);
+
+               snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+                                             WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHL_REF_ENABLE, true);
+
+               if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
+                                                WCD939X_HPH_HPHR_REF_ENABLE))
+                       usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */
+
+               set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+                                             WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+                       if (!wcd939x->comp1_enable)
+                               usleep_range(20000, 20100);
+                       else
+                               usleep_range(7000, 7100);
+                       if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+                           hph_mode == CLS_H_ULP)
+                               snd_soc_component_write_field(component,
+                                               WCD939X_HPH_REFBUFF_LP_CTL,
+                                               WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+                                               false);
+                       clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               }
+               snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+                                             WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
+               if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+                   hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+                       snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                                     WCD939X_RX_SUPPLIES_REGULATOR_MODE,
+                                                     true);
+               enable_irq(wcd939x->hphl_pdm_wd_int);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               disable_irq_nosync(wcd939x->hphl_pdm_wd_int);
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (!wcd939x->comp1_enable)
+                       usleep_range(20000, 20100);
+               else
+                       usleep_range(7000, 7100);
+
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHL_ENABLE, false);
+
+               wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF);
+               set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               /*
+                * 7ms sleep is required if compander is enabled as per
+                * HW requirement. If compander is disabled, then
+                * 20ms delay is required.
+                */
+               if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+                       if (!wcd939x->comp1_enable)
+                               usleep_range(21000, 21100);
+                       else
+                               usleep_range(7000, 7100);
+                       clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+               }
+               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                     WCD_EVENT_POST_HPHL_PA_OFF);
+               snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+                                             WCD939X_HPH_HPHL_REF_ENABLE, false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+                                             WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+                                       WCD_CLSH_STATE_HPHL, hph_mode);
+               if (wcd939x->ldoh)
+                       snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+                                                     WCD939X_MODE_LDOH_EN, false);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_enable_ear_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* Enable watchdog interrupt for HPHL */
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+                                             WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
+               /* For EAR, use CLASS_AB regulator mode */
+               snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+                                             WCD939X_RX_SUPPLIES_REGULATOR_MODE, true);
+               snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
+                                             WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               /* 6 msec delay as per HW requirement */
+               usleep_range(6000, 6010);
+               enable_irq(wcd939x->ear_pdm_wd_int);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               disable_irq_nosync(wcd939x->ear_pdm_wd_int);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
+                                             WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG,
+                                             false);
+               /* 7 msec delay as per HW requirement */
+               usleep_range(7000, 7010);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+                                             WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
+               wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+                                       WCD_CLSH_STATE_EAR, CLS_AB_HIFI);
+               break;
+       }
+
+       return 0;
+}
+
+/* TX Controls */
+
+static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+                                    struct snd_kcontrol *kcontrol,
+                                    int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       u16 dmic_clk_reg, dmic_clk_en_reg;
+       u8 dmic_clk_en_mask;
+       u8 dmic_ctl_mask;
+       u8 dmic_clk_mask;
+
+       switch (w->shift) {
+       case 0:
+       case 1:
+               dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
+               dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL;
+               dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN;
+               dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE;
+               dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL;
+               break;
+       case 2:
+       case 3:
+               dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
+               dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL;
+               dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN;
+               dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE;
+               dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL;
+               break;
+       case 4:
+       case 5:
+               dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
+               dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL;
+               dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN;
+               dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE;
+               dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL;
+               break;
+       case 6:
+       case 7:
+               dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
+               dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL;
+               dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN;
+               dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE;
+               dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL;
+               break;
+       default:
+               dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
+                                             dmic_ctl_mask, false);
+               /* 250us sleep as per HW requirement */
+               usleep_range(250, 260);
+               if (w->shift == 2)
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DMIC2_CTL,
+                                                     WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
+                                                     true);
+               /* Setting DMIC clock rate to 2.4MHz */
+               snd_soc_component_write_field(component, dmic_clk_reg,
+                                             dmic_clk_mask, 3);
+               snd_soc_component_write_field(component, dmic_clk_en_reg,
+                                             dmic_clk_en_mask, true);
+               /* enable clock scaling */
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+                                             WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+                                             WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
+                                             dmic_ctl_mask, 1);
+               if (w->shift == 2)
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DMIC2_CTL,
+                                                     WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
+                                                     false);
+               snd_soc_component_write_field(component, dmic_clk_en_reg,
+                                             dmic_clk_en_mask, 0);
+               break;
+       }
+       return 0;
+}
+
+static int wcd939x_tx_swr_ctrl(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int bank;
+       int rate;
+
+       bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+                       int mode = 0;
+
+                       if (test_bit(WCD_ADC1, &wcd939x->status_mask))
+                               mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]];
+                       if (test_bit(WCD_ADC2, &wcd939x->status_mask))
+                               mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]];
+                       if (test_bit(WCD_ADC3, &wcd939x->status_mask))
+                               mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]];
+                       if (test_bit(WCD_ADC4, &wcd939x->status_mask))
+                               mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]];
+
+                       if (mode)
+                               rate = wcd939x_get_clk_rate(ffs(mode) - 1);
+                       else
+                               rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
+                       wcd939x_set_swr_clk_rate(component, rate, bank);
+                       wcd939x_set_swr_clk_rate(component, rate, !bank);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+                       rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
+                       wcd939x_set_swr_clk_rate(component, rate, !bank);
+                       wcd939x_set_swr_clk_rate(component, rate, bank);
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_get_adc_mode(int val)
+{
+       int ret = 0;
+
+       switch (val) {
+       case ADC_MODE_INVALID:
+               ret = ADC_MODE_VAL_NORMAL;
+               break;
+       case ADC_MODE_HIFI:
+               ret = ADC_MODE_VAL_HIFI;
+               break;
+       case ADC_MODE_LO_HIF:
+               ret = ADC_MODE_VAL_LO_HIF;
+               break;
+       case ADC_MODE_NORMAL:
+               ret = ADC_MODE_VAL_NORMAL;
+               break;
+       case ADC_MODE_LP:
+               ret = ADC_MODE_VAL_LP;
+               break;
+       case ADC_MODE_ULP1:
+               ret = ADC_MODE_VAL_ULP1;
+               break;
+       case ADC_MODE_ULP2:
+               ret = ADC_MODE_VAL_ULP2;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int wcd939x_codec_enable_adc(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+                                             true);
+               set_bit(w->shift, &wcd939x->status_mask);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+                                             false);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                             WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN,
+                                             false);
+               clear_bit(w->shift, &wcd939x->status_mask);
+               break;
+       }
+
+       return 0;
+}
+
+static void wcd939x_tx_channel_config(struct snd_soc_component *component,
+                                     int channel, bool init)
+{
+       int reg, mask;
+
+       switch (channel) {
+       case 0:
+               reg = WCD939X_ANA_TX_CH2;
+               mask = WCD939X_TX_CH2_HPF1_INIT;
+               break;
+       case 1:
+               reg = WCD939X_ANA_TX_CH2;
+               mask = WCD939X_TX_CH2_HPF2_INIT;
+               break;
+       case 2:
+               reg = WCD939X_ANA_TX_CH4;
+               mask = WCD939X_TX_CH4_HPF3_INIT;
+               break;
+       case 3:
+               reg = WCD939X_ANA_TX_CH4;
+               mask = WCD939X_TX_CH4_HPF4_INIT;
+               break;
+       default:
+               return;
+       }
+
+       snd_soc_component_write_field(component, reg, mask, init);
+}
+
+static int wcd939x_adc_enable_req(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int mode;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
+                                             WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true);
+               snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
+                                             WCD939X_CDC_REQ_CTL_NO_NOTCH, false);
+
+               wcd939x_tx_channel_config(component, w->shift, true);
+               mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]);
+               if (mode < 0) {
+                       dev_info(component->dev, "Invalid ADC mode\n");
+                       return -EINVAL;
+               }
+
+               switch (w->shift) {
+               case 0:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+                                                     WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
+                                                     mode);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
+                                                     true);
+                       break;
+               case 1:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+                                                     WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
+                                                     mode);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
+                                                     true);
+                       break;
+               case 2:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+                                                     WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
+                                                     mode);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
+                                                     true);
+                       break;
+               case 3:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+                                                     WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
+                                                     mode);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
+                                                     true);
+                       break;
+               default:
+                       break;
+               }
+
+               wcd939x_tx_channel_config(component, w->shift, false);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               switch (w->shift) {
+               case 0:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+                                                     WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
+                                                     false);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
+                                                     false);
+                       break;
+               case 1:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+                                                     WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
+                                                     false);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
+                                                     false);
+                       break;
+               case 2:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+                                                     WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
+                                                     false);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
+                                                     false);
+                       break;
+               case 3:
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+                                                     WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
+                                                     false);
+                       snd_soc_component_write_field(component,
+                                                     WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                                     WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
+                                                     false);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_micbias_control(struct snd_soc_component *component,
+                                  int micb_num, int req, bool is_dapm)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int micb_index = micb_num - 1;
+       u16 micb_field;
+       u16 micb_reg;
+
+       switch (micb_num) {
+       case MIC_BIAS_1:
+               micb_reg = WCD939X_ANA_MICB1;
+               micb_field = WCD939X_MICB1_ENABLE;
+               break;
+       case MIC_BIAS_2:
+               micb_reg = WCD939X_ANA_MICB2;
+               micb_field = WCD939X_MICB2_ENABLE;
+               break;
+       case MIC_BIAS_3:
+               micb_reg = WCD939X_ANA_MICB3;
+               micb_field = WCD939X_MICB3_ENABLE;
+               break;
+       case MIC_BIAS_4:
+               micb_reg = WCD939X_ANA_MICB4;
+               micb_field = WCD939X_MICB4_ENABLE;
+               break;
+       default:
+               dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+                       __func__, micb_num);
+               return -EINVAL;
+       }
+
+       switch (req) {
+       case MICB_PULLUP_ENABLE:
+               wcd939x->pullup_ref[micb_index]++;
+               if (wcd939x->pullup_ref[micb_index] == 1 &&
+                   wcd939x->micb_ref[micb_index] == 0)
+                       snd_soc_component_write_field(component, micb_reg,
+                                                     micb_field, MICB_BIAS_PULL_UP);
+               break;
+       case MICB_PULLUP_DISABLE:
+               if (wcd939x->pullup_ref[micb_index] > 0)
+                       wcd939x->pullup_ref[micb_index]--;
+               if (wcd939x->pullup_ref[micb_index] == 0 &&
+                   wcd939x->micb_ref[micb_index] == 0)
+                       snd_soc_component_write_field(component, micb_reg,
+                                                     micb_field, MICB_BIAS_DISABLE);
+               break;
+       case MICB_ENABLE:
+               wcd939x->micb_ref[micb_index]++;
+               if (wcd939x->micb_ref[micb_index] == 1) {
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                               WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                               WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                               WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+                                               WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+                                               WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+                                               true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL,
+                                               WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN,
+                                               true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_MICB1_TEST_CTL_2,
+                                               WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_MICB2_TEST_CTL_2,
+                                               WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_MICB3_TEST_CTL_2,
+                                               WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+                       snd_soc_component_write_field(component,
+                                               WCD939X_MICB4_TEST_CTL_2,
+                                               WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+                       snd_soc_component_write_field(component, micb_reg, micb_field,
+                                                     MICB_BIAS_ENABLE);
+                       if (micb_num == MIC_BIAS_2)
+                               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                                     WCD_EVENT_POST_MICBIAS_2_ON);
+               }
+               if (micb_num == MIC_BIAS_2 && is_dapm)
+                       wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                             WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+               break;
+       case MICB_DISABLE:
+               if (wcd939x->micb_ref[micb_index] > 0)
+                       wcd939x->micb_ref[micb_index]--;
+
+               if (wcd939x->micb_ref[micb_index] == 0 &&
+                   wcd939x->pullup_ref[micb_index] > 0)
+                       snd_soc_component_write_field(component, micb_reg,
+                                                     micb_field, MICB_BIAS_PULL_UP);
+               else if (wcd939x->micb_ref[micb_index] == 0 &&
+                        wcd939x->pullup_ref[micb_index] == 0) {
+                       if (micb_num  == MIC_BIAS_2)
+                               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                                     WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+                       snd_soc_component_write_field(component, micb_reg,
+                                                     micb_field, MICB_BIAS_DISABLE);
+                       if (micb_num  == MIC_BIAS_2)
+                               wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                                     WCD_EVENT_POST_MICBIAS_2_OFF);
+               }
+               if (is_dapm && micb_num  == MIC_BIAS_2)
+                       wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+                                             WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kcontrol,
+                                       int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       int micb_num = w->shift;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               /* 1 msec delay as per HW requirement */
+               usleep_range(1000, 1100);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+                                              struct snd_kcontrol *kcontrol,
+                                              int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       int micb_num = w->shift;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               wcd939x_micbias_control(component, micb_num,
+                                       MICB_PULLUP_ENABLE, true);
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               /* 1 msec delay as per HW requirement */
+               usleep_range(1000, 1100);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               wcd939x_micbias_control(component, micb_num,
+                                       MICB_PULLUP_DISABLE, true);
+               break;
+       }
+
+       return 0;
+}
+
+static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int path = e->shift_l;
+
+       ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path];
+
+       return 0;
+}
+
+static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int path = e->shift_l;
+
+       if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0])
+               return 0;
+
+       wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0];
+
+       return 1;
+}
+
+/* RX Controls */
+
+static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = wcd939x->hph_mode;
+
+       return 0;
+}
+
+static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       u32 mode_val;
+
+       mode_val = ucontrol->value.enumerated.item[0];
+
+       if (mode_val == wcd939x->hph_mode)
+               return 0;
+
+       if (wcd939x->variant == WCD9390) {
+               switch (mode_val) {
+               case CLS_H_NORMAL:
+               case CLS_H_LP:
+               case CLS_AB:
+               case CLS_H_LOHIFI:
+               case CLS_H_ULP:
+               case CLS_AB_LP:
+               case CLS_AB_LOHIFI:
+                       wcd939x->hph_mode = mode_val;
+                       return 1;
+               }
+       } else {
+               switch (mode_val) {
+               case CLS_H_NORMAL:
+               case CLS_H_HIFI:
+               case CLS_H_LP:
+               case CLS_AB:
+               case CLS_H_LOHIFI:
+               case CLS_H_ULP:
+               case CLS_AB_HIFI:
+               case CLS_AB_LP:
+               case CLS_AB_LOHIFI:
+                       wcd939x->hph_mode = mode_val;
+                       return 1;
+               }
+       }
+
+       dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__);
+       return -EINVAL;
+}
+
+static int wcd939x_get_compander(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       if (mc->shift)
+               ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0;
+       else
+               ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0;
+
+       return 0;
+}
+
+static int wcd939x_set_compander(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB];
+       bool value = !!ucontrol->value.integer.value[0];
+       int portidx = wcd->ch_info[mc->reg].port_num;
+
+       if (mc->shift)
+               wcd939x->comp2_enable = value;
+       else
+               wcd939x->comp1_enable = value;
+
+       if (value)
+               wcd939x_connect_port(wcd, portidx, mc->reg, true);
+       else
+               wcd939x_connect_port(wcd, portidx, mc->reg, false);
+
+       return 1;
+}
+
+static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0;
+
+       return 0;
+}
+
+static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       if (wcd939x->ldoh == !!ucontrol->value.integer.value[0])
+               return 0;
+
+       wcd939x->ldoh = !!ucontrol->value.integer.value[0];
+
+       return 1;
+}
+
+static const char * const tx_mode_mux_text_wcd9390[] = {
+       "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+};
+
+static const struct soc_enum tx0_mode_mux_enum_wcd9390 =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+                       tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx1_mode_mux_enum_wcd9390 =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+                       tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx2_mode_mux_enum_wcd9390 =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+                       tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx3_mode_mux_enum_wcd9390 =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+                       tx_mode_mux_text_wcd9390);
+
+static const char * const tx_mode_mux_text[] = {
+       "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+       "ADC_ULP1", "ADC_ULP2",
+};
+
+static const struct soc_enum tx0_mode_mux_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text),
+                       tx_mode_mux_text);
+
+static const struct soc_enum tx1_mode_mux_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text),
+                       tx_mode_mux_text);
+
+static const struct soc_enum tx2_mode_mux_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text),
+                       tx_mode_mux_text);
+
+static const struct soc_enum tx3_mode_mux_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text),
+                       tx_mode_mux_text);
+
+static const char * const rx_hph_mode_mux_text_wcd9390[] = {
+       "CLS_H_NORMAL", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB",
+       "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP",
+       "CLS_AB_LOHIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390),
+                           rx_hph_mode_mux_text_wcd9390);
+
+static const char * const rx_hph_mode_mux_text[] = {
+       "CLS_H_NORMAL", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+       "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+                           rx_hph_mode_mux_text);
+
+static const struct snd_kcontrol_new wcd9390_snd_controls[] = {
+       SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL,
+                      2, 0x10, 0, ear_pa_gain),
+
+       SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390,
+                    wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),
+
+       SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new wcd9395_snd_controls[] = {
+       SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+                    wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),
+
+       SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+       SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum,
+                    wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc3_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc4_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic3_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic4_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic5_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic6_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic7_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic8_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc1_mux_text[] = {
+       "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4", "CH1_AMIC5"
+};
+
+static const struct soc_enum adc1_enum =
+       SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0,
+                       ARRAY_SIZE(adc1_mux_text), adc1_mux_text);
+
+static const struct snd_kcontrol_new tx_adc1_mux =
+       SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum);
+
+static const char * const adc2_mux_text[] = {
+       "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4", "CH2_AMIC5"
+};
+
+static const struct soc_enum adc2_enum =
+       SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3,
+                       ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+       SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const char * const adc3_mux_text[] = {
+       "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4", "CH3_AMIC5"
+};
+
+static const struct soc_enum adc3_enum =
+       SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0,
+                       ARRAY_SIZE(adc3_mux_text), adc3_mux_text);
+
+static const struct snd_kcontrol_new tx_adc3_mux =
+       SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum);
+
+static const char * const adc4_mux_text[] = {
+       "CH4_AMIC_DISABLE", "CH4_AMIC1", "CH4_AMIC3", "CH4_AMIC4", "CH4_AMIC5"
+};
+
+static const struct soc_enum adc4_enum =
+       SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3,
+                       ARRAY_SIZE(adc4_mux_text), adc4_mux_text);
+
+static const struct snd_kcontrol_new tx_adc4_mux =
+       SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum);
+
+static const char * const rdac3_mux_text[] = {
+       "RX3", "RX1"
+};
+
+static const struct soc_enum rdac3_enum =
+       SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0,
+                       ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text);
+
+static const struct snd_kcontrol_new rx_rdac3_mux =
+       SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum);
+
+static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
+       unsigned int portidx = wcd->ch_info[mixer->reg].port_num;
+
+       ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0;
+
+       return 0;
+}
+
+static const char *version_to_str(u32 version)
+{
+       switch (version) {
+       case WCD939X_VERSION_1_0:
+               return __stringify(WCD939X_1_0);
+       case WCD939X_VERSION_1_1:
+               return __stringify(WCD939X_1_1);
+       case WCD939X_VERSION_2_0:
+               return __stringify(WCD939X_2_0);
+       }
+       return NULL;
+}
+
+static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
+       unsigned int portidx = wcd->ch_info[mixer->reg].port_num;
+
+       wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0];
+
+       wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]);
+
+       return 1;
+}
+
+/* MBHC Related */
+
+static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component,
+                                  bool enable)
+{
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1,
+                                     WCD939X_CTL_1_RCO_EN, enable);
+}
+
+static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+                                          bool enable)
+{
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+                                     WCD939X_MBHC_ELECT_BIAS_EN, enable);
+}
+
+static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component,
+                                        int *btn_low, int *btn_high,
+                                        int num_btn, bool is_micbias)
+{
+       int i, vth;
+
+       if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+               dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+                       __func__, num_btn);
+               return;
+       }
+
+       for (i = 0; i < num_btn; i++) {
+               vth = (btn_high[i] * 2) / 25;
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i,
+                                             WCD939X_MBHC_BTN0_VTH, vth);
+               dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+                       __func__, i, btn_high[i], vth);
+       }
+}
+
+static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+
+       if (micb_num == MIC_BIAS_2) {
+               u8 val;
+
+               val = FIELD_GET(WCD939X_MICB2_ENABLE,
+                               snd_soc_component_read(component, WCD939X_ANA_MICB2));
+               if (val == MICB_BIAS_ENABLE)
+                       return true;
+       }
+
+       return false;
+}
+
+static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+                                              int pull_up_cur)
+{
+       /* Default pull up current to 2uA */
+       if (pull_up_cur > HS_PULLUP_I_OFF ||
+           pull_up_cur < HS_PULLUP_I_3P0_UA ||
+           pull_up_cur == HS_PULLUP_I_DEFAULT)
+               pull_up_cur = HS_PULLUP_I_2P0_UA;
+
+       dev_dbg(component->dev, "%s: HS pull up current:%d\n",
+               __func__, pull_up_cur);
+
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT,
+                                     WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur);
+}
+
+static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component,
+                                       int micb_num, int req)
+{
+       return wcd939x_micbias_control(component, micb_num, req, false);
+}
+
+static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+                                          bool enable)
+{
+       if (enable) {
+               snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+                                             WCD939X_MICB2_RAMP_SHIFT_CTL, 3);
+               snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+                                             WCD939X_MICB2_RAMP_RAMP_ENABLE, true);
+       } else {
+               snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+                                             WCD939X_MICB2_RAMP_RAMP_ENABLE, false);
+               snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+                                             WCD939X_MICB2_RAMP_SHIFT_CTL, 0);
+       }
+}
+
+static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv)
+{
+       /* min micbias voltage is 1V and maximum is 2.85V */
+       if (micb_mv < 1000 || micb_mv > 2850) {
+               pr_err("%s: unsupported micbias voltage\n", __func__);
+               return -EINVAL;
+       }
+
+       return (micb_mv - 1000) / 50;
+}
+
+static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+                                           int req_volt, int micb_num)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       unsigned int micb_en_field, micb_vout_ctl_field;
+       unsigned int micb_reg, cur_vout_ctl, micb_en;
+       int req_vout_ctl;
+       int ret = 0;
+
+       switch (micb_num) {
+       case MIC_BIAS_1:
+               micb_reg = WCD939X_ANA_MICB1;
+               micb_en_field = WCD939X_MICB1_ENABLE;
+               micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL;
+               break;
+       case MIC_BIAS_2:
+               micb_reg = WCD939X_ANA_MICB2;
+               micb_en_field = WCD939X_MICB2_ENABLE;
+               micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL;
+               break;
+       case MIC_BIAS_3:
+               micb_reg = WCD939X_ANA_MICB3;
+               micb_en_field = WCD939X_MICB3_ENABLE;
+               micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL;
+               break;
+       case MIC_BIAS_4:
+               micb_reg = WCD939X_ANA_MICB4;
+               micb_en_field = WCD939X_MICB4_ENABLE;
+               micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL;
+               break;
+       default:
+               return -EINVAL;
+       }
+       mutex_lock(&wcd939x->micb_lock);
+
+       /*
+        * If requested micbias voltage is same as current micbias
+        * voltage, then just return. Otherwise, adjust voltage as
+        * per requested value. If micbias is already enabled, then
+        * to avoid slow micbias ramp-up or down enable pull-up
+        * momentarily, change the micbias value and then re-enable
+        * micbias.
+        */
+       micb_en = snd_soc_component_read_field(component, micb_reg,
+                                              micb_en_field);
+       cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+                                                   micb_vout_ctl_field);
+
+       req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt);
+       if (req_vout_ctl < 0) {
+               ret = req_vout_ctl;
+               goto exit;
+       }
+
+       if (cur_vout_ctl == req_vout_ctl) {
+               ret = 0;
+               goto exit;
+       }
+
+       dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
+               __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
+                req_volt, micb_en);
+
+       if (micb_en == MICB_BIAS_ENABLE)
+               snd_soc_component_write_field(component, micb_reg,
+                                             micb_en_field, MICB_BIAS_PULL_DOWN);
+
+       snd_soc_component_write_field(component, micb_reg,
+                                     micb_vout_ctl_field, req_vout_ctl);
+
+       if (micb_en == MICB_BIAS_ENABLE) {
+               snd_soc_component_write_field(component, micb_reg,
+                                             micb_en_field, MICB_BIAS_ENABLE);
+               /*
+                * Add 2ms delay as per HW requirement after enabling
+                * micbias
+                */
+               usleep_range(2000, 2100);
+       }
+
+exit:
+       mutex_unlock(&wcd939x->micb_lock);
+       return ret;
+}
+
+static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+                                               int micb_num, bool req_en)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       int micb_mv;
+
+       if (micb_num != MIC_BIAS_2)
+               return -EINVAL;
+       /*
+        * If device tree micbias level is already above the minimum
+        * voltage needed to detect threshold microphone, then do
+        * not change the micbias, just return.
+        */
+       if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+               return 0;
+
+       micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv;
+
+       return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+}
+
+/* Selected by WCD939X_MBHC_GET_C1() */
+static const s16 wcd939x_wcd_mbhc_d1_a[4] = {
+       0, 30, 30, 6
+};
+
+/* Selected by zdet_param.noff */
+static const int wcd939x_mbhc_mincode_param[] = {
+       3277, 1639, 820, 410, 205, 103, 52, 26
+};
+
+static const struct zdet_param wcd939x_mbhc_zdet_param = {
+       .ldo_ctl = 4,
+       .noff = 0,
+       .nshift = 6,
+       .btn5 = 0x18,
+       .btn6 = 0x60,
+       .btn7 = 0x78,
+};
+
+static void wcd939x_mbhc_get_result_params(struct snd_soc_component *component,
+                                          int32_t *zdet)
+{
+       const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param;
+       s32 x1, d1, denom;
+       int val;
+       s16 c1;
+       int i;
+
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_CHG_EN, true);
+       for (i = 0; i < WCD939X_ZDET_NUM_MEASUREMENTS; i++) {
+               val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2,
+                                                  WCD939X_MBHC_RESULT_2_Z_RESULT_MSB);
+               if (val & BIT(7))
+                       break;
+       }
+       val = val << 8;
+       val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1,
+                                           WCD939X_MBHC_RESULT_1_Z_RESULT_LSB);
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_CHG_EN, false);
+       x1 = WCD939X_MBHC_GET_X1(val);
+       c1 = WCD939X_MBHC_GET_C1(val);
+
+       /* If ramp is not complete, give additional 5ms */
+       if (c1 < 2 && x1)
+               mdelay(5);
+
+       if (!c1 || !x1) {
+               dev_dbg(component->dev,
+                       "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+                       __func__, c1, x1);
+               goto ramp_down;
+       }
+
+       d1 = wcd939x_wcd_mbhc_d1_a[c1];
+       denom = (x1 * d1) - (1 << (14 - zdet_param->noff));
+       if (denom > 0)
+               *zdet = (WCD939X_ANA_MBHC_ZDET_CONST * 1000) / denom;
+       else if (x1 <  wcd939x_mbhc_mincode_param[zdet_param->noff])
+               *zdet = WCD939X_ZDET_FLOATING_IMPEDANCE;
+
+       dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+               __func__, d1, c1, x1, *zdet);
+ramp_down:
+       i = 0;
+       while (x1) {
+               val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1,
+                                                  WCD939X_MBHC_RESULT_1_Z_RESULT_LSB) << 8;
+               val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2,
+                                                   WCD939X_MBHC_RESULT_2_Z_RESULT_MSB);
+               x1 = WCD939X_MBHC_GET_X1(val);
+               i++;
+               if (i == WCD939X_ZDET_NUM_MEASUREMENTS)
+                       break;
+       }
+}
+
+static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component,
+                                  s32 *zl, int32_t *zr)
+{
+       const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param;
+       s32 zdet = 0;
+
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL,
+                                     WCD939X_ZDET_ANA_CTL_MAXV_CTL, zdet_param->ldo_ctl);
+       snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN5, WCD939X_MBHC_BTN5_VTH,
+                                     zdet_param->btn5);
+       snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN6, WCD939X_MBHC_BTN6_VTH,
+                                     zdet_param->btn6);
+       snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN7, WCD939X_MBHC_BTN7_VTH,
+                                     zdet_param->btn7);
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL,
+                                     WCD939X_ZDET_ANA_CTL_RANGE_CTL, zdet_param->noff);
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL,
+                                     WCD939X_ZDET_RAMP_CTL_TIME_CTL, zdet_param->nshift);
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL,
+                                     WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL, 6); /*acc1_min_63 */
+
+       if (!zl)
+               goto z_right;
+
+       /* Start impedance measurement for HPH_L */
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, true);
+       dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n",
+               __func__, zdet_param->noff);
+       wcd939x_mbhc_get_result_params(component, &zdet);
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, false);
+
+       *zl = zdet;
+
+z_right:
+       if (!zr)
+               return;
+
+       /* Start impedance measurement for HPH_R */
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, true);
+       dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n",
+               __func__, zdet_param->noff);
+       wcd939x_mbhc_get_result_params(component, &zdet);
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+                                     WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, false);
+
+       *zr = zdet;
+}
+
+static void wcd939x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+                                      s32 *z_val, int flag_l_r)
+{
+       int q1_cal;
+       s16 q1;
+
+       q1 = snd_soc_component_read(component, WCD939X_DIGITAL_EFUSE_REG_21 + flag_l_r);
+       if (q1 & BIT(7))
+               q1_cal = (10000 - ((q1 & GENMASK(6, 0)) * 10));
+       else
+               q1_cal = (10000 + (q1 * 10));
+
+       if (q1_cal > 0)
+               *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd939x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+                                           u32 *zl, uint32_t *zr)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev);
+       unsigned int reg0, reg1, reg2, reg3, reg4;
+       int z_mono, z_diff1, z_diff2;
+       bool is_fsm_disable = false;
+       s32 z1l, z1r, z1ls;
+
+       reg0 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN5);
+       reg1 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN6);
+       reg2 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN7);
+       reg3 = snd_soc_component_read(component, WCD939X_MBHC_CTL_CLK);
+       reg4 = snd_soc_component_read(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL);
+
+       if (snd_soc_component_read_field(component, WCD939X_ANA_MBHC_ELECT,
+                                        WCD939X_MBHC_ELECT_FSM_EN)) {
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+                                             WCD939X_MBHC_ELECT_FSM_EN, false);
+               is_fsm_disable = true;
+       }
+
+       /* For NO-jack, disable L_DET_EN before Z-det measurements */
+       if (wcd939x->mbhc_cfg.hphl_swh)
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_L_DET_EN, false);
+
+       /* Turn off 100k pull down on HPHL */
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                     WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND,
+                                     false);
+
+       /*
+        * Disable surge protection before impedance detection.
+        * This is done to give correct value for high impedance.
+        */
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false);
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false);
+
+       /* 1ms delay needed after disable surge protection */
+       usleep_range(1000, 1010);
+
+       /* First get impedance on Left */
+       wcd939x_mbhc_zdet_ramp(component, &z1l, NULL);
+       if (z1l == WCD939X_ZDET_FLOATING_IMPEDANCE || z1l > WCD939X_ZDET_VAL_100K) {
+               *zl = WCD939X_ZDET_FLOATING_IMPEDANCE;
+       } else {
+               *zl = z1l / 1000;
+               wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0);
+       }
+       dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+               __func__, *zl);
+
+       /* Start of right impedance ramp and calculation */
+       wcd939x_mbhc_zdet_ramp(component, NULL, &z1r);
+       if (z1r == WCD939X_ZDET_FLOATING_IMPEDANCE || z1r > WCD939X_ZDET_VAL_100K) {
+               *zr = WCD939X_ZDET_FLOATING_IMPEDANCE;
+       } else {
+               *zr = z1r / 1000;
+               wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1);
+       }
+       dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+               __func__, *zr);
+
+       /* Mono/stereo detection */
+       if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE &&
+           *zr == WCD939X_ZDET_FLOATING_IMPEDANCE) {
+               dev_dbg(component->dev,
+                       "%s: plug type is invalid or extension cable\n",
+                       __func__);
+               goto zdet_complete;
+       }
+
+       if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE ||
+           *zr == WCD939X_ZDET_FLOATING_IMPEDANCE ||
+           (*zl < WCD_MONO_HS_MIN_THR && *zr > WCD_MONO_HS_MIN_THR) ||
+           (*zl > WCD_MONO_HS_MIN_THR && *zr < WCD_MONO_HS_MIN_THR)) {
+               dev_dbg(component->dev,
+                       "%s: Mono plug type with one ch floating or shorted to GND\n",
+                       __func__);
+               wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+               goto zdet_complete;
+       }
+
+       snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST,
+                                     WCD939X_R_ATEST_HPH_GND_OVR, true);
+       snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+                                     WCD939X_PA_CTL2_HPHPA_GND_R, true);
+       wcd939x_mbhc_zdet_ramp(component, &z1ls, NULL);
+       snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+                                     WCD939X_PA_CTL2_HPHPA_GND_R, false);
+       snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST,
+                                     WCD939X_R_ATEST_HPH_GND_OVR, false);
+
+       z1ls /= 1000;
+       wcd939x_wcd_mbhc_qfuse_cal(component, &z1ls, 0);
+
+       /* Parallel of left Z and 9 ohm pull down resistor */
+       z_mono = (*zl * 9) / (*zl + 9);
+       z_diff1 = z1ls > z_mono ? z1ls - z_mono : z_mono - z1ls;
+       z_diff2 = *zl > z1ls ? *zl - z1ls : z1ls - *zl;
+       if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + z_mono))) {
+               dev_dbg(component->dev, "%s: stereo plug type detected\n",
+                       __func__);
+               wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_STEREO);
+       } else {
+               dev_dbg(component->dev, "%s: MONO plug type detected\n",
+                       __func__);
+               wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+       }
+
+       /* Enable surge protection again after impedance detection */
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHR, true);
+       snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+                                     WCD939X_EN_EN_SURGE_PROTECTION_HPHL, true);
+
+zdet_complete:
+       snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN5, reg0);
+       snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN6, reg1);
+       snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN7, reg2);
+
+       /* Turn on 100k pull down on HPHL */
+       snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                     WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, true);
+
+       /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+       if (wcd939x->mbhc_cfg.hphl_swh)
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_L_DET_EN, true);
+
+       snd_soc_component_write(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+       snd_soc_component_write(component, WCD939X_MBHC_CTL_CLK, reg3);
+
+       if (is_fsm_disable)
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+                                             WCD939X_MBHC_ELECT_FSM_EN, true);
+}
+
+static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+                                     bool enable)
+{
+       if (enable) {
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN,
+                                             true);
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_GND_DET_EN, true);
+       } else {
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_GND_DET_EN, false);
+               snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+                                             WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN,
+                                             false);
+       }
+}
+
+static void wcd939x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+                                           bool enable)
+{
+       snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+                                     WCD939X_PA_CTL2_HPHPA_GND_R, enable);
+       snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+                                     WCD939X_PA_CTL2_HPHPA_GND_L, enable);
+}
+
+static void wcd939x_mbhc_moisture_config(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) {
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+               return;
+       }
+
+       /* Do not enable moisture detection if jack type is NC */
+       if (!wcd939x->mbhc_cfg.hphl_swh) {
+               dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+                       __func__);
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+               return;
+       }
+
+       snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                     WCD939X_CTL_2_M_RTH_CTL, wcd939x->mbhc_cfg.moist_rref);
+}
+
+static void wcd939x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       if (enable)
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL,
+                                             wcd939x->mbhc_cfg.moist_rref);
+       else
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+}
+
+static bool wcd939x_mbhc_get_moisture_status(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       bool ret = false;
+
+       if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) {
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+               goto done;
+       }
+
+       /* Do not enable moisture detection if jack type is NC */
+       if (!wcd939x->mbhc_cfg.hphl_swh) {
+               dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+                       __func__);
+               snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                             WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+               goto done;
+       }
+
+       /*
+        * If moisture_en is already enabled, then skip to plug type
+        * detection.
+        */
+       if (snd_soc_component_read_field(component, WCD939X_MBHC_NEW_CTL_2,
+                                        WCD939X_CTL_2_M_RTH_CTL))
+               goto done;
+
+       wcd939x_mbhc_moisture_detect_en(component, true);
+
+       /* Read moisture comparator status, invert of status bit */
+       ret = !snd_soc_component_read_field(component, WCD939X_MBHC_NEW_FSM_STATUS,
+                                           WCD939X_FSM_STATUS_HS_M_COMP_STATUS);
+done:
+       return ret;
+}
+
+static void wcd939x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component,
+                                              bool enable)
+{
+       snd_soc_component_write_field(component,
+                                     WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL,
+                                     WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING,
+                                     enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+       .clk_setup = wcd939x_mbhc_clk_setup,
+       .mbhc_bias = wcd939x_mbhc_mbhc_bias_control,
+       .set_btn_thr = wcd939x_mbhc_program_btn_thr,
+       .micbias_enable_status = wcd939x_mbhc_micb_en_status,
+       .hph_pull_up_control_v2 = wcd939x_mbhc_hph_l_pull_up_control,
+       .mbhc_micbias_control = wcd939x_mbhc_request_micbias,
+       .mbhc_micb_ramp_control = wcd939x_mbhc_micb_ramp_control,
+       .mbhc_micb_ctrl_thr_mic = wcd939x_mbhc_micb_ctrl_threshold_mic,
+       .compute_impedance = wcd939x_wcd_mbhc_calc_impedance,
+       .mbhc_gnd_det_ctrl = wcd939x_mbhc_gnd_det_ctrl,
+       .hph_pull_down_ctrl = wcd939x_mbhc_hph_pull_down_ctrl,
+       .mbhc_moisture_config = wcd939x_mbhc_moisture_config,
+       .mbhc_get_moisture_status = wcd939x_mbhc_get_moisture_status,
+       .mbhc_moisture_polling_ctrl = wcd939x_mbhc_moisture_polling_ctrl,
+       .mbhc_moisture_detect_en = wcd939x_mbhc_moisture_detect_en,
+};
+
+static int wcd939x_get_hph_type(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd939x->wcd_mbhc);
+
+       return 0;
+}
+
+static int wcd939x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       bool hphr = mc->shift;
+       u32 zl, zr;
+
+       wcd_mbhc_get_impedance(wcd939x->wcd_mbhc, &zl, &zr);
+       dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+       ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+       SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+                      wcd939x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+       SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+                      wcd939x_hph_impedance_get, NULL),
+       SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+                      wcd939x_hph_impedance_get, NULL),
+};
+
+static int wcd939x_mbhc_init(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       struct wcd_mbhc_intr *intr_ids = &wcd939x->intr_ids;
+
+       intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                    WCD939X_IRQ_MBHC_SW_DET);
+       intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                           WCD939X_IRQ_MBHC_BUTTON_PRESS_DET);
+       intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                             WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET);
+       intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                        WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+       intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                        WCD939X_IRQ_MBHC_ELECT_INS_REM_DET);
+       intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                    WCD939X_IRQ_HPHL_OCP_INT);
+       intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                     WCD939X_IRQ_HPHR_OCP_INT);
+
+       wcd939x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+       if (IS_ERR(wcd939x->wcd_mbhc))
+               return PTR_ERR(wcd939x->wcd_mbhc);
+
+       snd_soc_add_component_controls(component, impedance_detect_controls,
+                                      ARRAY_SIZE(impedance_detect_controls));
+       snd_soc_add_component_controls(component, hph_type_detect_controls,
+                                      ARRAY_SIZE(hph_type_detect_controls));
+
+       return 0;
+}
+
+static void wcd939x_mbhc_deinit(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       wcd_mbhc_deinit(wcd939x->wcd_mbhc);
+}
+
+/* END MBHC */
+
+static const struct snd_kcontrol_new wcd939x_snd_controls[] = {
+       /* RX Path */
+       SOC_SINGLE_EXT("HPHL_COMP Switch", WCD939X_COMP_L, 0, 1, 0,
+                      wcd939x_get_compander, wcd939x_set_compander),
+       SOC_SINGLE_EXT("HPHR_COMP Switch", WCD939X_COMP_R, 1, 1, 0,
+                      wcd939x_get_compander, wcd939x_set_compander),
+       SOC_SINGLE_EXT("HPHL Switch", WCD939X_HPH_L, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("HPHR Switch", WCD939X_HPH_R, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("CLSH Switch", WCD939X_CLSH, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("LO Switch", WCD939X_LO, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DSD_L Switch", WCD939X_DSD_L, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DSD_R Switch", WCD939X_DSD_R, 0, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_TLV("HPHL Volume", WCD939X_HPH_L_EN, 0, 20, 1, line_gain),
+       SOC_SINGLE_TLV("HPHR Volume", WCD939X_HPH_R_EN, 0, 20, 1, line_gain),
+       SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0,
+                      wcd939x_ldoh_get, wcd939x_ldoh_put),
+
+       /* TX Path */
+       SOC_SINGLE_EXT("ADC1 Switch", WCD939X_ADC1, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("ADC2 Switch", WCD939X_ADC2, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("ADC3 Switch", WCD939X_ADC3, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("ADC4 Switch", WCD939X_ADC4, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC0 Switch", WCD939X_DMIC0, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC1 Switch", WCD939X_DMIC1, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("MBHC Switch", WCD939X_MBHC, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC2 Switch", WCD939X_DMIC2, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC3 Switch", WCD939X_DMIC3, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC4 Switch", WCD939X_DMIC4, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC5 Switch", WCD939X_DMIC5, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC6 Switch", WCD939X_DMIC6, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_EXT("DMIC7 Switch", WCD939X_DMIC7, 1, 1, 0,
+                      wcd939x_get_swr_port, wcd939x_set_swr_port),
+       SOC_SINGLE_TLV("ADC1 Volume", WCD939X_ANA_TX_CH1, 0, 20, 0,
+                      analog_gain),
+       SOC_SINGLE_TLV("ADC2 Volume", WCD939X_ANA_TX_CH2, 0, 20, 0,
+                      analog_gain),
+       SOC_SINGLE_TLV("ADC3 Volume", WCD939X_ANA_TX_CH3, 0, 20, 0,
+                      analog_gain),
+       SOC_SINGLE_TLV("ADC4 Volume", WCD939X_ANA_TX_CH4, 0, 20, 0,
+                      analog_gain),
+};
+
+static const struct snd_soc_dapm_widget wcd939x_dapm_widgets[] = {
+       /*input widgets*/
+       SND_SOC_DAPM_INPUT("AMIC1"),
+       SND_SOC_DAPM_INPUT("AMIC2"),
+       SND_SOC_DAPM_INPUT("AMIC3"),
+       SND_SOC_DAPM_INPUT("AMIC4"),
+       SND_SOC_DAPM_INPUT("AMIC5"),
+
+       SND_SOC_DAPM_MIC("Analog Mic1", NULL),
+       SND_SOC_DAPM_MIC("Analog Mic2", NULL),
+       SND_SOC_DAPM_MIC("Analog Mic3", NULL),
+       SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+       SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+
+       /* TX widgets */
+       SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0,
+                          wcd939x_codec_enable_adc,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0,
+                          wcd939x_codec_enable_adc,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0,
+                          wcd939x_codec_enable_adc,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0,
+                          wcd939x_codec_enable_adc,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0,
+                          wcd939x_codec_enable_dmic,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, NULL, 0,
+                            wcd939x_adc_enable_req,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, NULL, 0,
+                            wcd939x_adc_enable_req,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, NULL, 0,
+                            wcd939x_adc_enable_req,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0,
+                            wcd939x_adc_enable_req,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux),
+       SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+       SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux),
+       SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux),
+
+       /* tx mixers */
+       SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
+                            adc1_switch, ARRAY_SIZE(adc1_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0,
+                            adc2_switch, ARRAY_SIZE(adc2_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0,
+                            adc3_switch, ARRAY_SIZE(adc3_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0,
+                            adc4_switch, ARRAY_SIZE(adc4_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0,
+                            dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd939x_tx_swr_ctrl,
+                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* micbias widgets */
+       SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+                           wcd939x_codec_enable_micbias,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+                           wcd939x_codec_enable_micbias,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+                           wcd939x_codec_enable_micbias,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+                           wcd939x_codec_enable_micbias,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+
+       /* micbias pull up widgets */
+       SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+                           wcd939x_codec_enable_micbias_pullup,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+                           wcd939x_codec_enable_micbias_pullup,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+                           wcd939x_codec_enable_micbias_pullup,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+                           wcd939x_codec_enable_micbias_pullup,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+
+       /* output widgets tx */
+       SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"),
+       SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"),
+
+       SND_SOC_DAPM_INPUT("IN1_HPHL"),
+       SND_SOC_DAPM_INPUT("IN2_HPHR"),
+       SND_SOC_DAPM_INPUT("IN3_EAR"),
+
+       /* rx widgets */
+       SND_SOC_DAPM_PGA_E("EAR PGA", WCD939X_ANA_EAR, 7, 0, NULL, 0,
+                          wcd939x_codec_enable_ear_pa,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_PGA_E("HPHL PGA", WCD939X_ANA_HPH, 7, 0, NULL, 0,
+                          wcd939x_codec_enable_hphl_pa,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_PGA_E("HPHR PGA", WCD939X_ANA_HPH, 6, 0, NULL, 0,
+                          wcd939x_codec_enable_hphr_pa,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0,
+                          wcd939x_codec_hphl_dac_event,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0,
+                          wcd939x_codec_hphr_dac_event,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0,
+                          wcd939x_codec_ear_dac_event,
+                          SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux),
+
+       SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0,
+                           wcd939x_codec_enable_rxclk,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+       /* rx mixer widgets */
+       SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0,
+                          ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)),
+       SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0,
+                          hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)),
+       SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0,
+                          hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)),
+
+       /* output widgets rx */
+       SND_SOC_DAPM_OUTPUT("EAR"),
+       SND_SOC_DAPM_OUTPUT("HPHL"),
+       SND_SOC_DAPM_OUTPUT("HPHR"),
+};
+
+static const struct snd_soc_dapm_route wcd939x_audio_map[] = {
+       /* TX Path */
+       {"ADC1_OUTPUT", NULL, "ADC1_MIXER"},
+       {"ADC1_MIXER", "Switch", "ADC1 REQ"},
+       {"ADC1 REQ", NULL, "ADC1"},
+       {"ADC1", NULL, "ADC1 MUX"},
+       {"ADC1 MUX", "CH1_AMIC1", "AMIC1"},
+       {"ADC1 MUX", "CH1_AMIC2", "AMIC2"},
+       {"ADC1 MUX", "CH1_AMIC3", "AMIC3"},
+       {"ADC1 MUX", "CH1_AMIC4", "AMIC4"},
+       {"ADC1 MUX", "CH1_AMIC5", "AMIC5"},
+
+       {"ADC2_OUTPUT", NULL, "ADC2_MIXER"},
+       {"ADC2_MIXER", "Switch", "ADC2 REQ"},
+       {"ADC2 REQ", NULL, "ADC2"},
+       {"ADC2", NULL, "ADC2 MUX"},
+       {"ADC2 MUX", "CH2_AMIC1", "AMIC1"},
+       {"ADC2 MUX", "CH2_AMIC2", "AMIC2"},
+       {"ADC2 MUX", "CH2_AMIC3", "AMIC3"},
+       {"ADC2 MUX", "CH2_AMIC4", "AMIC4"},
+       {"ADC2 MUX", "CH2_AMIC5", "AMIC5"},
+
+       {"ADC3_OUTPUT", NULL, "ADC3_MIXER"},
+       {"ADC3_MIXER", "Switch", "ADC3 REQ"},
+       {"ADC3 REQ", NULL, "ADC3"},
+       {"ADC3", NULL, "ADC3 MUX"},
+       {"ADC3 MUX", "CH3_AMIC1", "AMIC1"},
+       {"ADC3 MUX", "CH3_AMIC3", "AMIC3"},
+       {"ADC3 MUX", "CH3_AMIC4", "AMIC4"},
+       {"ADC3 MUX", "CH3_AMIC5", "AMIC5"},
+
+       {"ADC4_OUTPUT", NULL, "ADC4_MIXER"},
+       {"ADC4_MIXER", "Switch", "ADC4 REQ"},
+       {"ADC4 REQ", NULL, "ADC4"},
+       {"ADC4", NULL, "ADC4 MUX"},
+       {"ADC4 MUX", "CH4_AMIC1", "AMIC1"},
+       {"ADC4 MUX", "CH4_AMIC3", "AMIC3"},
+       {"ADC4 MUX", "CH4_AMIC4", "AMIC4"},
+       {"ADC4 MUX", "CH4_AMIC5", "AMIC5"},
+
+       {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"},
+       {"DMIC1_MIXER", "Switch", "DMIC1"},
+
+       {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"},
+       {"DMIC2_MIXER", "Switch", "DMIC2"},
+
+       {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"},
+       {"DMIC3_MIXER", "Switch", "DMIC3"},
+
+       {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"},
+       {"DMIC4_MIXER", "Switch", "DMIC4"},
+
+       {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"},
+       {"DMIC5_MIXER", "Switch", "DMIC5"},
+
+       {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"},
+       {"DMIC6_MIXER", "Switch", "DMIC6"},
+
+       {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"},
+       {"DMIC7_MIXER", "Switch", "DMIC7"},
+
+       {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"},
+       {"DMIC8_MIXER", "Switch", "DMIC8"},
+
+       /* RX Path */
+       {"IN1_HPHL", NULL, "VDD_BUCK"},
+       {"IN1_HPHL", NULL, "CLS_H_PORT"},
+
+       {"RX1", NULL, "IN1_HPHL"},
+       {"RX1", NULL, "RXCLK"},
+       {"RDAC1", NULL, "RX1"},
+       {"HPHL_RDAC", "Switch", "RDAC1"},
+       {"HPHL PGA", NULL, "HPHL_RDAC"},
+       {"HPHL", NULL, "HPHL PGA"},
+
+       {"IN2_HPHR", NULL, "VDD_BUCK"},
+       {"IN2_HPHR", NULL, "CLS_H_PORT"},
+       {"RX2", NULL, "IN2_HPHR"},
+       {"RDAC2", NULL, "RX2"},
+       {"RX2", NULL, "RXCLK"},
+       {"HPHR_RDAC", "Switch", "RDAC2"},
+       {"HPHR PGA", NULL, "HPHR_RDAC"},
+       {"HPHR", NULL, "HPHR PGA"},
+
+       {"IN3_EAR", NULL, "VDD_BUCK"},
+       {"RX3", NULL, "IN3_EAR"},
+       {"RX3", NULL, "RXCLK"},
+
+       {"RDAC3_MUX", "RX3", "RX3"},
+       {"RDAC3_MUX", "RX1", "RX1"},
+       {"RDAC3", NULL, "RDAC3_MUX"},
+       {"EAR_RDAC", "Switch", "RDAC3"},
+       {"EAR PGA", NULL, "EAR_RDAC"},
+       {"EAR", NULL, "EAR PGA"},
+};
+
+static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x)
+{
+       int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
+
+       /* set micbias voltage */
+       vout_ctl_1 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb1_mv);
+       vout_ctl_2 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb2_mv);
+       vout_ctl_3 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb3_mv);
+       vout_ctl_4 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb4_mv);
+       if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0)
+               return -EINVAL;
+
+       regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1,
+                          WCD939X_MICB1_VOUT_CTL, vout_ctl_1);
+       regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2,
+                          WCD939X_MICB2_VOUT_CTL, vout_ctl_2);
+       regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3,
+                          WCD939X_MICB3_VOUT_CTL, vout_ctl_3);
+       regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4,
+                          WCD939X_MICB4_VOUT_CTL, vout_ctl_4);
+
+       return 0;
+}
+
+static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data)
+{
+       /*
+        * HPHR/HPHL/EAR Watchdog interrupt threaded handler
+        *
+        * Watchdog interrupts are expected to be enabled when switching
+        * on the HPHL/R and EAR RX PGA in order to make sure the interrupts
+        * are acked by the regmap_irq handler to allow PDM sync.
+        * We could leave those interrupts masked but we would not have
+        * any valid way to enable/disable them without violating irq layers.
+        *
+        * The HPHR/HPHL/EAR Watchdog interrupts are handled
+        * by regmap_irq, so requesting a threaded handler is the
+        * safest way to be able to ack those interrupts without
+        * colliding with the regmap_irq setup.
+        */
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Setup a virtual interrupt domain to hook regmap_irq
+ * The root domain will have a single interrupt which mapping
+ * will trigger the regmap_irq handler.
+ *
+ * root:
+ *   wcd_irq_chip
+ *     [0] wcd939x_regmap_irq_chip
+ *       [0] MBHC_BUTTON_PRESS_DET
+ *       [1] MBHC_BUTTON_RELEASE_DET
+ *       ...
+ *       [16] HPHR_SURGE_DET_INT
+ *
+ * Interrupt trigger:
+ *   soundwire_interrupt_callback()
+ *   \-handle_nested_irq(0)
+ *     \- regmap_irq_thread()
+ *         \- handle_nested_irq(i)
+ */
+static struct irq_chip wcd_irq_chip = {
+       .name = "WCD939x",
+};
+
+static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+                           irq_hw_number_t hw)
+{
+       irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
+       irq_set_nested_thread(virq, 1);
+       irq_set_noprobe(virq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops wcd_domain_ops = {
+       .map = wcd_irq_chip_map,
+};
+
+static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev)
+{
+       wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL);
+       if (!(wcd->virq)) {
+               dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+               return -EINVAL;
+       }
+
+       return devm_regmap_add_irq_chip(dev, wcd->regmap,
+                                       irq_create_mapping(wcd->virq, 0),
+                                       IRQF_ONESHOT, 0, &wcd939x_regmap_irq_chip,
+                                       &wcd->irq_chip);
+}
+
+static int wcd939x_soc_codec_probe(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+       struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev;
+       struct device *dev = component->dev;
+       unsigned long time_left;
+       int ret, i;
+
+       time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+                                               msecs_to_jiffies(2000));
+       if (!time_left) {
+               dev_err(dev, "soundwire device init timeout\n");
+               return -ETIMEDOUT;
+       }
+
+       snd_soc_component_init_regmap(component, wcd939x->regmap);
+
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret < 0)
+               return ret;
+
+       wcd939x->variant = snd_soc_component_read_field(component,
+                                                       WCD939X_DIGITAL_EFUSE_REG_0,
+                                                       WCD939X_EFUSE_REG_0_WCD939X_ID);
+
+       wcd939x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD939X);
+       if (IS_ERR(wcd939x->clsh_info)) {
+               pm_runtime_put(dev);
+               return PTR_ERR(wcd939x->clsh_info);
+       }
+
+       wcd939x_io_init(component);
+
+       /* Set all interrupts as edge triggered */
+       for (i = 0; i < wcd939x_regmap_irq_chip.num_regs; i++)
+               regmap_write(wcd939x->regmap,
+                            (WCD939X_DIGITAL_INTR_LEVEL_0 + i), 0);
+
+       pm_runtime_put(dev);
+
+       /* Request for watchdog interrupt */
+       wcd939x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                      WCD939X_IRQ_HPHR_PDM_WD_INT);
+       wcd939x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                      WCD939X_IRQ_HPHL_PDM_WD_INT);
+       wcd939x->ear_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+                                                     WCD939X_IRQ_EAR_PDM_WD_INT);
+
+       ret = request_threaded_irq(wcd939x->hphr_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+                                  IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+                                  "HPHR PDM WD INT", wcd939x);
+       if (ret) {
+               dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret);
+               goto err_free_clsh_ctrl;
+       }
+
+       ret = request_threaded_irq(wcd939x->hphl_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+                                  IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+                                  "HPHL PDM WD INT", wcd939x);
+       if (ret) {
+               dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret);
+               goto err_free_hphr_pdm_wd_int;
+       }
+
+       ret = request_threaded_irq(wcd939x->ear_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+                                  IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+                                  "AUX PDM WD INT", wcd939x);
+       if (ret) {
+               dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret);
+               goto err_free_hphl_pdm_wd_int;
+       }
+
+       /* Disable watchdog interrupt for HPH and AUX */
+       disable_irq_nosync(wcd939x->hphr_pdm_wd_int);
+       disable_irq_nosync(wcd939x->hphl_pdm_wd_int);
+       disable_irq_nosync(wcd939x->ear_pdm_wd_int);
+
+       switch (wcd939x->variant) {
+       case WCD9390:
+               ret = snd_soc_add_component_controls(component, wcd9390_snd_controls,
+                                                    ARRAY_SIZE(wcd9390_snd_controls));
+               if (ret < 0) {
+                       dev_err(component->dev,
+                               "%s: Failed to add snd ctrls for variant: %d\n",
+                               __func__, wcd939x->variant);
+                       goto err_free_ear_pdm_wd_int;
+               }
+               break;
+       case WCD9395:
+               ret = snd_soc_add_component_controls(component, wcd9395_snd_controls,
+                                                    ARRAY_SIZE(wcd9395_snd_controls));
+               if (ret < 0) {
+                       dev_err(component->dev,
+                               "%s: Failed to add snd ctrls for variant: %d\n",
+                               __func__, wcd939x->variant);
+                       goto err_free_ear_pdm_wd_int;
+               }
+               break;
+       default:
+               break;
+       }
+
+       ret = wcd939x_mbhc_init(component);
+       if (ret) {
+               dev_err(component->dev,  "mbhc initialization failed\n");
+               goto err_free_ear_pdm_wd_int;
+       }
+
+       return 0;
+
+err_free_ear_pdm_wd_int:
+       free_irq(wcd939x->ear_pdm_wd_int, wcd939x);
+err_free_hphl_pdm_wd_int:
+       free_irq(wcd939x->hphl_pdm_wd_int, wcd939x);
+err_free_hphr_pdm_wd_int:
+       free_irq(wcd939x->hphr_pdm_wd_int, wcd939x);
+err_free_clsh_ctrl:
+       wcd_clsh_ctrl_free(wcd939x->clsh_info);
+
+       return ret;
+}
+
+static void wcd939x_soc_codec_remove(struct snd_soc_component *component)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       wcd939x_mbhc_deinit(component);
+
+       free_irq(wcd939x->ear_pdm_wd_int, wcd939x);
+       free_irq(wcd939x->hphl_pdm_wd_int, wcd939x);
+       free_irq(wcd939x->hphr_pdm_wd_int, wcd939x);
+
+       wcd_clsh_ctrl_free(wcd939x->clsh_info);
+}
+
+static int wcd939x_codec_set_jack(struct snd_soc_component *comp,
+                                 struct snd_soc_jack *jack, void *data)
+{
+       struct wcd939x_priv *wcd = dev_get_drvdata(comp->dev);
+
+       if (jack)
+               return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack);
+
+       wcd_mbhc_stop(wcd->wcd_mbhc);
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_wcd939x = {
+       .name = "wcd939x_codec",
+       .probe = wcd939x_soc_codec_probe,
+       .remove = wcd939x_soc_codec_remove,
+       .controls = wcd939x_snd_controls,
+       .num_controls = ARRAY_SIZE(wcd939x_snd_controls),
+       .dapm_widgets = wcd939x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wcd939x_dapm_widgets),
+       .dapm_routes = wcd939x_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(wcd939x_audio_map),
+       .set_jack = wcd939x_codec_set_jack,
+       .endianness = 1,
+};
+
+#if IS_ENABLED(CONFIG_TYPEC)
+/* Get USB-C plug orientation to provide swap event for MBHC */
+static int wcd939x_typec_switch_set(struct typec_switch_dev *sw,
+                                   enum typec_orientation orientation)
+{
+       struct wcd939x_priv *wcd939x = typec_switch_get_drvdata(sw);
+
+       wcd939x->typec_orientation = orientation;
+
+       return 0;
+}
+
+static int wcd939x_typec_mux_set(struct typec_mux_dev *mux,
+                                struct typec_mux_state *state)
+{
+       struct wcd939x_priv *wcd939x = typec_mux_get_drvdata(mux);
+       unsigned int previous_mode = wcd939x->typec_mode;
+
+       if (!wcd939x->wcd_mbhc)
+               return -EINVAL;
+
+       if (wcd939x->typec_mode != state->mode) {
+               wcd939x->typec_mode = state->mode;
+
+               if (wcd939x->typec_mode == TYPEC_MODE_AUDIO)
+                       return wcd_mbhc_typec_report_plug(wcd939x->wcd_mbhc);
+               else if (previous_mode == TYPEC_MODE_AUDIO)
+                       return wcd_mbhc_typec_report_unplug(wcd939x->wcd_mbhc);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_TYPEC */
+
+static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_priv *wcd)
+{
+       struct device_node *np = dev->of_node;
+       u32 prop_val = 0;
+       int rc = 0;
+
+       rc = of_property_read_u32(np, "qcom,micbias1-microvolt",  &prop_val);
+       if (!rc)
+               wcd->micb1_mv = prop_val / 1000;
+       else
+               dev_info(dev, "%s: Micbias1 DT property not found\n", __func__);
+
+       rc = of_property_read_u32(np, "qcom,micbias2-microvolt",  &prop_val);
+       if (!rc)
+               wcd->micb2_mv = prop_val / 1000;
+       else
+               dev_info(dev, "%s: Micbias2 DT property not found\n", __func__);
+
+       rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
+       if (!rc)
+               wcd->micb3_mv = prop_val / 1000;
+       else
+               dev_info(dev, "%s: Micbias3 DT property not found\n", __func__);
+
+       rc = of_property_read_u32(np, "qcom,micbias4-microvolt",  &prop_val);
+       if (!rc)
+               wcd->micb4_mv = prop_val / 1000;
+       else
+               dev_info(dev, "%s: Micbias4 DT property not found\n", __func__);
+}
+
+#if IS_ENABLED(CONFIG_TYPEC)
+static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active)
+{
+       struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+       if (!wcd939x->typec_analog_mux || !wcd939x->typec_switch)
+               return false;
+
+       /* Report inversion via Type Switch of USBSS */
+       typec_switch_set(wcd939x->typec_switch,
+                        wcd939x->typec_orientation == TYPEC_ORIENTATION_REVERSE ?
+                               TYPEC_ORIENTATION_NORMAL : TYPEC_ORIENTATION_REVERSE);
+
+       return true;
+}
+#endif /* CONFIG_TYPEC */
+
+static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device *dev)
+{
+       struct wcd_mbhc_config *cfg = &wcd939x->mbhc_cfg;
+#if IS_ENABLED(CONFIG_TYPEC)
+       struct device_node *np;
+#endif /* CONFIG_TYPEC */
+       int ret;
+
+       wcd939x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
+       if (wcd939x->reset_gpio < 0)
+               return dev_err_probe(dev, wcd939x->reset_gpio,
+                                    "Failed to get reset gpio\n");
+
+       wcd939x->supplies[0].supply = "vdd-rxtx";
+       wcd939x->supplies[1].supply = "vdd-io";
+       wcd939x->supplies[2].supply = "vdd-buck";
+       wcd939x->supplies[3].supply = "vdd-mic-bias";
+
+       ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+       ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+       if (ret) {
+               regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+               return dev_err_probe(dev, ret, "Failed to enable supplies\n");
+       }
+
+       wcd939x_dt_parse_micbias_info(dev, wcd939x);
+
+       cfg->mbhc_micbias = MIC_BIAS_2;
+       cfg->anc_micbias = MIC_BIAS_2;
+       cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+       cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS;
+       cfg->micb_mv = wcd939x->micb2_mv;
+       cfg->linein_th = 5000;
+       cfg->hs_thr = 1700;
+       cfg->hph_thr = 50;
+
+       wcd_dt_parse_mbhc_data(dev, cfg);
+
+#if IS_ENABLED(CONFIG_TYPEC)
+       /*
+        * Is node has a port and a valid remote endpoint
+        * consider HP lines are connected to the USBSS part
+        */
+       np = of_graph_get_remote_node(dev->of_node, 0, 0);
+       if (np) {
+               wcd939x->typec_analog_mux = true;
+               cfg->typec_analog_mux = true;
+               cfg->swap_gnd_mic = wcd939x_swap_gnd_mic;
+       }
+#endif /* CONFIG_TYPEC */
+
+       return 0;
+}
+
+static int wcd939x_reset(struct wcd939x_priv *wcd939x)
+{
+       gpio_direction_output(wcd939x->reset_gpio, 0);
+       /* 20us sleep required after pulling the reset gpio to LOW */
+       usleep_range(20, 30);
+       gpio_set_value(wcd939x->reset_gpio, 1);
+       /* 20us sleep required after pulling the reset gpio to HIGH */
+       usleep_range(20, 30);
+
+       return 0;
+}
+
+static int wcd939x_codec_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+       return wcd939x_sdw_hw_params(wcd, substream, params, dai);
+}
+
+static int wcd939x_codec_free(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+       return wcd939x_sdw_free(wcd, substream, dai);
+}
+
+static int wcd939x_codec_set_sdw_stream(struct snd_soc_dai *dai,
+                                       void *stream, int direction)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+       struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+       return wcd939x_sdw_set_sdw_stream(wcd, dai, stream, direction);
+}
+
+static const struct snd_soc_dai_ops wcd939x_sdw_dai_ops = {
+       .hw_params = wcd939x_codec_hw_params,
+       .hw_free = wcd939x_codec_free,
+       .set_stream = wcd939x_codec_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver wcd939x_dais[] = {
+       [0] = {
+               .name = "wcd939x-sdw-rx",
+               .playback = {
+                       .stream_name = "WCD AIF1 Playback",
+                       .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK,
+                       .formats = WCD939X_FORMATS,
+                       .rate_max = 384000,
+                       .rate_min = 8000,
+                       .channels_min = 1,
+                       .channels_max = 2,
+               },
+               .ops = &wcd939x_sdw_dai_ops,
+       },
+       [1] = {
+               .name = "wcd939x-sdw-tx",
+               .capture = {
+                       .stream_name = "WCD AIF1 Capture",
+                       .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK,
+                       .formats = WCD939X_FORMATS,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .channels_min = 1,
+                       .channels_max = 4,
+               },
+               .ops = &wcd939x_sdw_dai_ops,
+       },
+};
+
+static int wcd939x_bind(struct device *dev)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
+       unsigned int version, id1, status1;
+       int ret;
+
+#if IS_ENABLED(CONFIG_TYPEC)
+       /*
+        * Get USBSS type-c switch to send gnd/mic swap events
+        * typec_switch is fetched now to avoid a probe deadlock since
+        * the USBSS depends on the typec_mux register in wcd939x_probe()
+        */
+       if (wcd939x->typec_analog_mux) {
+               wcd939x->typec_switch = fwnode_typec_switch_get(dev->fwnode);
+               if (IS_ERR(wcd939x->typec_switch))
+                       return dev_err_probe(dev, PTR_ERR(wcd939x->typec_switch),
+                                            "failed to acquire orientation-switch\n");
+       }
+#endif /* CONFIG_TYPEC */
+
+       ret = component_bind_all(dev, wcd939x);
+       if (ret) {
+               dev_err(dev, "%s: Slave bind failed, ret = %d\n",
+                       __func__, ret);
+               goto err_put_typec_switch;
+       }
+
+       wcd939x->rxdev = wcd939x_sdw_device_get(wcd939x->rxnode);
+       if (!wcd939x->rxdev) {
+               dev_err(dev, "could not find slave with matching of node\n");
+               ret = -EINVAL;
+               goto err_unbind;
+       }
+       wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev);
+       wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x;
+
+       wcd939x->txdev = wcd939x_sdw_device_get(wcd939x->txnode);
+       if (!wcd939x->txdev) {
+               dev_err(dev, "could not find txslave with matching of node\n");
+               ret = -EINVAL;
+               goto err_put_rxdev;
+       }
+       wcd939x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd939x->txdev);
+       wcd939x->sdw_priv[AIF1_CAP]->wcd939x = wcd939x;
+       wcd939x->tx_sdw_dev = dev_to_sdw_dev(wcd939x->txdev);
+
+       /*
+        * As TX is main CSR reg interface, which should not be suspended first.
+        * explicitly add the dependency link
+        */
+       if (!device_link_add(wcd939x->rxdev, wcd939x->txdev, DL_FLAG_STATELESS |
+                           DL_FLAG_PM_RUNTIME)) {
+               dev_err(dev, "could not devlink tx and rx\n");
+               ret = -EINVAL;
+               goto err_put_txdev;
+       }
+
+       if (!device_link_add(dev, wcd939x->txdev, DL_FLAG_STATELESS |
+                                       DL_FLAG_PM_RUNTIME)) {
+               dev_err(dev, "could not devlink wcd and tx\n");
+               ret = -EINVAL;
+               goto err_remove_rxtx_link;
+       }
+
+       if (!device_link_add(dev, wcd939x->rxdev, DL_FLAG_STATELESS |
+                                       DL_FLAG_PM_RUNTIME)) {
+               dev_err(dev, "could not devlink wcd and rx\n");
+               ret = -EINVAL;
+               goto err_remove_tx_link;
+       }
+
+       /* Get regmap from TX SoundWire device */
+       wcd939x->regmap = wcd939x_swr_get_regmap(wcd939x->sdw_priv[AIF1_CAP]);
+       if (IS_ERR(wcd939x->regmap)) {
+               dev_err(dev, "could not get TX device regmap\n");
+               ret = PTR_ERR(wcd939x->regmap);
+               goto err_remove_rx_link;
+       }
+
+       ret = wcd939x_irq_init(wcd939x, dev);
+       if (ret) {
+               dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret);
+               goto err_remove_rx_link;
+       }
+
+       wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq;
+       wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq;
+
+       ret = wcd939x_set_micbias_data(wcd939x);
+       if (ret < 0) {
+               dev_err(dev, "%s: bad micbias pdata\n", __func__);
+               goto err_remove_rx_link;
+       }
+
+       /* Check WCD9395 version */
+       regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1);
+       regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1);
+
+       if (id1 == 0)
+               version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0);
+       else
+               version = WCD939X_VERSION_2_0;
+
+       dev_dbg(dev, "wcd939x version: %s\n", version_to_str(version));
+
+       ret = snd_soc_register_component(dev, &soc_codec_dev_wcd939x,
+                                        wcd939x_dais, ARRAY_SIZE(wcd939x_dais));
+       if (ret) {
+               dev_err(dev, "%s: Codec registration failed\n",
+                       __func__);
+               goto err_remove_rx_link;
+       }
+
+       return 0;
+
+err_remove_rx_link:
+       device_link_remove(dev, wcd939x->rxdev);
+err_remove_tx_link:
+       device_link_remove(dev, wcd939x->txdev);
+err_remove_rxtx_link:
+       device_link_remove(wcd939x->rxdev, wcd939x->txdev);
+err_put_txdev:
+       put_device(wcd939x->txdev);
+err_put_rxdev:
+       put_device(wcd939x->rxdev);
+err_unbind:
+       component_unbind_all(dev, wcd939x);
+err_put_typec_switch:
+#if IS_ENABLED(CONFIG_TYPEC)
+       if (wcd939x->typec_analog_mux)
+               typec_switch_put(wcd939x->typec_switch);
+#endif /* CONFIG_TYPEC */
+
+       return ret;
+}
+
+static void wcd939x_unbind(struct device *dev)
+{
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
+
+       snd_soc_unregister_component(dev);
+       device_link_remove(dev, wcd939x->txdev);
+       device_link_remove(dev, wcd939x->rxdev);
+       device_link_remove(wcd939x->rxdev, wcd939x->txdev);
+       put_device(wcd939x->txdev);
+       put_device(wcd939x->rxdev);
+       component_unbind_all(dev, wcd939x);
+}
+
+static const struct component_master_ops wcd939x_comp_ops = {
+       .bind   = wcd939x_bind,
+       .unbind = wcd939x_unbind,
+};
+
+static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x,
+                                       struct device *dev,
+                                       struct component_match **matchptr)
+{
+       struct device_node *np = dev->of_node;
+
+       wcd939x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+       if (!wcd939x->rxnode) {
+               dev_err(dev, "%s: Rx-device node not defined\n", __func__);
+               return -ENODEV;
+       }
+
+       of_node_get(wcd939x->rxnode);
+       component_match_add_release(dev, matchptr, component_release_of,
+                                   component_compare_of, wcd939x->rxnode);
+
+       wcd939x->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+       if (!wcd939x->txnode) {
+               dev_err(dev, "%s: Tx-device node not defined\n", __func__);
+               return -ENODEV;
+       }
+       of_node_get(wcd939x->txnode);
+       component_match_add_release(dev, matchptr, component_release_of,
+                                   component_compare_of, wcd939x->txnode);
+       return 0;
+}
+
+static int wcd939x_probe(struct platform_device *pdev)
+{
+       struct component_match *match = NULL;
+       struct wcd939x_priv *wcd939x = NULL;
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       wcd939x = devm_kzalloc(dev, sizeof(struct wcd939x_priv),
+                              GFP_KERNEL);
+       if (!wcd939x)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, wcd939x);
+       mutex_init(&wcd939x->micb_lock);
+
+       ret = wcd939x_populate_dt_data(wcd939x, dev);
+       if (ret) {
+               dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
+               return -EINVAL;
+       }
+
+#if IS_ENABLED(CONFIG_TYPEC)
+       /*
+        * Is USBSS is used to mux analog lines,
+        * register a typec mux/switch to get typec events
+        */
+       if (wcd939x->typec_analog_mux) {
+               struct typec_mux_desc mux_desc = {
+                       .drvdata = wcd939x,
+                       .fwnode = dev_fwnode(dev),
+                       .set = wcd939x_typec_mux_set,
+               };
+               struct typec_switch_desc sw_desc = {
+                       .drvdata = wcd939x,
+                       .fwnode = dev_fwnode(dev),
+                       .set = wcd939x_typec_switch_set,
+               };
+
+               wcd939x->typec_mux = typec_mux_register(dev, &mux_desc);
+               if (IS_ERR(wcd939x->typec_mux)) {
+                       ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_mux),
+                                           "failed to register typec mux\n");
+                       goto err_disable_regulators;
+               }
+
+               wcd939x->typec_sw = typec_switch_register(dev, &sw_desc);
+               if (IS_ERR(wcd939x->typec_sw)) {
+                       ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_sw),
+                                           "failed to register typec switch\n");
+                       goto err_unregister_typec_mux;
+               }
+       }
+#endif /* CONFIG_TYPEC */
+
+       ret = wcd939x_add_slave_components(wcd939x, dev, &match);
+       if (ret)
+               goto err_unregister_typec_switch;
+
+       wcd939x_reset(wcd939x);
+
+       ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match);
+       if (ret)
+               goto err_disable_regulators;
+
+       pm_runtime_set_autosuspend_delay(dev, 1000);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_idle(dev);
+
+       return 0;
+
+#if IS_ENABLED(CONFIG_TYPEC)
+err_unregister_typec_mux:
+       if (wcd939x->typec_analog_mux)
+               typec_mux_unregister(wcd939x->typec_mux);
+#endif /* CONFIG_TYPEC */
+
+err_unregister_typec_switch:
+#if IS_ENABLED(CONFIG_TYPEC)
+       if (wcd939x->typec_analog_mux)
+               typec_switch_unregister(wcd939x->typec_sw);
+#endif /* CONFIG_TYPEC */
+
+err_disable_regulators:
+       regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+       regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+
+       return ret;
+}
+
+static void wcd939x_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
+
+       component_master_del(dev, &wcd939x_comp_ops);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_dont_use_autosuspend(dev);
+
+       regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+       regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id wcd939x_dt_match[] = {
+       { .compatible = "qcom,wcd9390-codec" },
+       { .compatible = "qcom,wcd9395-codec" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, wcd939x_dt_match);
+#endif
+
+static struct platform_driver wcd939x_codec_driver = {
+       .probe = wcd939x_probe,
+       .remove_new = wcd939x_remove,
+       .driver = {
+               .name = "wcd939x_codec",
+               .of_match_table = of_match_ptr(wcd939x_dt_match),
+               .suppress_bind_attrs = true,
+       },
+};
+
+module_platform_driver(wcd939x_codec_driver);
+MODULE_DESCRIPTION("WCD939X Codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h
new file mode 100644 (file)
index 0000000..807cf31
--- /dev/null
@@ -0,0 +1,989 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __WCD939X_H__
+#define __WCD939X_H__
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define WCD939X_BASE                                      (0x3000)
+#define WCD939X_ANA_PAGE                                  (0x3000)
+#define WCD939X_ANA_BIAS                                  (0x3001)
+#define WCD939X_BIAS_ANALOG_BIAS_EN    BIT(7)
+#define WCD939X_BIAS_PRECHRG_EN        BIT(6)
+#define WCD939X_BIAS_PRECHRG_CTL_MODE  BIT(5)
+#define WCD939X_ANA_RX_SUPPLIES                                   (0x3008)
+#define WCD939X_RX_SUPPLIES_VPOS_EN    BIT(7)
+#define WCD939X_RX_SUPPLIES_VNEG_EN    BIT(6)
+#define WCD939X_RX_SUPPLIES_VPOS_PWR_LVL       BIT(3)
+#define WCD939X_RX_SUPPLIES_VNEG_PWR_LVL       BIT(2)
+#define WCD939X_RX_SUPPLIES_REGULATOR_MODE     BIT(1)
+#define WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE     BIT(0)
+#define WCD939X_ANA_HPH                                           (0x3009)
+#define WCD939X_HPH_HPHL_ENABLE        BIT(7)
+#define WCD939X_HPH_HPHR_ENABLE        BIT(6)
+#define WCD939X_HPH_HPHL_REF_ENABLE    BIT(5)
+#define WCD939X_HPH_HPHR_REF_ENABLE    BIT(4)
+#define WCD939X_HPH_PWR_LEVEL  GENMASK(3, 2)
+#define WCD939X_ANA_EAR                                           (0x300a)
+#define WCD939X_ANA_EAR_COMPANDER_CTL                     (0x300b)
+#define WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG        BIT(7)
+#define WCD939X_EAR_COMPANDER_CTL_EAR_GAIN     GENMASK(6, 2)
+#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_BYP BIT(1)
+#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_CLK_EDGE    BIT(0)
+#define WCD939X_ANA_TX_CH1                                (0x300e)
+#define WCD939X_ANA_TX_CH2                                (0x300f)
+#define WCD939X_TX_CH2_ENABLE  BIT(7)
+#define WCD939X_TX_CH2_HPF1_INIT       BIT(6)
+#define WCD939X_TX_CH2_HPF2_INIT       BIT(5)
+#define WCD939X_TX_CH2_GAIN    GENMASK(4, 0)
+#define WCD939X_ANA_TX_CH3                                (0x3010)
+#define WCD939X_ANA_TX_CH4                                (0x3011)
+#define WCD939X_TX_CH4_ENABLE  BIT(7)
+#define WCD939X_TX_CH4_HPF3_INIT       BIT(6)
+#define WCD939X_TX_CH4_HPF4_INIT       BIT(5)
+#define WCD939X_TX_CH4_GAIN    GENMASK(4, 0)
+#define WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC              (0x3012)
+#define WCD939X_ANA_MICB3_DSP_EN_LOGIC                    (0x3013)
+#define WCD939X_ANA_MBHC_MECH                             (0x3014)
+#define WCD939X_MBHC_MECH_L_DET_EN     BIT(7)
+#define WCD939X_MBHC_MECH_GND_DET_EN   BIT(6)
+#define WCD939X_MBHC_MECH_MECH_DETECT_TYPE     BIT(5)
+#define WCD939X_MBHC_MECH_HPHL_PLUG_TYPE       BIT(4)
+#define WCD939X_MBHC_MECH_GND_PLUG_TYPE        BIT(3)
+#define WCD939X_MBHC_MECH_MECH_HS_L_PULLUP_COMP_EN     BIT(2)
+#define WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN     BIT(1)
+#define WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND       BIT(0)
+#define WCD939X_ANA_MBHC_ELECT                            (0x3015)
+#define WCD939X_MBHC_ELECT_FSM_EN      BIT(7)
+#define WCD939X_MBHC_ELECT_BTNDET_ISRC_CTL     GENMASK(6, 4)
+#define WCD939X_MBHC_ELECT_ELECT_DET_TYPE      BIT(3)
+#define WCD939X_MBHC_ELECT_ELECT_SCHMT_ISRC_CTL        GENMASK(2, 1)
+#define WCD939X_MBHC_ELECT_BIAS_EN     BIT(0)
+#define WCD939X_ANA_MBHC_ZDET                             (0x3016)
+#define WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN       BIT(7)
+#define WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN       BIT(6)
+#define WCD939X_MBHC_ZDET_ZDET_CHG_EN  BIT(5)
+#define WCD939X_MBHC_ZDET_ZDET_ILEAK_COMP_EN   BIT(4)
+#define WCD939X_MBHC_ZDET_ELECT_ISRC_EN        BIT(1)
+#define WCD939X_ANA_MBHC_RESULT_1                         (0x3017)
+#define WCD939X_MBHC_RESULT_1_Z_RESULT_LSB     GENMASK(7, 0)
+#define WCD939X_ANA_MBHC_RESULT_2                         (0x3018)
+#define WCD939X_MBHC_RESULT_2_Z_RESULT_MSB     GENMASK(7, 0)
+#define WCD939X_ANA_MBHC_RESULT_3                         (0x3019)
+#define WCD939X_ANA_MBHC_BTN0                             (0x301a)
+#define WCD939X_MBHC_BTN0_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN1                             (0x301b)
+#define WCD939X_MBHC_BTN1_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN2                             (0x301c)
+#define WCD939X_MBHC_BTN2_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN3                             (0x301d)
+#define WCD939X_MBHC_BTN3_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN4                             (0x301e)
+#define WCD939X_MBHC_BTN4_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN5                             (0x301f)
+#define WCD939X_MBHC_BTN5_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN6                             (0x3020)
+#define WCD939X_MBHC_BTN6_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN7                             (0x3021)
+#define WCD939X_MBHC_BTN7_VTH  GENMASK(7, 2)
+#define WCD939X_ANA_MICB1                                 (0x3022)
+#define WCD939X_MICB1_ENABLE   GENMASK(7, 6)
+#define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0)
+#define WCD939X_ANA_MICB2                                 (0x3023)
+#define WCD939X_MICB2_ENABLE   GENMASK(7, 6)
+#define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0)
+#define WCD939X_ANA_MICB2_RAMP                            (0x3024)
+#define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7)
+#define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE       BIT(6)
+#define WCD939X_MICB2_RAMP_ALLSW_OVRD_ENABLE   BIT(5)
+#define WCD939X_MICB2_RAMP_SHIFT_CTL   GENMASK(4, 2)
+#define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP        GENMASK(1, 0)
+#define WCD939X_ANA_MICB3                                 (0x3025)
+#define WCD939X_MICB3_ENABLE   GENMASK(7, 6)
+#define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0)
+#define WCD939X_ANA_MICB4                                 (0x3026)
+#define WCD939X_MICB4_ENABLE   GENMASK(7, 6)
+#define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0)
+#define WCD939X_BIAS_CTL                                  (0x3028)
+#define WCD939X_BIAS_VBG_FINE_ADJ                         (0x3029)
+#define WCD939X_LDOL_VDDCX_ADJUST                         (0x3040)
+#define WCD939X_LDOL_DISABLE_LDOL                         (0x3041)
+#define WCD939X_MBHC_CTL_CLK                              (0x3056)
+#define WCD939X_MBHC_CTL_ANA                              (0x3057)
+#define WCD939X_MBHC_ZDET_VNEG_CTL                        (0x3058)
+#define WCD939X_MBHC_ZDET_BIAS_CTL                        (0x3059)
+#define WCD939X_MBHC_CTL_BCS                              (0x305a)
+#define WCD939X_MBHC_MOISTURE_DET_FSM_STATUS              (0x305b)
+#define WCD939X_MBHC_TEST_CTL                             (0x305c)
+#define WCD939X_LDOH_MODE                                 (0x3067)
+#define WCD939X_MODE_LDOH_EN   BIT(7)
+#define WCD939X_MODE_PWRDN_STATE       BIT(6)
+#define WCD939X_MODE_SLOWRAMP_EN       BIT(5)
+#define WCD939X_MODE_VOUT_ADJUST       GENMASK(4, 3)
+#define WCD939X_MODE_VOUT_COARSE_ADJ   GENMASK(2, 0)
+#define WCD939X_LDOH_BIAS                                 (0x3068)
+#define WCD939X_LDOH_STB_LOADS                            (0x3069)
+#define WCD939X_LDOH_SLOWRAMP                             (0x306a)
+#define WCD939X_MICB1_TEST_CTL_1                          (0x306b)
+#define WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL  GENMASK(7, 5)
+#define WCD939X_TEST_CTL_1_EN_VREFGEN  BIT(4)
+#define WCD939X_TEST_CTL_1_EN_LDO      BIT(3)
+#define WCD939X_TEST_CTL_1_LDO_BLEEDER_I_CTRL  GENMASK(2, 0)
+#define WCD939X_MICB1_TEST_CTL_2                          (0x306c)
+#define WCD939X_TEST_CTL_2_IBIAS_VREFGEN       GENMASK(7, 6)
+#define WCD939X_TEST_CTL_2_INRUSH_CURRENT_FIX_DIS      BIT(5)
+#define WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER    GENMASK(2, 0)
+#define WCD939X_MICB1_TEST_CTL_3                          (0x306d)
+#define WCD939X_TEST_CTL_3_CFILT_REF_EN        BIT(7)
+#define WCD939X_TEST_CTL_3_RZ_LDO_VAL  GENMASK(6, 4)
+#define WCD939X_TEST_CTL_3_IBIAS_LDO_STG3      GENMASK(3, 2)
+#define WCD939X_TEST_CTL_3_ATEST_CTRL  GENMASK(1, 0)
+#define WCD939X_MICB2_TEST_CTL_1                          (0x306e)
+#define WCD939X_MICB2_TEST_CTL_2                          (0x306f)
+#define WCD939X_MICB2_TEST_CTL_3                          (0x3070)
+#define WCD939X_MICB3_TEST_CTL_1                          (0x3071)
+#define WCD939X_MICB3_TEST_CTL_2                          (0x3072)
+#define WCD939X_MICB3_TEST_CTL_3                          (0x3073)
+#define WCD939X_MICB4_TEST_CTL_1                          (0x3074)
+#define WCD939X_MICB4_TEST_CTL_2                          (0x3075)
+#define WCD939X_MICB4_TEST_CTL_3                          (0x3076)
+#define WCD939X_TX_COM_ADC_VCM                            (0x3077)
+#define WCD939X_TX_COM_BIAS_ATEST                         (0x3078)
+#define WCD939X_TX_COM_SPARE1                             (0x3079)
+#define WCD939X_TX_COM_SPARE2                             (0x307a)
+#define WCD939X_TX_COM_TXFE_DIV_CTL                       (0x307b)
+#define WCD939X_TX_COM_TXFE_DIV_START                     (0x307c)
+#define WCD939X_TX_COM_SPARE3                             (0x307d)
+#define WCD939X_TX_COM_SPARE4                             (0x307e)
+#define WCD939X_TX_1_2_TEST_EN                            (0x307f)
+#define WCD939X_TX_1_2_ADC_IB                             (0x3080)
+#define WCD939X_TX_1_2_ATEST_REFCTL                       (0x3081)
+#define WCD939X_TX_1_2_TEST_CTL                                   (0x3082)
+#define WCD939X_TX_1_2_TEST_BLK_EN1                       (0x3083)
+#define WCD939X_TX_1_2_TXFE1_CLKDIV                       (0x3084)
+#define WCD939X_TX_1_2_SAR2_ERR                                   (0x3085)
+#define WCD939X_TX_1_2_SAR1_ERR                                   (0x3086)
+#define WCD939X_TX_3_4_TEST_EN                            (0x3087)
+#define WCD939X_TX_3_4_ADC_IB                             (0x3088)
+#define WCD939X_TX_3_4_ATEST_REFCTL                       (0x3089)
+#define WCD939X_TX_3_4_TEST_CTL                                   (0x308a)
+#define WCD939X_TX_3_4_TEST_BLK_EN3                       (0x308b)
+#define WCD939X_TX_3_4_TXFE3_CLKDIV                       (0x308c)
+#define WCD939X_TX_3_4_SAR4_ERR                                   (0x308d)
+#define WCD939X_TX_3_4_SAR3_ERR                                   (0x308e)
+#define WCD939X_TX_3_4_TEST_BLK_EN2                       (0x308f)
+#define WCD939X_TEST_BLK_EN2_ADC2_INT1_EN      BIT(7)
+#define WCD939X_TEST_BLK_EN2_ADC2_INT2_EN      BIT(6)
+#define WCD939X_TEST_BLK_EN2_ADC2_SAR_EN       BIT(5)
+#define WCD939X_TEST_BLK_EN2_ADC2_CMGEN_EN     BIT(4)
+#define WCD939X_TEST_BLK_EN2_ADC2_CLKGEN_EN    BIT(3)
+#define WCD939X_TEST_BLK_EN2_ADC12_VREF_NONL2  GENMASK(2, 1)
+#define WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN      BIT(0)
+#define WCD939X_TX_3_4_TXFE2_CLKDIV                       (0x3090)
+#define WCD939X_TX_3_4_SPARE1                             (0x3091)
+#define WCD939X_TX_3_4_TEST_BLK_EN4                       (0x3092)
+#define WCD939X_TX_3_4_TXFE4_CLKDIV                       (0x3093)
+#define WCD939X_TX_3_4_SPARE2                             (0x3094)
+#define WCD939X_CLASSH_MODE_1                             (0x3097)
+#define WCD939X_CLASSH_MODE_2                             (0x3098)
+#define WCD939X_CLASSH_MODE_3                             (0x3099)
+#define WCD939X_CLASSH_CTRL_VCL_1                         (0x309a)
+#define WCD939X_CLASSH_CTRL_VCL_2                         (0x309b)
+#define WCD939X_CLASSH_CTRL_CCL_1                         (0x309c)
+#define WCD939X_CLASSH_CTRL_CCL_2                         (0x309d)
+#define WCD939X_CLASSH_CTRL_CCL_3                         (0x309e)
+#define WCD939X_CLASSH_CTRL_CCL_4                         (0x309f)
+#define WCD939X_CLASSH_CTRL_CCL_5                         (0x30a0)
+#define WCD939X_CLASSH_BUCK_TMUX_A_D                      (0x30a1)
+#define WCD939X_CLASSH_BUCK_SW_DRV_CNTL                           (0x30a2)
+#define WCD939X_CLASSH_SPARE                              (0x30a3)
+#define WCD939X_FLYBACK_EN                                (0x30a4)
+#define WCD939X_FLYBACK_VNEG_CTRL_1                       (0x30a5)
+#define WCD939X_FLYBACK_VNEG_CTRL_2                       (0x30a6)
+#define WCD939X_FLYBACK_VNEG_CTRL_3                       (0x30a7)
+#define WCD939X_FLYBACK_VNEG_CTRL_4                       (0x30a8)
+#define WCD939X_VNEG_CTRL_4_ILIM_SEL   GENMASK(7, 4)
+#define WCD939X_VNEG_CTRL_4_PW_BUF_POS GENMASK(3, 2)
+#define WCD939X_VNEG_CTRL_4_PW_BUF_NEG GENMASK(1, 0)
+#define WCD939X_FLYBACK_VNEG_CTRL_5                       (0x30a9)
+#define WCD939X_FLYBACK_VNEG_CTRL_6                       (0x30aa)
+#define WCD939X_FLYBACK_VNEG_CTRL_7                       (0x30ab)
+#define WCD939X_FLYBACK_VNEG_CTRL_8                       (0x30ac)
+#define WCD939X_FLYBACK_VNEG_CTRL_9                       (0x30ad)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_1                    (0x30ae)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_2                    (0x30af)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_3                    (0x30b0)
+#define WCD939X_FLYBACK_CTRL_1                            (0x30b1)
+#define WCD939X_FLYBACK_TEST_CTL                          (0x30b2)
+#define WCD939X_RX_AUX_SW_CTL                             (0x30b3)
+#define WCD939X_RX_PA_AUX_IN_CONN                         (0x30b4)
+#define WCD939X_RX_TIMER_DIV                              (0x30b5)
+#define WCD939X_RX_OCP_CTL                                (0x30b6)
+#define WCD939X_RX_OCP_COUNT                              (0x30b7)
+#define WCD939X_RX_BIAS_EAR_DAC                                   (0x30b8)
+#define WCD939X_RX_BIAS_EAR_AMP                                   (0x30b9)
+#define WCD939X_RX_BIAS_HPH_LDO                                   (0x30ba)
+#define WCD939X_RX_BIAS_HPH_PA                            (0x30bb)
+#define WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2                 (0x30bc)
+#define WCD939X_RX_BIAS_HPH_RDAC_LDO                      (0x30bd)
+#define WCD939X_RX_BIAS_HPH_CNP1                          (0x30be)
+#define WCD939X_RX_BIAS_HPH_LOWPOWER                      (0x30bf)
+#define WCD939X_RX_BIAS_AUX_DAC                                   (0x30c0)
+#define WCD939X_RX_BIAS_AUX_AMP                                   (0x30c1)
+#define WCD939X_RX_BIAS_VNEGDAC_BLEEDER                           (0x30c2)
+#define WCD939X_RX_BIAS_MISC                              (0x30c3)
+#define WCD939X_RX_BIAS_BUCK_RST                          (0x30c4)
+#define WCD939X_RX_BIAS_BUCK_VREF_ERRAMP                  (0x30c5)
+#define WCD939X_RX_BIAS_FLYB_ERRAMP                       (0x30c6)
+#define WCD939X_RX_BIAS_FLYB_BUFF                         (0x30c7)
+#define WCD939X_RX_BIAS_FLYB_MID_RST                      (0x30c8)
+#define WCD939X_HPH_L_STATUS                              (0x30c9)
+#define WCD939X_HPH_R_STATUS                              (0x30ca)
+#define WCD939X_HPH_CNP_EN                                (0x30cb)
+#define WCD939X_HPH_CNP_WG_CTL                            (0x30cc)
+#define WCD939X_HPH_CNP_WG_TIME                                   (0x30cd)
+#define WCD939X_HPH_OCP_CTL                               (0x30ce)
+#define WCD939X_OCP_CTL_OCP_CURR_LIMIT GENMASK(7, 5)
+#define WCD939X_OCP_CTL_OCP_FSM_EN     BIT(4)
+#define WCD939X_OCP_CTL_SPARE_BITS     BIT(3)
+#define WCD939X_OCP_CTL_SCD_OP_EN      BIT(1)
+#define WCD939X_HPH_AUTO_CHOP                             (0x30cf)
+#define WCD939X_HPH_CHOP_CTL                              (0x30d0)
+#define WCD939X_HPH_PA_CTL1                               (0x30d1)
+#define WCD939X_HPH_PA_CTL2                               (0x30d2)
+#define WCD939X_PA_CTL2_HPHPA_GND_R    BIT(6)
+#define WCD939X_PA_CTL2_HPHPA_GND_L    BIT(4)
+#define WCD939X_PA_CTL2_GM3_CASCODE_CTL_NORMAL GENMASK(1, 0)
+#define WCD939X_HPH_L_EN                                  (0x30d3)
+#define WCD939X_L_EN_CONST_SEL_L       GENMASK(7, 6)
+#define WCD939X_L_EN_GAIN_SOURCE_SEL   BIT(5)
+#define WCD939X_L_EN_SPARE_BITS        GENMASK(4, 0)
+#define WCD939X_HPH_L_TEST                                (0x30d4)
+#define WCD939X_HPH_L_ATEST                               (0x30d5)
+#define WCD939X_HPH_R_EN                                  (0x30d6)
+#define WCD939X_R_EN_CONST_SEL_R       GENMASK(7, 6)
+#define WCD939X_R_EN_GAIN_SOURCE_SEL   BIT(5)
+#define WCD939X_R_EN_SPARE_BITS        GENMASK(4, 0)
+#define WCD939X_HPH_R_TEST                                (0x30d7)
+#define WCD939X_HPH_R_ATEST                               (0x30d8)
+#define WCD939X_R_ATEST_DACR_REF_ATEST1_CONN   BIT(7)
+#define WCD939X_R_ATEST_LDO1_R_ATEST2_CONN     BIT(6)
+#define WCD939X_R_ATEST_LDO_R_ATEST2_CAL       BIT(5)
+#define WCD939X_R_ATEST_LDO2_R_ATEST2_CONN     BIT(4)
+#define WCD939X_R_ATEST_LDO_1P65V_ATEST1_CONN  BIT(3)
+#define WCD939X_R_ATEST_HPH_GND_OVR    BIT(1)
+#define WCD939X_HPH_RDAC_CLK_CTL1                         (0x30d9)
+#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN        BIT(7)
+#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_DIV_CTRL  GENMASK(6, 4)
+#define WCD939X_RDAC_CLK_CTL1_SPARE_BITS       GENMASK(3, 0)
+#define WCD939X_HPH_RDAC_CLK_CTL2                         (0x30da)
+#define WCD939X_HPH_RDAC_LDO_CTL                          (0x30db)
+#define WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL                  (0x30dc)
+#define WCD939X_HPH_REFBUFF_UHQA_CTL                      (0x30dd)
+#define WCD939X_REFBUFF_UHQA_CTL_SPARE_BITS    GENMASK(7, 6)
+#define WCD939X_REFBUFF_UHQA_CTL_HPH_VNEGREG2_COMP_CTL_OV      BIT(5)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_RBIAS_ADJUST  BIT(4)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFP_IOUT_CTL      GENMASK(3, 2)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_IOUT_CTL      GENMASK(1, 0)
+#define WCD939X_HPH_REFBUFF_LP_CTL                        (0x30de)
+#define WCD939X_REFBUFF_LP_CTL_HPH_VNEGREG2_CURR_COMP  GENMASK(7, 6)
+#define WCD939X_REFBUFF_LP_CTL_SPARE_BITS      GENMASK(5, 4)
+#define WCD939X_REFBUFF_LP_CTL_EN_PREREF_FILT_STARTUP_CLKDIV   BIT(3)
+#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_STARTUP_CLKDIV_CTL  GENMASK(2, 1)
+#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS      BIT(0)
+#define WCD939X_HPH_L_DAC_CTL                             (0x30df)
+#define WCD939X_HPH_R_DAC_CTL                             (0x30e0)
+#define WCD939X_HPH_SURGE_COMP_SEL                        (0x30e1)
+#define WCD939X_HPH_SURGE_EN                              (0x30e2)
+#define WCD939X_EN_EN_SURGE_PROTECTION_HPHL    BIT(7)
+#define WCD939X_EN_EN_SURGE_PROTECTION_HPHR    BIT(6)
+#define WCD939X_EN_SEL_SURGE_COMP_IQ   GENMASK(5, 4)
+#define WCD939X_EN_SURGE_VOLT_MODE_SHUTOFF_EN  BIT(3)
+#define WCD939X_EN_LATCH_INTR_OP_STG_HIZ_EN    BIT(2)
+#define WCD939X_EN_SURGE_LATCH_REG_RESET       BIT(1)
+#define WCD939X_EN_SWTICH_VN_VNDAC_NSURGE_EN   BIT(0)
+#define WCD939X_HPH_SURGE_MISC1                                   (0x30e3)
+#define WCD939X_HPH_SURGE_STATUS                          (0x30e4)
+#define WCD939X_EAR_EN                                    (0x30e9)
+#define WCD939X_EAR_PA_CON                                (0x30ea)
+#define WCD939X_EAR_SP_CON                                (0x30eb)
+#define WCD939X_EAR_DAC_CON                               (0x30ec)
+#define WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL    BIT(7)
+#define WCD939X_DAC_CON_REF_DBG_EN     BIT(6)
+#define WCD939X_DAC_CON_REF_DBG_GAIN   GENMASK(5, 3)
+#define WCD939X_DAC_CON_GAIN_DAC       GENMASK(2, 1)
+#define WCD939X_DAC_CON_INV_DATA       BIT(0)
+#define WCD939X_EAR_CNP_FSM_CON                                   (0x30ed)
+#define WCD939X_EAR_TEST_CTL                              (0x30ee)
+#define WCD939X_EAR_STATUS_REG_1                          (0x30ef)
+#define WCD939X_EAR_STATUS_REG_2                          (0x30f0)
+#define WCD939X_FLYBACK_NEW_CTRL_2                        (0x30f6)
+#define WCD939X_FLYBACK_NEW_CTRL_3                        (0x30f7)
+#define WCD939X_FLYBACK_NEW_CTRL_4                        (0x30f8)
+#define WCD939X_ANA_NEW_PAGE                              (0x3100)
+#define WCD939X_HPH_NEW_ANA_HPH2                          (0x3101)
+#define WCD939X_HPH_NEW_ANA_HPH3                          (0x3102)
+#define WCD939X_SLEEP_CTL                                 (0x3103)
+#define WCD939X_SLEEP_WATCHDOG_CTL                        (0x3104)
+#define WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL              (0x311f)
+#define WCD939X_MBHC_NEW_CTL_1                            (0x3120)
+#define WCD939X_CTL_1_RCO_EN   BIT(7)
+#define WCD939X_CTL_1_ADC_MODE BIT(4)
+#define WCD939X_CTL_1_ADC_ENABLE       BIT(3)
+#define WCD939X_CTL_1_DETECTION_DONE   BIT(2)
+#define WCD939X_CTL_1_BTN_DBNC_CTL     GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_CTL_2                            (0x3121)
+#define WCD939X_CTL_2_MUX_CTL  GENMASK(6, 4)
+#define WCD939X_CTL_2_M_RTH_CTL        GENMASK(3, 2)
+#define WCD939X_CTL_2_HS_VREF_CTL      GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_PLUG_DETECT_CTL                  (0x3122)
+#define WCD939X_MBHC_NEW_ZDET_ANA_CTL                     (0x3123)
+#define WCD939X_ZDET_ANA_CTL_AVERAGING_EN      BIT(7)
+#define WCD939X_ZDET_ANA_CTL_MAXV_CTL  GENMASK(6, 4)
+#define WCD939X_ZDET_ANA_CTL_RANGE_CTL GENMASK(3, 0)
+#define WCD939X_MBHC_NEW_ZDET_RAMP_CTL                    (0x3124)
+#define WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL     GENMASK(6, 4)
+#define WCD939X_ZDET_RAMP_CTL_TIME_CTL GENMASK(3, 0)
+#define WCD939X_MBHC_NEW_FSM_STATUS                       (0x3125)
+#define WCD939X_FSM_STATUS_ADC_TIMEOUT BIT(7)
+#define WCD939X_FSM_STATUS_ADC_COMPLETE        BIT(6)
+#define WCD939X_FSM_STATUS_HS_M_COMP_STATUS    BIT(5)
+#define WCD939X_FSM_STATUS_FAST_PRESS_FLAG_STATUS      BIT(4)
+#define WCD939X_FSM_STATUS_FAST_REMOVAL_FLAG_STATUS    BIT(3)
+#define WCD939X_FSM_STATUS_REMOVAL_FLAG_STATUS BIT(2)
+#define WCD939X_FSM_STATUS_ELECT_REM_RT_STATUS BIT(1)
+#define WCD939X_FSM_STATUS_BTN_STATUS  BIT(0)
+#define WCD939X_MBHC_NEW_ADC_RESULT                       (0x3126)
+#define WCD939X_ADC_RESULT_VALUE       GENMASK(7, 0)
+#define WCD939X_TX_NEW_CH12_MUX                                   (0x3127)
+#define WCD939X_TX_NEW_CH34_MUX                                   (0x3128)
+#define WCD939X_DIE_CRACK_DET_EN                          (0x312c)
+#define WCD939X_DIE_CRACK_DET_OUT                         (0x312d)
+#define WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL                 (0x3132)
+#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L                 (0x3133)
+#define WCD939X_PA_GAIN_CTL_L_EN_HPHPA_2VPK    BIT(7)
+#define WCD939X_PA_GAIN_CTL_L_RX_SUPPLY_LEVEL  BIT(6)
+#define WCD939X_PA_GAIN_CTL_L_DAC_DR_BOOST     BIT(5)
+#define WCD939X_PA_GAIN_CTL_L_VALUE    GENMASK(4, 0)
+#define WCD939X_HPH_NEW_INT_RDAC_VREF_CTL                 (0x3134)
+#define WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL             (0x3135)
+#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R                 (0x3136)
+#define WCD939X_PA_GAIN_CTL_R_D_RCO_CLK_EN     BIT(7)
+#define WCD939X_PA_GAIN_CTL_R_SPARE_BITS       GENMASK(6, 5)
+#define WCD939X_PA_GAIN_CTL_R_VALUE    GENMASK(4, 0)
+#define WCD939X_HPH_NEW_INT_PA_MISC1                      (0x3137)
+#define WCD939X_HPH_NEW_INT_PA_MISC2                      (0x3138)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC                  (0x3139)
+#define WCD939X_HPH_NEW_INT_TIMER1                        (0x313a)
+#define WCD939X_TIMER1_CURR_IDIV_CTL_CMPDR_OFF GENMASK(7, 5)
+#define WCD939X_TIMER1_CURR_IDIV_CTL_AUTOCHOP  GENMASK(4, 2)
+#define WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN   BIT(1)
+#define WCD939X_HPH_NEW_INT_TIMER2                        (0x313b)
+#define WCD939X_HPH_NEW_INT_TIMER3                        (0x313c)
+#define WCD939X_HPH_NEW_INT_TIMER4                        (0x313d)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC2                 (0x313e)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC3                 (0x313f)
+#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L                (0x3140)
+#define WCD939X_RDAC_HD2_CTL_L_EN_HD2_RES_DIV_L        BIT(7)
+#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_PULLGND_L   BIT(6)
+#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L       GENMASK(5, 0)
+#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R                (0x3141)
+#define WCD939X_RDAC_HD2_CTL_R_EN_HD2_RES_DIV_R        BIT(7)
+#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_PULLGND_L   BIT(6)
+#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R       GENMASK(5, 0)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI                   (0x3145)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP              (0x3146)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP                (0x3147)
+#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL         (0x31af)
+#define WCD939X_MOISTURE_DET_DC_CTRL_ONCOUNT   GENMASK(6, 5)
+#define WCD939X_MOISTURE_DET_DC_CTRL_OFFCOUNT  GENMASK(4, 0)
+#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL    (0x31b0)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_HPHL_PA_EN   BIT(6)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_DTEST_EN     GENMASK(5, 4)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_OVRD_POLLING   BIT(3)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING     BIT(2)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_DBNC_TIME      GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT             (0x31b1)
+#define WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL      GENMASK(4, 0)
+#define WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW (0x31b2)
+#define WCD939X_EAR_INT_NEW_CHOPPER_CON                           (0x31b7)
+#define WCD939X_EAR_INT_NEW_CNP_VCM_CON1                  (0x31b8)
+#define WCD939X_EAR_INT_NEW_CNP_VCM_CON2                  (0x31b9)
+#define WCD939X_EAR_INT_NEW_DYNAMIC_BIAS                  (0x31ba)
+#define WCD939X_SLEEP_INT_WATCHDOG_CTL_1                  (0x31d0)
+#define WCD939X_SLEEP_INT_WATCHDOG_CTL_2                  (0x31d1)
+#define WCD939X_DIE_CRACK_INT_DET_INT1                    (0x31d3)
+#define WCD939X_DIE_CRACK_INT_DET_INT2                    (0x31d4)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2              (0x31d5)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1              (0x31d6)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0              (0x31d7)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M         (0x31d8)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M         (0x31d9)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1         (0x31da)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0                   (0x31db)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP          (0x31dc)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1     (0x31dd)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0       (0x31de)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP      (0x31df)
+#define WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE    GENMASK(4, 0)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0           (0x31e0)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP      (0x31e1)
+#define WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M     GENMASK(7, 4)
+#define WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE    GENMASK(3, 0)
+#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1            (0x31e2)
+#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP                   (0x31e3)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L2                 (0x31e4)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L1                 (0x31e5)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L0                 (0x31e6)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_ULP                (0x31e7)
+#define WCD939X_DIGITAL_PAGE                              (0x3400)
+#define WCD939X_DIGITAL_CHIP_ID0                          (0x3401)
+#define WCD939X_DIGITAL_CHIP_ID1                          (0x3402)
+#define WCD939X_DIGITAL_CHIP_ID2                          (0x3403)
+#define WCD939X_DIGITAL_CHIP_ID3                          (0x3404)
+#define WCD939X_DIGITAL_SWR_TX_CLK_RATE                           (0x3405)
+#define WCD939X_DIGITAL_CDC_RST_CTL                       (0x3406)
+#define WCD939X_DIGITAL_TOP_CLK_CFG                       (0x3407)
+#define WCD939X_DIGITAL_CDC_ANA_CLK_CTL                           (0x3408)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV4_CLK_EN     BIT(5)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN     BIT(4)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN  BIT(3)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN     BIT(2)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN     BIT(1)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN  BIT(0)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN     BIT(4)
+#define WCD939X_DIGITAL_CDC_DIG_CLK_CTL                           (0x3409)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN    BIT(7)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN    BIT(6)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN    BIT(5)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN    BIT(4)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN    BIT(2)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN    BIT(1)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN    BIT(0)
+#define WCD939X_DIGITAL_SWR_RST_EN                        (0x340a)
+#define WCD939X_DIGITAL_CDC_PATH_MODE                     (0x340b)
+#define WCD939X_DIGITAL_CDC_RX_RST                        (0x340c)
+#define WCD939X_DIGITAL_CDC_RX0_CTL                       (0x340d)
+#define WCD939X_DIGITAL_CDC_RX1_CTL                       (0x340e)
+#define WCD939X_DIGITAL_CDC_RX2_CTL                       (0x340f)
+#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1               (0x3410)
+#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE  GENMASK(7, 4)
+#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE  GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3               (0x3411)
+#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE  GENMASK(7, 4)
+#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE  GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_COMP_CTL_0                    (0x3414)
+#define WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN    BIT(1)
+#define WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN    BIT(0)
+#define WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL                (0x3417)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_MBHC_1P2M_CLK_EN        BIT(5)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX3_ADC_CLK_EN  BIT(4)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX2_ADC_CLK_EN  BIT(3)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX1_ADC_CLK_EN  BIT(2)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX0_ADC_CLK_EN  BIT(1)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN BIT(0)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_0                  (0x3418)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_1                  (0x3419)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_0                  (0x341a)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_1                  (0x341b)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_0                  (0x341c)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_1                  (0x341d)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_0                  (0x341e)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_1                  (0x341f)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_0                  (0x3420)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_1                  (0x3421)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A6_0                  (0x3422)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A7_0                  (0x3423)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_0                           (0x3424)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_1                           (0x3425)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_2                           (0x3426)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_3                           (0x3427)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R1                    (0x3428)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R2                    (0x3429)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R3                    (0x342a)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R4                    (0x342b)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R5                    (0x342c)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R6                    (0x342d)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R7                    (0x342e)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_0                  (0x342f)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_1                  (0x3430)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_0                  (0x3431)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_1                  (0x3432)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_0                  (0x3433)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_1                  (0x3434)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_0                  (0x3435)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_1                  (0x3436)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_0                  (0x3437)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_1                  (0x3438)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A6_0                  (0x3439)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A7_0                  (0x343a)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_0                           (0x343b)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_1                           (0x343c)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_2                           (0x343d)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_3                           (0x343e)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R1                    (0x343f)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R2                    (0x3440)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R3                    (0x3441)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R4                    (0x3442)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R5                    (0x3443)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R6                    (0x3444)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R7                    (0x3445)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0                 (0x3446)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1                 (0x3447)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0                (0x3448)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1                (0x3449)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2                (0x344a)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0                (0x344b)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1                (0x344c)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2                (0x344d)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_CTL                  (0x344e)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPH_STEREO_EN BIT(4)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN    BIT(3)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN    BIT(2)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_DSD_EN   BIT(1)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_DSD_EN   BIT(0)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_CTL                  (0x344f)
+#define WCD939X_CDC_EAR_GAIN_CTL_EAR_EN        BIT(0)
+#define WCD939X_DIGITAL_CDC_EAR_PATH_CTL                  (0x3450)
+#define WCD939X_DIGITAL_CDC_SWR_CLH                       (0x3451)
+#define WCD939X_CDC_SWR_CLH_CLH_CTL    GENMASK(7, 0)
+#define WCD939X_DIGITAL_SWR_CLH_BYP                       (0x3452)
+#define WCD939X_DIGITAL_CDC_TX0_CTL                       (0x3453)
+#define WCD939X_DIGITAL_CDC_TX1_CTL                       (0x3454)
+#define WCD939X_DIGITAL_CDC_TX2_CTL                       (0x3455)
+#define WCD939X_DIGITAL_CDC_TX_RST                        (0x3456)
+#define WCD939X_DIGITAL_CDC_REQ_CTL                       (0x3457)
+#define WCD939X_CDC_REQ_CTL_TX3_WIDE_BAND      BIT(5)
+#define WCD939X_CDC_REQ_CTL_TX2_WIDE_BAND      BIT(4)
+#define WCD939X_CDC_REQ_CTL_TX1_WIDE_BAND      BIT(3)
+#define WCD939X_CDC_REQ_CTL_TX0_WIDE_BAND      BIT(2)
+#define WCD939X_CDC_REQ_CTL_FS_RATE_4P8        BIT(1)
+#define WCD939X_CDC_REQ_CTL_NO_NOTCH   BIT(0)
+#define WCD939X_DIGITAL_CDC_RST                                   (0x3458)
+#define WCD939X_DIGITAL_CDC_AMIC_CTL                      (0x345a)
+#define WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL      BIT(3)
+#define WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL      BIT(2)
+#define WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL      BIT(1)
+#define WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL      BIT(0)
+#define WCD939X_DIGITAL_CDC_DMIC_CTL                      (0x345b)
+#define WCD939X_CDC_DMIC_CTL_DMIC_LEGACY_SW_MODE       BIT(3)
+#define WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN   BIT(2)
+#define WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN      BIT(1)
+#define WCD939X_CDC_DMIC_CTL_SOFT_RESET        BIT(0)
+#define WCD939X_DIGITAL_CDC_DMIC1_CTL                     (0x345c)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SCALE_SEL       GENMASK(6, 4)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN      BIT(3)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SEL     GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC2_CTL                     (0x345d)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN     BIT(7)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SCALE_SEL       GENMASK(6, 4)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN      BIT(3)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SEL     GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC3_CTL                     (0x345e)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SCALE_SEL       GENMASK(6, 4)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN      BIT(3)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SEL     GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC4_CTL                     (0x345f)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SCALE_SEL       GENMASK(6, 4)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN      BIT(3)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SEL     GENMASK(2, 0)
+#define WCD939X_DIGITAL_EFUSE_PRG_CTL                     (0x3460)
+#define WCD939X_DIGITAL_EFUSE_CTL                         (0x3461)
+#define WCD939X_DIGITAL_CDC_DMIC_RATE_1_2                 (0x3462)
+#define WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE   GENMASK(7, 4)
+#define WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE   GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_DMIC_RATE_3_4                 (0x3463)
+#define WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE   GENMASK(7, 4)
+#define WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE   GENMASK(3, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL0                       (0x3465)
+#define WCD939X_PDM_WD_CTL0_HOLD_OFF   BIT(4)
+#define WCD939X_PDM_WD_CTL0_TIME_OUT_SEL       BIT(3)
+#define WCD939X_PDM_WD_CTL0_PDM_WD_EN  GENMASK(2, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL1                       (0x3466)
+#define WCD939X_PDM_WD_CTL1_HOLD_OFF   BIT(4)
+#define WCD939X_PDM_WD_CTL1_TIME_OUT_SEL       BIT(3)
+#define WCD939X_PDM_WD_CTL1_PDM_WD_EN  GENMASK(2, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL2                       (0x3467)
+#define WCD939X_DIGITAL_INTR_MODE                         (0x346a)
+#define WCD939X_DIGITAL_INTR_MASK_0                       (0x346b)
+#define WCD939X_DIGITAL_INTR_MASK_1                       (0x346c)
+#define WCD939X_DIGITAL_INTR_MASK_2                       (0x346d)
+#define WCD939X_DIGITAL_INTR_STATUS_0                     (0x346e)
+#define WCD939X_DIGITAL_INTR_STATUS_1                     (0x346f)
+#define WCD939X_DIGITAL_INTR_STATUS_2                     (0x3470)
+#define WCD939X_DIGITAL_INTR_CLEAR_0                      (0x3471)
+#define WCD939X_DIGITAL_INTR_CLEAR_1                      (0x3472)
+#define WCD939X_DIGITAL_INTR_CLEAR_2                      (0x3473)
+#define WCD939X_DIGITAL_INTR_LEVEL_0                      (0x3474)
+#define WCD939X_DIGITAL_INTR_LEVEL_1                      (0x3475)
+#define WCD939X_DIGITAL_INTR_LEVEL_2                      (0x3476)
+#define WCD939X_DIGITAL_INTR_SET_0                        (0x3477)
+#define WCD939X_DIGITAL_INTR_SET_1                        (0x3478)
+#define WCD939X_DIGITAL_INTR_SET_2                        (0x3479)
+#define WCD939X_DIGITAL_INTR_TEST_0                       (0x347a)
+#define WCD939X_DIGITAL_INTR_TEST_1                       (0x347b)
+#define WCD939X_DIGITAL_INTR_TEST_2                       (0x347c)
+#define WCD939X_DIGITAL_TX_MODE_DBG_EN                    (0x347f)
+#define WCD939X_DIGITAL_TX_MODE_DBG_0_1                           (0x3480)
+#define WCD939X_DIGITAL_TX_MODE_DBG_2_3                           (0x3481)
+#define WCD939X_DIGITAL_LB_IN_SEL_CTL                     (0x3482)
+#define WCD939X_DIGITAL_LOOP_BACK_MODE                    (0x3483)
+#define WCD939X_DIGITAL_SWR_DAC_TEST                      (0x3484)
+#define WCD939X_DIGITAL_SWR_HM_TEST_RX_0                  (0x3485)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_0                  (0x3486)
+#define WCD939X_DIGITAL_SWR_HM_TEST_RX_1                  (0x3487)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_1                  (0x3488)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_2                  (0x3489)
+#define WCD939X_DIGITAL_SWR_HM_TEST_0                     (0x348a)
+#define WCD939X_DIGITAL_SWR_HM_TEST_1                     (0x348b)
+#define WCD939X_DIGITAL_PAD_CTL_SWR_0                     (0x348c)
+#define WCD939X_DIGITAL_PAD_CTL_SWR_1                     (0x348d)
+#define WCD939X_DIGITAL_I2C_CTL                                   (0x348e)
+#define WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE             (0x348f)
+#define WCD939X_DIGITAL_EFUSE_TEST_CTL_0                  (0x3490)
+#define WCD939X_DIGITAL_EFUSE_TEST_CTL_1                  (0x3491)
+#define WCD939X_DIGITAL_EFUSE_T_DATA_0                    (0x3492)
+#define WCD939X_DIGITAL_EFUSE_T_DATA_1                    (0x3493)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_RX0                           (0x3494)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_RX1                           (0x3495)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX0                           (0x3496)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX1                           (0x3497)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX2                           (0x3498)
+#define WCD939X_DIGITAL_PAD_INP_DIS_0                     (0x3499)
+#define WCD939X_DIGITAL_PAD_INP_DIS_1                     (0x349a)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_0                  (0x349b)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_1                  (0x349c)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_2                  (0x349d)
+#define WCD939X_DIGITAL_RX_DATA_EDGE_CTL                  (0x349e)
+#define WCD939X_DIGITAL_TX_DATA_EDGE_CTL                  (0x349f)
+#define WCD939X_DIGITAL_GPIO_MODE                         (0x34a0)
+#define WCD939X_DIGITAL_PIN_CTL_OE                        (0x34a1)
+#define WCD939X_DIGITAL_PIN_CTL_DATA_0                    (0x34a2)
+#define WCD939X_DIGITAL_PIN_CTL_DATA_1                    (0x34a3)
+#define WCD939X_DIGITAL_PIN_STATUS_0                      (0x34a4)
+#define WCD939X_DIGITAL_PIN_STATUS_1                      (0x34a5)
+#define WCD939X_DIGITAL_DIG_DEBUG_CTL                     (0x34a6)
+#define WCD939X_DIGITAL_DIG_DEBUG_EN                      (0x34a7)
+#define WCD939X_DIGITAL_ANA_CSR_DBG_ADD                           (0x34a8)
+#define WCD939X_DIGITAL_ANA_CSR_DBG_CTL                           (0x34a9)
+#define WCD939X_DIGITAL_SSP_DBG                                   (0x34aa)
+#define WCD939X_DIGITAL_MODE_STATUS_0                     (0x34ab)
+#define WCD939X_DIGITAL_MODE_STATUS_1                     (0x34ac)
+#define WCD939X_DIGITAL_SPARE_0                                   (0x34ad)
+#define WCD939X_DIGITAL_SPARE_1                                   (0x34ae)
+#define WCD939X_DIGITAL_SPARE_2                                   (0x34af)
+#define WCD939X_DIGITAL_EFUSE_REG_0                       (0x34b0)
+#define WCD939X_EFUSE_REG_0_WCD939X_ID GENMASK(4, 1)
+#define WCD939X_EFUSE_REG_0_EFUSE_BLOWN        BIT(0)
+#define WCD939X_DIGITAL_EFUSE_REG_1                       (0x34b1)
+#define WCD939X_DIGITAL_EFUSE_REG_2                       (0x34b2)
+#define WCD939X_DIGITAL_EFUSE_REG_3                       (0x34b3)
+#define WCD939X_DIGITAL_EFUSE_REG_4                       (0x34b4)
+#define WCD939X_DIGITAL_EFUSE_REG_5                       (0x34b5)
+#define WCD939X_DIGITAL_EFUSE_REG_6                       (0x34b6)
+#define WCD939X_DIGITAL_EFUSE_REG_7                       (0x34b7)
+#define WCD939X_DIGITAL_EFUSE_REG_8                       (0x34b8)
+#define WCD939X_DIGITAL_EFUSE_REG_9                       (0x34b9)
+#define WCD939X_DIGITAL_EFUSE_REG_10                      (0x34ba)
+#define WCD939X_DIGITAL_EFUSE_REG_11                      (0x34bb)
+#define WCD939X_DIGITAL_EFUSE_REG_12                      (0x34bc)
+#define WCD939X_DIGITAL_EFUSE_REG_13                      (0x34bd)
+#define WCD939X_DIGITAL_EFUSE_REG_14                      (0x34be)
+#define WCD939X_DIGITAL_EFUSE_REG_15                      (0x34bf)
+#define WCD939X_DIGITAL_EFUSE_REG_16                      (0x34c0)
+#define WCD939X_DIGITAL_EFUSE_REG_17                      (0x34c1)
+#define WCD939X_DIGITAL_EFUSE_REG_18                      (0x34c2)
+#define WCD939X_DIGITAL_EFUSE_REG_19                      (0x34c3)
+#define WCD939X_DIGITAL_EFUSE_REG_20                      (0x34c4)
+#define WCD939X_DIGITAL_EFUSE_REG_21                      (0x34c5)
+#define WCD939X_DIGITAL_EFUSE_REG_22                      (0x34c6)
+#define WCD939X_DIGITAL_EFUSE_REG_23                      (0x34c7)
+#define WCD939X_DIGITAL_EFUSE_REG_24                      (0x34c8)
+#define WCD939X_DIGITAL_EFUSE_REG_25                      (0x34c9)
+#define WCD939X_DIGITAL_EFUSE_REG_26                      (0x34ca)
+#define WCD939X_DIGITAL_EFUSE_REG_27                      (0x34cb)
+#define WCD939X_DIGITAL_EFUSE_REG_28                      (0x34cc)
+#define WCD939X_DIGITAL_EFUSE_REG_29                      (0x34cd)
+#define WCD939X_DIGITAL_EFUSE_REG_30                      (0x34ce)
+#define WCD939X_DIGITAL_EFUSE_REG_31                      (0x34cf)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_0                           (0x34d0)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_1                           (0x34d1)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_2                           (0x34d2)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_3                           (0x34d3)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_4                           (0x34d4)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA0                  (0x34d5)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA1                  (0x34d6)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA2                  (0x34d7)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA3                  (0x34d8)
+#define WCD939X_DIGITAL_DEM_SECOND_ORDER                  (0x34d9)
+#define WCD939X_DIGITAL_DSM_CTRL                          (0x34da)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_0               (0x34db)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_1               (0x34dc)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_2               (0x34dd)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_3               (0x34de)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_0               (0x34df)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_1               (0x34e0)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_2               (0x34e1)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_3               (0x34e2)
+#define WCD939X_RX_TOP_PAGE                               (0x3500)
+#define WCD939X_RX_TOP_TOP_CFG0                                   (0x3501)
+#define WCD939X_TOP_CFG0_HPH_DAC_RATE_SEL      BIT(1)
+#define WCD939X_TOP_CFG0_PGA_UPDATE    BIT(0)
+#define WCD939X_RX_TOP_HPHL_COMP_WR_LSB                           (0x3502)
+#define WCD939X_RX_TOP_HPHL_COMP_WR_MSB                           (0x3503)
+#define WCD939X_RX_TOP_HPHL_COMP_LUT                      (0x3504)
+#define WCD939X_RX_TOP_HPHL_COMP_RD_LSB                           (0x3505)
+#define WCD939X_RX_TOP_HPHL_COMP_RD_MSB                           (0x3506)
+#define WCD939X_RX_TOP_HPHR_COMP_WR_LSB                           (0x3507)
+#define WCD939X_RX_TOP_HPHR_COMP_WR_MSB                           (0x3508)
+#define WCD939X_RX_TOP_HPHR_COMP_LUT                      (0x3509)
+#define WCD939X_RX_TOP_HPHR_COMP_RD_LSB                           (0x350a)
+#define WCD939X_RX_TOP_HPHR_COMP_RD_MSB                           (0x350b)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG1                    (0x350c)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG2                    (0x350d)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG3                    (0x350e)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG4                    (0x350f)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG5                    (0x3510)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG6                    (0x3511)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG1                    (0x3512)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG2                    (0x3513)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG3                    (0x3514)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG4                    (0x3515)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG5                    (0x3516)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG6                    (0x3517)
+#define WCD939X_RX_TOP_HPHL_PATH_CFG0                     (0x351c)
+#define WCD939X_HPHL_PATH_CFG0_INT_EN  BIT(1)
+#define WCD939X_HPHL_PATH_CFG0_DLY_ZN_EN       BIT(0)
+#define WCD939X_RX_TOP_HPHL_PATH_CFG1                     (0x351d)
+#define WCD939X_HPHL_PATH_CFG1_DSM_SOFT_RST    BIT(5)
+#define WCD939X_HPHL_PATH_CFG1_INT_SOFT_RST    BIT(4)
+#define WCD939X_HPHL_PATH_CFG1_FMT_CONV        BIT(3)
+#define WCD939X_HPHL_PATH_CFG1_IDLE_OVRD_EN    BIT(2)
+#define WCD939X_HPHL_PATH_CFG1_RX_DC_DROOP_COEFF_SEL   GENMASK(1, 0)
+#define WCD939X_RX_TOP_HPHR_PATH_CFG0                     (0x351e)
+#define WCD939X_HPHR_PATH_CFG0_INT_EN  BIT(2)
+#define WCD939X_HPHR_PATH_CFG0_DLY_ZN_EN       BIT(1)
+#define WCD939X_RX_TOP_HPHR_PATH_CFG1                     (0x351f)
+#define WCD939X_HPHR_PATH_CFG1_DSM_SOFT_RST    BIT(5)
+#define WCD939X_HPHR_PATH_CFG1_INT_SOFT_RST    BIT(4)
+#define WCD939X_HPHR_PATH_CFG1_FMT_CONV        BIT(3)
+#define WCD939X_HPHR_PATH_CFG1_IDLE_OVRD_EN    BIT(2)
+#define WCD939X_HPHR_PATH_CFG1_RX_DC_DROOP_COEFF_SEL   GENMASK(1, 0)
+#define WCD939X_RX_TOP_PATH_CFG2                          (0x3520)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC0                     (0x3521)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC1                     (0x3522)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC2                     (0x3523)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC3                     (0x3524)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC0                     (0x3525)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC1                     (0x3526)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC2                     (0x3527)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC3                     (0x3528)
+#define WCD939X_RX_TOP_PATH_SEC4                          (0x3529)
+#define WCD939X_RX_TOP_PATH_SEC5                          (0x352a)
+#define WCD939X_COMPANDER_HPHL_CTL0                       (0x3540)
+#define WCD939X_COMPANDER_HPHL_CTL1                       (0x3541)
+#define WCD939X_COMPANDER_HPHL_CTL2                       (0x3542)
+#define WCD939X_COMPANDER_HPHL_CTL3                       (0x3543)
+#define WCD939X_COMPANDER_HPHL_CTL4                       (0x3544)
+#define WCD939X_COMPANDER_HPHL_CTL5                       (0x3545)
+#define WCD939X_COMPANDER_HPHL_CTL6                       (0x3546)
+#define WCD939X_COMPANDER_HPHL_CTL7                       (0x3547)
+#define WCD939X_COMPANDER_HPHL_CTL8                       (0x3548)
+#define WCD939X_COMPANDER_HPHL_CTL9                       (0x3549)
+#define WCD939X_COMPANDER_HPHL_CTL10                      (0x354a)
+#define WCD939X_COMPANDER_HPHL_CTL11                      (0x354b)
+#define WCD939X_COMPANDER_HPHL_CTL12                      (0x354c)
+#define WCD939X_COMPANDER_HPHL_CTL13                      (0x354d)
+#define WCD939X_COMPANDER_HPHL_CTL14                      (0x354e)
+#define WCD939X_COMPANDER_HPHL_CTL15                      (0x354f)
+#define WCD939X_COMPANDER_HPHL_CTL16                      (0x3550)
+#define WCD939X_COMPANDER_HPHL_CTL17                      (0x3551)
+#define WCD939X_COMPANDER_HPHL_CTL18                      (0x3552)
+#define WCD939X_COMPANDER_HPHL_CTL19                      (0x3553)
+#define WCD939X_R_CTL0                                    (0x3560)
+#define WCD939X_R_CTL1                                    (0x3561)
+#define WCD939X_R_CTL2                                    (0x3562)
+#define WCD939X_R_CTL3                                    (0x3563)
+#define WCD939X_R_CTL4                                    (0x3564)
+#define WCD939X_R_CTL5                                    (0x3565)
+#define WCD939X_R_CTL6                                    (0x3566)
+#define WCD939X_R_CTL7                                    (0x3567)
+#define WCD939X_R_CTL8                                    (0x3568)
+#define WCD939X_R_CTL9                                    (0x3569)
+#define WCD939X_R_CTL10                                           (0x356a)
+#define WCD939X_R_CTL11                                           (0x356b)
+#define WCD939X_R_CTL12                                           (0x356c)
+#define WCD939X_R_CTL13                                           (0x356d)
+#define WCD939X_R_CTL14                                           (0x356e)
+#define WCD939X_R_CTL15                                           (0x356f)
+#define WCD939X_R_CTL16                                           (0x3570)
+#define WCD939X_R_CTL17                                           (0x3571)
+#define WCD939X_R_CTL18                                           (0x3572)
+#define WCD939X_R_CTL19                                           (0x3573)
+#define WCD939X_E_PATH_CTL                                (0x3580)
+#define WCD939X_E_CFG0                                    (0x3581)
+#define WCD939X_CFG0_AUTO_DISABLE_ANC  BIT(2)
+#define WCD939X_CFG0_AUTO_DISABLE_DSD  BIT(1)
+#define WCD939X_CFG0_IDLE_STEREO       BIT(0)
+#define WCD939X_E_CFG1                                    (0x3582)
+#define WCD939X_E_CFG2                                    (0x3583)
+#define WCD939X_E_CFG3                                    (0x3584)
+#define WCD939X_DSD_HPHL_PATH_CTL                         (0x3590)
+#define WCD939X_DSD_HPHL_CFG0                             (0x3591)
+#define WCD939X_DSD_HPHL_CFG1                             (0x3592)
+#define WCD939X_DSD_HPHL_CFG2                             (0x3593)
+#define WCD939X_DSD_HPHL_CFG3                             (0x3594)
+#define WCD939X_DSD_HPHL_CFG4                             (0x3595)
+#define WCD939X_DSD_HPHL_CFG5                             (0x3596)
+#define WCD939X_DSD_HPHR_PATH_CTL                         (0x35a0)
+#define WCD939X_DSD_HPHR_CFG0                             (0x35a1)
+#define WCD939X_DSD_HPHR_CFG1                             (0x35a2)
+#define WCD939X_DSD_HPHR_CFG2                             (0x35a3)
+#define WCD939X_DSD_HPHR_CFG3                             (0x35a4)
+#define WCD939X_DSD_HPHR_CFG4                             (0x35a5)
+#define WCD939X_DSD_HPHR_CFG5                             (0x35a6)
+#define WCD939X_MAX_REGISTER                              (WCD939X_DSD_HPHR_CFG5)
+
+#define WCD939X_MAX_SWR_PORTS          (6)
+#define WCD939X_MAX_RX_SWR_PORTS       (6)
+#define WCD939X_MAX_TX_SWR_PORTS       (4)
+#define WCD939X_MAX_SWR_CH_IDS         (15)
+
+struct wcd939x_sdw_ch_info {
+       int port_num;
+       unsigned int ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask)      \
+       [id] = {                        \
+               .port_num = pn,         \
+               .ch_mask = cmask,       \
+       }
+
+enum wcd939x_tx_sdw_ports {
+       WCD939X_ADC_1_4_PORT = 1,
+       WCD939X_ADC_DMIC_1_2_PORT,
+       WCD939X_DMIC_0_3_MBHC_PORT,
+       WCD939X_DMIC_3_7_PORT,
+};
+
+enum wcd939x_tx_sdw_channels {
+       WCD939X_ADC1,
+       WCD939X_ADC2,
+       WCD939X_ADC3,
+       WCD939X_ADC4,
+       WCD939X_DMIC0,
+       WCD939X_DMIC1,
+       WCD939X_MBHC,
+       WCD939X_DMIC2,
+       WCD939X_DMIC3,
+       WCD939X_DMIC4,
+       WCD939X_DMIC5,
+       WCD939X_DMIC6,
+       WCD939X_DMIC7,
+};
+
+enum wcd939x_rx_sdw_ports {
+       WCD939X_HPH_PORT = 1,
+       WCD939X_CLSH_PORT,
+       WCD939X_COMP_PORT,
+       WCD939X_LO_PORT,
+       WCD939X_DSD_PORT,
+       WCD939X_HIFI_PCM_PORT,
+};
+
+enum wcd939x_rx_sdw_channels {
+       WCD939X_HPH_L,
+       WCD939X_HPH_R,
+       WCD939X_CLSH,
+       WCD939X_COMP_L,
+       WCD939X_COMP_R,
+       WCD939X_LO,
+       WCD939X_DSD_L,
+       WCD939X_DSD_R,
+       WCD939X_HIFI_PCM_L,
+       WCD939X_HIFI_PCM_R,
+};
+
+enum {
+       WCD939X_SDW_DIR_RX,
+       WCD939X_SDW_DIR_TX,
+};
+
+struct wcd939x_priv;
+struct wcd939x_sdw_priv {
+       struct sdw_slave *sdev;
+       struct sdw_stream_config sconfig;
+       struct sdw_stream_runtime *sruntime;
+       struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS];
+       struct wcd939x_sdw_ch_info *ch_info;
+       bool port_enable[WCD939X_MAX_SWR_CH_IDS];
+       int active_ports;
+       int num_ports;
+       bool is_tx;
+       struct wcd939x_priv *wcd939x;
+       struct irq_domain *slave_irq;
+       struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD939X_SDW)
+int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+                    struct snd_pcm_substream *substream,
+                    struct snd_soc_dai *dai);
+int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+                              struct snd_soc_dai *dai,
+                              void *stream, int direction);
+int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+                         struct snd_pcm_substream *substream,
+                         struct snd_pcm_hw_params *params,
+                         struct snd_soc_dai *dai);
+
+struct device *wcd939x_sdw_device_get(struct device_node *np);
+unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev);
+
+struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd);
+#else
+
+static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+                                  struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+                                            struct snd_soc_dai *dai,
+                                            void *stream, int direction)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+                                       struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params,
+                                       struct snd_soc_dai *dai)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline struct device *wcd939x_sdw_device_get(struct device_node *np)
+{
+       return NULL;
+}
+
+static inline unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev)
+{
+       return 0;
+}
+
+struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd)
+{
+       return PTR_ERR(-EINVAL);
+}
+#endif /* CONFIG_SND_SOC_WCD939X_SDW */
+
+#endif /* __WCD939X_H__ */
index 36ea0dcdc7ab0033eb48e393d783e4f7d4df9854..e451c009f2d99980bab20dd5d4c55cc26bd73cd5 100644 (file)
@@ -1092,27 +1092,36 @@ static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
        dsp->fatal_error = false;
 }
 
+int wm_adsp_run(struct wm_adsp *dsp)
+{
+       flush_work(&dsp->boot_work);
+
+       return cs_dsp_run(&dsp->cs_dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_run);
+
+void wm_adsp_stop(struct wm_adsp *dsp)
+{
+       cs_dsp_stop(&dsp->cs_dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_stop);
+
 int wm_adsp_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 wm_adsp *dsps = snd_soc_component_get_drvdata(component);
        struct wm_adsp *dsp = &dsps[w->shift];
-       int ret = 0;
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
-               flush_work(&dsp->boot_work);
-               ret = cs_dsp_run(&dsp->cs_dsp);
-               break;
+               return wm_adsp_run(dsp);
        case SND_SOC_DAPM_PRE_PMD:
-               cs_dsp_stop(&dsp->cs_dsp);
-               break;
+               wm_adsp_stop(dsp);
+               return 0;
        default:
-               break;
+               return 0;
        }
-
-       return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp_event);
 
index 067d807a7ca82d262685591226620abd75de64bd..e53dfcf1f78ff77e5bad89e9e1aef3bb34db242d 100644 (file)
@@ -98,6 +98,8 @@ irqreturn_t wm_adsp2_bus_error(int irq, void *data);
 irqreturn_t wm_halo_bus_error(int irq, void *data);
 irqreturn_t wm_halo_wdt_expire(int irq, void *data);
 
+int wm_adsp_run(struct wm_adsp *dsp);
+void wm_adsp_stop(struct wm_adsp *dsp);
 int wm_adsp_event(struct snd_soc_dapm_widget *w,
                  struct snd_kcontrol *kcontrol, int event);
 
index f2653df84e4afcc3d7a44f44e310ef269e75c97c..a9767ef0e39d158a1a58929f6e85762499530568 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_registers.h>
@@ -699,6 +700,7 @@ struct wsa884x_priv {
        struct sdw_stream_runtime *sruntime;
        struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS];
        struct gpio_desc *sd_n;
+       struct reset_control *sd_reset;
        bool port_prepared[WSA884X_MAX_SWR_PORTS];
        bool port_enable[WSA884X_MAX_SWR_PORTS];
        unsigned int variant;
@@ -1799,9 +1801,22 @@ static struct snd_soc_dai_driver wsa884x_dais[] = {
        },
 };
 
-static void wsa884x_gpio_powerdown(void *data)
+static void wsa884x_reset_powerdown(void *data)
 {
-       gpiod_direction_output(data, 1);
+       struct wsa884x_priv *wsa884x = data;
+
+       if (wsa884x->sd_reset)
+               reset_control_assert(wsa884x->sd_reset);
+       else
+               gpiod_direction_output(wsa884x->sd_n, 1);
+}
+
+static void wsa884x_reset_deassert(struct wsa884x_priv *wsa884x)
+{
+       if (wsa884x->sd_reset)
+               reset_control_deassert(wsa884x->sd_reset);
+       else
+               gpiod_direction_output(wsa884x->sd_n, 0);
 }
 
 static void wsa884x_regulator_disable(void *data)
@@ -1809,6 +1824,27 @@ static void wsa884x_regulator_disable(void *data)
        regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data);
 }
 
+static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x)
+{
+       wsa884x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
+       if (IS_ERR(wsa884x->sd_reset))
+               return dev_err_probe(dev, PTR_ERR(wsa884x->sd_reset),
+                                    "Failed to get reset\n");
+       else if (wsa884x->sd_reset)
+               return 0;
+       /*
+        * else: NULL, so use the backwards compatible way for powerdown-gpios,
+        * which does not handle sharing GPIO properly.
+        */
+       wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+                                               GPIOD_OUT_HIGH);
+       if (IS_ERR(wsa884x->sd_n))
+               return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n),
+                                    "Shutdown Control GPIO not found\n");
+
+       return 0;
+}
+
 static int wsa884x_probe(struct sdw_slave *pdev,
                         const struct sdw_device_id *id)
 {
@@ -1838,11 +1874,9 @@ static int wsa884x_probe(struct sdw_slave *pdev,
        if (ret)
                return ret;
 
-       wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
-                                               GPIOD_OUT_HIGH);
-       if (IS_ERR(wsa884x->sd_n))
-               return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n),
-                                    "Shutdown Control GPIO not found\n");
+       ret = wsa884x_get_reset(dev, wsa884x);
+       if (ret)
+               return ret;
 
        dev_set_drvdata(dev, wsa884x);
        wsa884x->slave = pdev;
@@ -1858,9 +1892,8 @@ static int wsa884x_probe(struct sdw_slave *pdev,
        pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop;
        pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
 
-       /* Bring out of reset */
-       gpiod_direction_output(wsa884x->sd_n, 0);
-       ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n);
+       wsa884x_reset_deassert(wsa884x);
+       ret = devm_add_action_or_reset(dev, wsa884x_reset_powerdown, wsa884x);
        if (ret)
                return ret;
 
index 63f1f05da9476f5eb5af2ff4475ce1f83d467a9c..6be074ea0b3f040e36744f6030fa9ff222800e65 100644 (file)
@@ -196,7 +196,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
                }
        }
 
-       ret = snd_soc_register_card(&eukrea_tlv320);
+       ret = devm_snd_soc_register_card(&pdev->dev, &eukrea_tlv320);
 err:
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
@@ -205,11 +205,6 @@ err:
        return ret;
 }
 
-static void eukrea_tlv320_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&eukrea_tlv320);
-}
-
 static const struct of_device_id imx_tlv320_dt_ids[] = {
        { .compatible = "eukrea,asoc-tlv320"},
        { /* sentinel */ }
@@ -222,7 +217,6 @@ static struct platform_driver eukrea_tlv320_driver = {
                .of_match_table = imx_tlv320_dt_ids,
        },
        .probe = eukrea_tlv320_probe,
-       .remove_new = eukrea_tlv320_remove,
 };
 
 module_platform_driver(eukrea_tlv320_driver);
index 546bd4e333b5fbc87569c83fd57073d9caea8bcc..0e2c3143967083a32046de0f8d5a88dbd61fd144 100644 (file)
@@ -1639,6 +1639,18 @@ static const struct fsl_sai_soc_data fsl_sai_imx93_data = {
        .max_burst = {8, 8},
 };
 
+static const struct fsl_sai_soc_data fsl_sai_imx95_data = {
+       .use_imx_pcm = true,
+       .use_edma = true,
+       .fifo_depth = 128,
+       .reg_offset = 8,
+       .mclk0_is_mclk1 = false,
+       .pins = 8,
+       .flags = 0,
+       .max_register = FSL_SAI_MCTL,
+       .max_burst = {8, 8},
+};
+
 static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
        { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
@@ -1651,6 +1663,7 @@ static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
        { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data },
        { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data },
+       { .compatible = "fsl,imx95-sai", .data = &fsl_sai_imx95_data },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fsl_sai_ids);
index 18d129c2164813cdf8e5be033344680b8ce7facd..a42149311c8bed02a293aba6744a53dfe6dbd3ba 100644 (file)
@@ -61,7 +61,7 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
 /* There's only one global utilities register */
 static phys_addr_t guts_phys;
 
-/**
+/*
  * machine_data: machine-specific ASoC device data
  *
  * This structure contains data for a single sound platform device on an
@@ -80,11 +80,14 @@ struct machine_data {
 };
 
 /**
- * p1022_rdk_machine_probe: initialize the board
+ * p1022_rdk_machine_probe - initialize the board
+ * @card: ASoC card instance
  *
  * This function is used to initialize the board-specific hardware.
  *
  * Here we program the DMACR and PMUXCR registers.
+ *
+ * Returns: %0 on success or negative errno value on error
  */
 static int p1022_rdk_machine_probe(struct snd_soc_card *card)
 {
@@ -119,11 +122,14 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card)
 }
 
 /**
- * p1022_rdk_startup: program the board with various hardware parameters
+ * p1022_rdk_startup - program the board with various hardware parameters
+ * @substream: ASoC substream object
  *
  * This function takes board-specific information, like clock frequencies
  * and serial data formats, and passes that information to the codec and
  * transport drivers.
+ *
+ * Returns: %0 on success or negative errno value on error
  */
 static int p1022_rdk_startup(struct snd_pcm_substream *substream)
 {
@@ -153,10 +159,13 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream)
 }
 
 /**
- * p1022_rdk_machine_remove: Remove the sound device
+ * p1022_rdk_machine_remove - Remove the sound device
+ * @card: ASoC card instance
  *
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
+ *
+ * Returns: %0 on success or negative errno value on error
  */
 static int p1022_rdk_machine_remove(struct snd_soc_card *card)
 {
@@ -181,7 +190,7 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card)
        return 0;
 }
 
-/**
+/*
  * p1022_rdk_ops: ASoC machine driver operations
  */
 static const struct snd_soc_ops p1022_rdk_ops = {
@@ -189,11 +198,14 @@ static const struct snd_soc_ops p1022_rdk_ops = {
 };
 
 /**
- * p1022_rdk_probe: platform probe function for the machine driver
+ * p1022_rdk_probe - platform probe function for the machine driver
+ * @pdev: platform device pointer
  *
  * Although this is a machine driver, the SSI node is the "master" node with
  * respect to audio hardware connections.  Therefore, we create a new ASoC
  * device for each new SSI node that has a codec attached.
+ *
+ * Returns: %0 on success or negative errno value on error
  */
 static int p1022_rdk_probe(struct platform_device *pdev)
 {
@@ -341,7 +353,8 @@ error_put:
 }
 
 /**
- * p1022_rdk_remove: remove the platform device
+ * p1022_rdk_remove - remove the platform device
+ * @pdev: platform device pointer
  *
  * This function is called when the platform device is removed.
  */
@@ -368,9 +381,11 @@ static struct platform_driver p1022_rdk_driver = {
 };
 
 /**
- * p1022_rdk_init: machine driver initialization.
+ * p1022_rdk_init - machine driver initialization.
  *
  * This function is called when this module is loaded.
+ *
+ * Returns: %0 on success or negative errno value on error
  */
 static int __init p1022_rdk_init(void)
 {
@@ -391,7 +406,7 @@ static int __init p1022_rdk_init(void)
 }
 
 /**
- * p1022_rdk_exit: machine driver exit
+ * p1022_rdk_exit - machine driver exit
  *
  * This function is called when this driver is unloaded.
  */
index 3fc2c9a6c44dcdb92779c31598ab81abfb3323c5..0630e58b9d6bf0ad1fd794899490d8d23d7de42a 100644 (file)
@@ -19,8 +19,9 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/compress_driver.h>
-#include <asm/intel-mid.h>
+
 #include <asm/platform_sst_audio.h>
+
 #include "../sst-mfld-platform.h"
 #include "sst.h"
 
index 460ee6599daf035baf03013be668a35c4e7cb5b4..5480500337f8572df38b2c586497f73959ca4ca5 100644 (file)
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
-                   topology.o path.o pcm.o board_selection.o control.o
+                   topology.o path.o pcm.o board_selection.o control.o \
+                   sysfs.o
 snd-soc-avs-objs += cldma.o
-snd-soc-avs-objs += skl.o apl.o
+snd-soc-avs-objs += skl.o apl.o cnl.o icl.o tgl.o
 
 snd-soc-avs-objs += trace.o
 # tell define_trace.h where to find the trace header
index 1860099c782a7249b89deb35637061de804faf05..c21ecaef9ebaa7d3a3093881777575d4e0ebe7fb 100644 (file)
 #include "path.h"
 #include "topology.h"
 
-static int __maybe_unused
-apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
-               u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+#ifdef CONFIG_DEBUG_FS
+int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                       u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
 {
-       struct apl_log_state_info *info;
+       struct avs_apl_log_state_info *info;
        u32 size, num_cores = adev->hw_cfg.dsp_cores;
        int ret, i;
 
@@ -47,10 +47,11 @@ apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri
 
        return 0;
 }
+#endif
 
-static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
 {
-       struct apl_log_buffer_layout layout;
+       struct avs_apl_log_buffer_layout layout;
        void __iomem *addr, *buf;
 
        addr = avs_log_buffer_addr(adev, msg->log.core);
@@ -63,11 +64,11 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg
                /* consume the logs regardless of consumer presence */
                goto update_read_ptr;
 
-       buf = apl_log_payload_addr(addr);
+       buf = avs_apl_log_payload_addr(addr);
 
        if (layout.read_ptr > layout.write_ptr) {
                avs_dump_fw_log(adev, buf + layout.read_ptr,
-                               apl_log_payload_size(adev) - layout.read_ptr);
+                               avs_apl_log_payload_size(adev) - layout.read_ptr);
                layout.read_ptr = 0;
        }
        avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
@@ -77,7 +78,8 @@ update_read_ptr:
        return 0;
 }
 
-static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout)
+static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core,
+                                 struct avs_apl_log_buffer_layout *layout)
 {
        unsigned long timeout;
        void __iomem *addr;
@@ -99,11 +101,11 @@ static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buf
 }
 
 /* reads log header and tests its type */
-#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+#define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
 
-static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
 {
-       struct apl_log_buffer_layout layout;
+       struct avs_apl_log_buffer_layout layout;
        void __iomem *addr, *buf;
        size_t dump_size;
        u16 offset = 0;
@@ -124,9 +126,9 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
        if (!addr)
                goto exit;
 
-       buf = apl_log_payload_addr(addr);
+       buf = avs_apl_log_payload_addr(addr);
        memcpy_fromio(&layout, addr, sizeof(layout));
-       if (!apl_is_entry_stackdump(buf + layout.read_ptr)) {
+       if (!avs_apl_is_entry_stackdump(buf + layout.read_ptr)) {
                union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
 
                /*
@@ -142,11 +144,11 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
        do {
                u32 count;
 
-               if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+               if (avs_apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
                        break;
 
                if (layout.read_ptr > layout.write_ptr) {
-                       count = apl_log_payload_size(adev) - layout.read_ptr;
+                       count = avs_apl_log_payload_size(adev) - layout.read_ptr;
                        memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
                        layout.read_ptr = 0;
                        offset += count;
@@ -165,7 +167,7 @@ exit:
        return 0;
 }
 
-static bool apl_lp_streaming(struct avs_dev *adev)
+static bool avs_apl_lp_streaming(struct avs_dev *adev)
 {
        struct avs_path *path;
 
@@ -201,7 +203,7 @@ static bool apl_lp_streaming(struct avs_dev *adev)
        return true;
 }
 
-static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
 {
        /* wake in all cases */
        if (wake)
@@ -215,10 +217,10 @@ static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool w
         * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
         * not the power-gating mechanism known from cAVS 2.0.
         */
-       return apl_lp_streaming(adev);
+       return avs_apl_lp_streaming(adev);
 }
 
-static int apl_set_d0ix(struct avs_dev *adev, bool enable)
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable)
 {
        bool streaming = false;
        int ret;
@@ -231,20 +233,20 @@ static int apl_set_d0ix(struct avs_dev *adev, bool enable)
        return AVS_IPC_RET(ret);
 }
 
-const struct avs_dsp_ops apl_dsp_ops = {
+const struct avs_dsp_ops avs_apl_dsp_ops = {
        .power = avs_dsp_core_power,
        .reset = avs_dsp_core_reset,
        .stall = avs_dsp_core_stall,
-       .irq_handler = avs_dsp_irq_handler,
-       .irq_thread = avs_dsp_irq_thread,
+       .irq_handler = avs_irq_handler,
+       .irq_thread = avs_skl_irq_thread,
        .int_control = avs_dsp_interrupt_control,
        .load_basefw = avs_hda_load_basefw,
        .load_lib = avs_hda_load_library,
        .transfer_mods = avs_hda_transfer_modules,
-       .log_buffer_offset = skl_log_buffer_offset,
-       .log_buffer_status = apl_log_buffer_status,
-       .coredump = apl_coredump,
-       .d0ix_toggle = apl_d0ix_toggle,
-       .set_d0ix = apl_set_d0ix,
+       .log_buffer_offset = avs_skl_log_buffer_offset,
+       .log_buffer_status = avs_apl_log_buffer_status,
+       .coredump = avs_apl_coredump,
+       .d0ix_toggle = avs_apl_d0ix_toggle,
+       .set_d0ix = avs_apl_set_d0ix,
        AVS_SET_ENABLE_LOGS_OP(apl)
 };
index d694e08e44e1851fafb69352eeadafbb70d0def8..f80f79415344abde8784ce485ee7345fb8d61b30 100644 (file)
@@ -46,8 +46,8 @@ struct avs_dsp_ops {
        int (* const power)(struct avs_dev *, u32, bool);
        int (* const reset)(struct avs_dev *, u32, bool);
        int (* const stall)(struct avs_dev *, u32, bool);
-       irqreturn_t (* const irq_handler)(int, void *);
-       irqreturn_t (* const irq_thread)(int, void *);
+       irqreturn_t (* const irq_handler)(struct avs_dev *);
+       irqreturn_t (* const irq_thread)(struct avs_dev *);
        void (* const int_control)(struct avs_dev *, bool);
        int (* const load_basefw)(struct avs_dev *, struct firmware *);
        int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
@@ -64,8 +64,11 @@ struct avs_dsp_ops {
 #define avs_dsp_op(adev, op, ...) \
        ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
 
-extern const struct avs_dsp_ops skl_dsp_ops;
-extern const struct avs_dsp_ops apl_dsp_ops;
+extern const struct avs_dsp_ops avs_skl_dsp_ops;
+extern const struct avs_dsp_ops avs_apl_dsp_ops;
+extern const struct avs_dsp_ops avs_cnl_dsp_ops;
+extern const struct avs_dsp_ops avs_icl_dsp_ops;
+extern const struct avs_dsp_ops avs_tgl_dsp_ops;
 
 #define AVS_PLATATTR_CLDMA             BIT_ULL(0)
 #define AVS_PLATATTR_IMR               BIT_ULL(1)
@@ -73,6 +76,23 @@ extern const struct avs_dsp_ops apl_dsp_ops;
 #define avs_platattr_test(adev, attr) \
        ((adev)->spec->attributes & AVS_PLATATTR_##attr)
 
+struct avs_sram_spec {
+       const u32 base_offset;
+       const u32 window_size;
+       const u32 rom_status_offset;
+};
+
+struct avs_hipc_spec {
+       const u32 req_offset;
+       const u32 req_ext_offset;
+       const u32 req_busy_mask;
+       const u32 ack_offset;
+       const u32 ack_done_mask;
+       const u32 rsp_offset;
+       const u32 rsp_busy_mask;
+       const u32 ctl_offset;
+};
+
 /* Platform specific descriptor */
 struct avs_spec {
        const char *name;
@@ -82,9 +102,8 @@ struct avs_spec {
 
        const u32 core_init_mask;       /* used during DSP boot */
        const u64 attributes;           /* bitmask of AVS_PLATATTR_* */
-       const u32 sram_base_offset;
-       const u32 sram_window_size;
-       const u32 rom_status;
+       const struct avs_sram_spec *sram;
+       const struct avs_hipc_spec *hipc;
 };
 
 struct avs_fw_entry {
@@ -127,6 +146,7 @@ struct avs_dev {
        int *core_refs;         /* reference count per core */
        char **lib_names;
        int num_lp_paths;
+       atomic_t l1sen_counter; /* controls whether L1SEN should be disabled */
 
        struct completion fw_ready;
        struct work_struct probe_work;
@@ -225,8 +245,7 @@ struct avs_ipc {
 #define AVS_IPC_RET(ret) \
        (((ret) <= 0) ? (ret) : -AVS_EIPC)
 
-irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id);
-irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id);
+irqreturn_t avs_irq_handler(struct avs_dev *adev);
 void avs_dsp_process_response(struct avs_dev *adev, u64 header);
 int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
                             struct avs_ipc_msg *reply, int timeout, const char *name);
@@ -248,7 +267,20 @@ void avs_ipc_block(struct avs_ipc *ipc);
 int avs_dsp_disable_d0ix(struct avs_dev *adev);
 int avs_dsp_enable_d0ix(struct avs_dev *adev);
 
-int skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+irqreturn_t avs_skl_irq_thread(struct avs_dev *adev);
+irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev);
+int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                       u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
+int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                       u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg);
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg);
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable);
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable);
 
 /* Firmware resources management */
 
@@ -293,6 +325,8 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
 int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
                             struct avs_module_entry *mods, u32 num_mods);
 
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw);
+
 /* Soc component members */
 
 struct avs_soc_component {
@@ -342,21 +376,21 @@ static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_n
        return ret;
 }
 
-struct apl_log_buffer_layout {
+struct avs_apl_log_buffer_layout {
        u32 read_ptr;
        u32 write_ptr;
        u8 buffer[];
 } __packed;
 
-#define apl_log_payload_size(adev) \
-       (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout))
+#define avs_apl_log_payload_size(adev) \
+       (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
 
-#define apl_log_payload_addr(addr) \
-       (addr + sizeof(struct apl_log_buffer_layout))
+#define avs_apl_log_payload_addr(addr) \
+       (addr + sizeof(struct avs_apl_log_buffer_layout))
 
 #ifdef CONFIG_DEBUG_FS
 #define AVS_SET_ENABLE_LOGS_OP(name) \
-       .enable_logs = name##_enable_logs
+       .enable_logs = avs_##name##_enable_logs
 
 bool avs_logging_fw(struct avs_dev *adev);
 void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
@@ -392,4 +426,8 @@ static inline void avs_debugfs_init(struct avs_dev *adev) { }
 static inline void avs_debugfs_exit(struct avs_dev *adev) { }
 #endif
 
+/* Filesystems integration */
+
+extern const struct attribute_group *avs_attr_groups[];
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
index 8e91eece992db6b56565c47d0f69a1ff1b497116..8360ce557401c646a1f02bf1d7bf4cec811016c0 100644 (file)
@@ -236,6 +236,82 @@ static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
        {},
 };
 
+static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = {
+       {
+               .id = "INT34C2",
+               .drv_name = "avs_rt274",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt274-tplg.bin",
+       },
+       {
+               .id = "10EC5682",
+               .drv_name = "avs_rt5682",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "rt5682-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = {
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt298",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt298-tplg.bin",
+       },
+       {
+               .id = "INT34C2",
+               .drv_name = "avs_rt274",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt274-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
+       {
+               .id = "INT34C2",
+               .drv_name = "avs_rt274",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt274-tplg.bin",
+       },
+       {
+               .id = "10EC0298",
+               .drv_name = "avs_rt298",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt298-tplg.bin",
+       },
+       {
+               .id = "10EC1308",
+               .drv_name = "avs_rt1308",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "rt1308-tplg.bin",
+       },
+       {
+               .id = "ESSX8336",
+               .drv_name = "avs_es8336",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "es8336-tplg.bin",
+       },
+       {},
+};
+
 static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
        {
                .drv_name = "avs_i2s_test",
@@ -296,6 +372,15 @@ static const struct avs_acpi_boards i2s_boards[] = {
        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_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),
+       AVS_MACH_ENTRY(HDA_ICL_LP,      avs_icl_i2s_machines),
+       AVS_MACH_ENTRY(HDA_TGL_LP,      avs_tgl_i2s_machines),
+       AVS_MACH_ENTRY(HDA_EHL_0,       avs_tgl_i2s_machines),
+       AVS_MACH_ENTRY(HDA_ADL_P,       avs_tgl_i2s_machines),
+       AVS_MACH_ENTRY(HDA_RPL_P_0,     avs_tgl_i2s_machines),
+       AVS_MACH_ENTRY(HDA_RPL_M,       avs_tgl_i2s_machines),
        {},
 };
 
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
new file mode 100644 (file)
index 0000000..5423c29
--- /dev/null
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+
+irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev)
+{
+       union avs_reply_msg msg;
+       u32 hipctdr, hipctdd, hipctda;
+
+       hipctdr = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR);
+       hipctdd = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD);
+
+       /* Ensure DSP sent new response to process. */
+       if (!(hipctdr & CNL_ADSP_HIPCTDR_BUSY))
+               return IRQ_NONE;
+
+       msg.primary = hipctdr;
+       msg.ext.val = hipctdd;
+       avs_dsp_process_response(adev, msg.val);
+
+       /* Tell DSP we accepted its message. */
+       snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR,
+                             CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY);
+       /* Ack this response. */
+       snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA,
+                             CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE);
+       /* HW might have been clock gated, give some time for change to propagate. */
+       snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda,
+                                !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000);
+       /* Unmask busy interrupt. */
+       snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCCTL,
+                             AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY);
+
+       return IRQ_HANDLED;
+}
+
+const struct avs_dsp_ops avs_cnl_dsp_ops = {
+       .power = avs_dsp_core_power,
+       .reset = avs_dsp_core_reset,
+       .stall = avs_dsp_core_stall,
+       .irq_handler = avs_irq_handler,
+       .irq_thread = avs_cnl_irq_thread,
+       .int_control = avs_dsp_interrupt_control,
+       .load_basefw = avs_hda_load_basefw,
+       .load_lib = avs_hda_load_library,
+       .transfer_mods = avs_hda_transfer_modules,
+       .log_buffer_offset = avs_skl_log_buffer_offset,
+       .log_buffer_status = avs_apl_log_buffer_status,
+       .coredump = avs_apl_coredump,
+       .d0ix_toggle = avs_apl_d0ix_toggle,
+       .set_d0ix = avs_apl_set_d0ix,
+       AVS_SET_ENABLE_LOGS_OP(apl)
+};
index db78eb2f0108071736b6bafa5e77f874b15f3375..d7f8940099cec6510c1179b7045134243767625d 100644 (file)
@@ -69,9 +69,14 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
 
 void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
 {
-       u32 value = enable ? AZX_VS_EM2_L1SEN : 0;
-
-       snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
+       if (enable) {
+               if (atomic_inc_and_test(&adev->l1sen_counter))
+                       snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN,
+                                             AZX_VS_EM2_L1SEN);
+       } else {
+               if (atomic_dec_return(&adev->l1sen_counter) == -1)
+                       snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, 0);
+       }
 }
 
 static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
@@ -144,7 +149,7 @@ static int probe_codec(struct hdac_bus *bus, int addr)
        /* configure effectively creates new ASoC component */
        ret = snd_hda_codec_configure(codec);
        if (ret < 0) {
-               dev_err(bus->dev, "failed to config codec %d\n", ret);
+               dev_warn(bus->dev, "failed to config codec #%d: %d\n", addr, ret);
                return ret;
        }
 
@@ -153,15 +158,16 @@ static int probe_codec(struct hdac_bus *bus, int addr)
 
 static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
 {
-       int c;
+       int ret, c;
 
        /* First try to probe all given codec slots */
        for (c = 0; c < HDA_MAX_CODECS; c++) {
                if (!(bus->codec_mask & BIT(c)))
                        continue;
 
-               if (!probe_codec(bus, c))
-                       /* success, continue probing */
+               ret = probe_codec(bus, c);
+               /* Ignore codecs with no supporting driver. */
+               if (!ret || ret == -ENODEV)
                        continue;
 
                /*
@@ -316,6 +322,20 @@ static irqreturn_t hdac_bus_irq_thread(int irq, void *context)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
+{
+       struct avs_dev *adev = dev_id;
+
+       return avs_dsp_op(adev, irq_handler);
+}
+
+static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
+{
+       struct avs_dev *adev = dev_id;
+
+       return avs_dsp_op(adev, irq_thread);
+}
+
 static int avs_hdac_acquire_irq(struct avs_dev *adev)
 {
        struct hdac_bus *bus = &adev->base.core;
@@ -725,38 +745,107 @@ static const struct dev_pm_ops avs_dev_pm = {
        SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
 };
 
+static const struct avs_sram_spec skl_sram_spec = {
+       .base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+       .window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
+       .rom_status_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_sram_spec apl_sram_spec = {
+       .base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+       .window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+       .rom_status_offset = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec skl_hipc_spec = {
+       .req_offset = SKL_ADSP_REG_HIPCI,
+       .req_ext_offset = SKL_ADSP_REG_HIPCIE,
+       .req_busy_mask = SKL_ADSP_HIPCI_BUSY,
+       .ack_offset = SKL_ADSP_REG_HIPCIE,
+       .ack_done_mask = SKL_ADSP_HIPCIE_DONE,
+       .rsp_offset = SKL_ADSP_REG_HIPCT,
+       .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY,
+       .ctl_offset = SKL_ADSP_REG_HIPCCTL,
+};
+
+static const struct avs_hipc_spec cnl_hipc_spec = {
+       .req_offset = CNL_ADSP_REG_HIPCIDR,
+       .req_ext_offset = CNL_ADSP_REG_HIPCIDD,
+       .req_busy_mask = CNL_ADSP_HIPCIDR_BUSY,
+       .ack_offset = CNL_ADSP_REG_HIPCIDA,
+       .ack_done_mask = CNL_ADSP_HIPCIDA_DONE,
+       .rsp_offset = CNL_ADSP_REG_HIPCTDR,
+       .rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY,
+       .ctl_offset = CNL_ADSP_REG_HIPCCTL,
+};
+
 static const struct avs_spec skl_desc = {
        .name = "skl",
-       .min_fw_version = {
-               .major = 9,
-               .minor = 21,
-               .hotfix = 0,
-               .build = 4732,
-       },
-       .dsp_ops = &skl_dsp_ops,
+       .min_fw_version = { 9, 21, 0, 4732 },
+       .dsp_ops = &avs_skl_dsp_ops,
        .core_init_mask = 1,
        .attributes = AVS_PLATATTR_CLDMA,
-       .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
-       .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
-       .rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
+       .sram = &skl_sram_spec,
+       .hipc = &skl_hipc_spec,
 };
 
 static const struct avs_spec apl_desc = {
        .name = "apl",
-       .min_fw_version = {
-               .major = 9,
-               .minor = 22,
-               .hotfix = 1,
-               .build = 4323,
-       },
-       .dsp_ops = &apl_dsp_ops,
+       .min_fw_version = { 9, 22, 1, 4323 },
+       .dsp_ops = &avs_apl_dsp_ops,
        .core_init_mask = 3,
        .attributes = AVS_PLATATTR_IMR,
-       .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET,
-       .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE,
-       .rom_status = APL_ADSP_SRAM_BASE_OFFSET,
+       .sram = &apl_sram_spec,
+       .hipc = &skl_hipc_spec,
+};
+
+static const struct avs_spec cnl_desc = {
+       .name = "cnl",
+       .min_fw_version = { 10, 23, 0, 5314 },
+       .dsp_ops = &avs_cnl_dsp_ops,
+       .core_init_mask = 1,
+       .attributes = AVS_PLATATTR_IMR,
+       .sram = &apl_sram_spec,
+       .hipc = &cnl_hipc_spec,
+};
+
+static const struct avs_spec icl_desc = {
+       .name = "icl",
+       .min_fw_version = { 10, 23, 0, 5040 },
+       .dsp_ops = &avs_icl_dsp_ops,
+       .core_init_mask = 1,
+       .attributes = AVS_PLATATTR_IMR,
+       .sram = &apl_sram_spec,
+       .hipc = &cnl_hipc_spec,
 };
 
+static const struct avs_spec jsl_desc = {
+       .name = "jsl",
+       .min_fw_version = { 10, 26, 0, 5872 },
+       .dsp_ops = &avs_icl_dsp_ops,
+       .core_init_mask = 1,
+       .attributes = AVS_PLATATTR_IMR,
+       .sram = &apl_sram_spec,
+       .hipc = &cnl_hipc_spec,
+};
+
+#define AVS_TGL_BASED_SPEC(sname)              \
+static const struct avs_spec sname##_desc = {  \
+       .name = #sname,                         \
+       .min_fw_version = { 10, 29, 0, 5646 },  \
+       .dsp_ops = &avs_tgl_dsp_ops,            \
+       .core_init_mask = 1,                    \
+       .attributes = AVS_PLATATTR_IMR,         \
+       .sram = &apl_sram_spec,                 \
+       .hipc = &cnl_hipc_spec,                 \
+}
+
+AVS_TGL_BASED_SPEC(lkf);
+AVS_TGL_BASED_SPEC(tgl);
+AVS_TGL_BASED_SPEC(ehl);
+AVS_TGL_BASED_SPEC(adl);
+AVS_TGL_BASED_SPEC(adl_n);
+
 static const struct pci_device_id avs_ids[] = {
        { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) },
        { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) },
@@ -766,6 +855,32 @@ static const struct pci_device_id avs_ids[] = {
        { 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_CNL_LP,    &cnl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_CNL_H,     &cnl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_CML_LP,    &cnl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_CML_H,     &cnl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RKL_S,     &cnl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP,    &icl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ICL_N,     &icl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ICL_H,     &icl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_JSL_N,     &jsl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_LKF,       &lkf_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP,    &tgl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_TGL_H,     &tgl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_CML_R,     &tgl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_EHL_0,     &ehl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_EHL_3,     &ehl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_S,     &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_P,     &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS,    &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_M,     &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX,    &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_ADL_N,     &adl_n_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RPL_S,     &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0,   &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1,   &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RPL_M,     &adl_desc) },
+       { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX,    &adl_desc) },
        { 0 }
 };
 MODULE_DEVICE_TABLE(pci, avs_ids);
@@ -776,6 +891,7 @@ static struct pci_driver avs_pci_driver = {
        .probe = avs_pci_probe,
        .remove = avs_pci_remove,
        .shutdown = avs_pci_shutdown,
+       .dev_groups = avs_attr_groups,
        .driver = {
                .pm = &avs_dev_pm,
        },
diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c
new file mode 100644 (file)
index 0000000..9d9921e
--- /dev/null
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/slab.h>
+#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+
+#define ICL_VS_LTRP_GB_ICCMAX  95
+
+#ifdef CONFIG_DEBUG_FS
+int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                       u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+       struct avs_icl_log_state_info *info;
+       u32 size, num_libs = adev->fw_cfg.max_libs_count;
+       int i, ret;
+
+       if (fls_long(resource_mask) > num_libs)
+               return -EINVAL;
+       size = struct_size(info, logs_priorities_mask, num_libs);
+       info = kzalloc(size, GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->aging_timer_period = aging_period;
+       info->fifo_full_timer_period = fifo_full_period;
+       info->enable = enable;
+       if (enable)
+               for_each_set_bit(i, &resource_mask, num_libs)
+                       info->logs_priorities_mask[i] = *priorities++;
+
+       ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+       kfree(info);
+       if (ret)
+               return AVS_IPC_RET(ret);
+
+       return 0;
+}
+#endif
+
+union avs_icl_memwnd2_slot_type {
+       u32 val;
+       struct {
+               u32 resource_id:8;
+               u32 type:24;
+       };
+} __packed;
+
+struct avs_icl_memwnd2_desc {
+       u32 resource_id;
+       union avs_icl_memwnd2_slot_type slot_id;
+       u32 vma;
+} __packed;
+
+#define AVS_ICL_MEMWND2_SLOTS_COUNT    15
+
+struct avs_icl_memwnd2 {
+       union {
+               struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT];
+               u8 rsvd[PAGE_SIZE];
+       };
+       u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE];
+} __packed;
+
+#define AVS_ICL_SLOT_UNUSED \
+       ((union avs_icl_memwnd2_slot_type) { 0x00000000U })
+#define AVS_ICL_SLOT_CRITICAL_LOG \
+       ((union avs_icl_memwnd2_slot_type) { 0x54524300U })
+#define AVS_ICL_SLOT_DEBUG_LOG \
+       ((union avs_icl_memwnd2_slot_type) { 0x474f4c00U })
+#define AVS_ICL_SLOT_GDB_STUB \
+       ((union avs_icl_memwnd2_slot_type) { 0x42444700U })
+#define AVS_ICL_SLOT_BROKEN \
+       ((union avs_icl_memwnd2_slot_type) { 0x44414544U })
+
+static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_type slot_type)
+{
+       struct avs_icl_memwnd2_desc desc[AVS_ICL_MEMWND2_SLOTS_COUNT];
+       int i;
+
+       memcpy_fromio(&desc, avs_sram_addr(adev, AVS_DEBUG_WINDOW), sizeof(desc));
+
+       for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++)
+               if (desc[i].slot_id.val == slot_type.val)
+                       return offsetof(struct avs_icl_memwnd2, slot_array) +
+                              avs_skl_log_buffer_offset(adev, i);
+       return -ENXIO;
+}
+
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+       union avs_icl_memwnd2_slot_type slot_type = AVS_ICL_SLOT_DEBUG_LOG;
+       int ret;
+
+       slot_type.resource_id = core;
+       ret = avs_icl_slot_offset(adev, slot_type);
+       if (ret < 0)
+               dev_dbg(adev->dev, "No slot offset found for: %x\n",
+                       slot_type.val);
+
+       return ret;
+}
+
+bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+       /* Payload-less IPCs do not take part in d0ix toggling. */
+       return tx->size;
+}
+
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+       int ret;
+
+       ret = avs_ipc_set_d0ix(adev, enable, false);
+       return AVS_IPC_RET(ret);
+}
+
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+       struct hdac_bus *bus = &adev->base.core;
+       struct hdac_ext_stream *host_stream;
+       struct snd_pcm_substream substream;
+       struct snd_dma_buffer dmab;
+       unsigned int sd_fmt;
+       u8 ltrp_gb;
+       int ret;
+
+       /*
+        * ICCMAX:
+        *
+        * For ICL+ platforms, as per HW recommendation LTRP_GB is set to 95us
+        * during FW load. Its original value shall be restored once load completes.
+        *
+        * To avoid DMI/OPIO L1 entry during the load procedure, additional CAPTURE
+        * stream is allocated and set to run.
+        */
+
+       memset(&substream, 0, sizeof(substream));
+       substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+
+       host_stream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST);
+       if (!host_stream)
+               return -EBUSY;
+
+       ltrp_gb = snd_hdac_chip_readb(bus, VS_LTRP) & AZX_REG_VS_LTRP_GB_MASK;
+       /* Carries no real data, use default format. */
+       sd_fmt = snd_hdac_stream_format(1, 32, 48000);
+
+       ret = snd_hdac_dsp_prepare(hdac_stream(host_stream), sd_fmt, fw->size, &dmab);
+       if (ret < 0)
+               goto release_stream;
+
+       snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ICL_VS_LTRP_GB_ICCMAX);
+
+       spin_lock(&bus->reg_lock);
+       snd_hdac_stream_start(hdac_stream(host_stream));
+       spin_unlock(&bus->reg_lock);
+
+       ret = avs_hda_load_basefw(adev, fw);
+
+       spin_lock(&bus->reg_lock);
+       snd_hdac_stream_stop(hdac_stream(host_stream));
+       spin_unlock(&bus->reg_lock);
+
+       snd_hdac_dsp_cleanup(hdac_stream(host_stream), &dmab);
+
+release_stream:
+       snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+       snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ltrp_gb);
+
+       return ret;
+}
+
+const struct avs_dsp_ops avs_icl_dsp_ops = {
+       .power = avs_dsp_core_power,
+       .reset = avs_dsp_core_reset,
+       .stall = avs_dsp_core_stall,
+       .irq_handler = avs_irq_handler,
+       .irq_thread = avs_cnl_irq_thread,
+       .int_control = avs_dsp_interrupt_control,
+       .load_basefw = avs_icl_load_basefw,
+       .load_lib = avs_hda_load_library,
+       .transfer_mods = avs_hda_transfer_modules,
+       .log_buffer_offset = avs_icl_log_buffer_offset,
+       .log_buffer_status = avs_apl_log_buffer_status,
+       .coredump = avs_apl_coredump,
+       .d0ix_toggle = avs_icl_d0ix_toggle,
+       .set_d0ix = avs_icl_set_d0ix,
+       AVS_SET_ENABLE_LOGS_OP(icl)
+};
index 65bfc83bd1f06bc435edf9d1a94466dcaae41917..ad0e535b3c2ed21650f7b9897874b538f0e10db3 100644 (file)
@@ -301,10 +301,10 @@ void avs_dsp_process_response(struct avs_dev *adev, u64 header)
        complete(&ipc->busy_completion);
 }
 
-irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
+irqreturn_t avs_irq_handler(struct avs_dev *adev)
 {
-       struct avs_dev *adev = dev_id;
        struct avs_ipc *ipc = adev->ipc;
+       const struct avs_spec *const spec = adev->spec;
        u32 adspis, hipc_rsp, hipc_ack;
        irqreturn_t ret = IRQ_NONE;
 
@@ -312,35 +312,35 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
        if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC))
                return ret;
 
-       hipc_ack = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCIE);
-       hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
+       hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset);
+       hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
 
        /* DSP acked host's request */
-       if (hipc_ack & SKL_ADSP_HIPCIE_DONE) {
+       if (hipc_ack & spec->hipc->ack_done_mask) {
                /*
                 * As an extra precaution, mask done interrupt. Code executed
                 * due to complete() found below does not assume any masking.
                 */
-               snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
+               snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
                                      AVS_ADSP_HIPCCTL_DONE, 0);
 
                complete(&ipc->done_completion);
 
                /* tell DSP it has our attention */
-               snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE,
-                                     SKL_ADSP_HIPCIE_DONE,
-                                     SKL_ADSP_HIPCIE_DONE);
+               snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset,
+                                     spec->hipc->ack_done_mask,
+                                     spec->hipc->ack_done_mask);
                /* unmask done interrupt */
-               snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
+               snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
                                      AVS_ADSP_HIPCCTL_DONE,
                                      AVS_ADSP_HIPCCTL_DONE);
                ret = IRQ_HANDLED;
        }
 
        /* DSP sent new response to process */
-       if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) {
+       if (hipc_rsp & spec->hipc->rsp_busy_mask) {
                /* mask busy interrupt */
-               snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
+               snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
                                      AVS_ADSP_HIPCCTL_BUSY, 0);
 
                ret = IRQ_WAKE_THREAD;
@@ -349,40 +349,14 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
        return ret;
 }
 
-irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
-{
-       struct avs_dev *adev = dev_id;
-       union avs_reply_msg msg;
-       u32 hipct, hipcte;
-
-       hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
-       hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE);
-
-       /* ensure DSP sent new response to process */
-       if (!(hipct & SKL_ADSP_HIPCT_BUSY))
-               return IRQ_NONE;
-
-       msg.primary = hipct;
-       msg.ext.val = hipcte;
-       avs_dsp_process_response(adev, msg.val);
-
-       /* tell DSP we accepted its message */
-       snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT,
-                             SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY);
-       /* unmask busy interrupt */
-       snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
-                             AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY);
-
-       return IRQ_HANDLED;
-}
-
 static bool avs_ipc_is_busy(struct avs_ipc *ipc)
 {
        struct avs_dev *adev = to_avs_dev(ipc->dev);
+       const struct avs_spec *const spec = adev->spec;
        u32 hipc_rsp;
 
-       hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
-       return hipc_rsp & SKL_ADSP_HIPCT_BUSY;
+       hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+       return hipc_rsp & spec->hipc->rsp_busy_mask;
 }
 
 static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
@@ -440,9 +414,10 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
 
 static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs)
 {
+       const struct avs_spec *const spec = adev->spec;
        u64 reg = ULONG_MAX;
 
-       tx->header |= SKL_ADSP_HIPCI_BUSY;
+       tx->header |= spec->hipc->req_busy_mask;
        if (read_fwregs)
                reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
 
@@ -450,8 +425,8 @@ static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool r
 
        if (tx->size)
                memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
-       snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32);
-       snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX);
+       snd_hdac_adsp_writel(adev, spec->hipc->req_ext_offset, tx->header >> 32);
+       snd_hdac_adsp_writel(adev, spec->hipc->req_offset, tx->header & UINT_MAX);
 }
 
 static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
@@ -606,6 +581,7 @@ int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, cons
 
 void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
 {
+       const struct avs_spec *const spec = adev->spec;
        u32 value, mask;
 
        /*
@@ -617,7 +593,7 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
 
        mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY;
        value = enable ? mask : 0;
-       snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value);
+       snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, mask, value);
 }
 
 int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
index e83ce6a35755d01a80ab86565bd138b4098b69f3..8e34d3536082aac69ae11d72748e1a245b0a68b5 100644 (file)
@@ -306,7 +306,7 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge)
        }
 
        /* await ROM init */
-       ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg,
+       ret = snd_hdac_adsp_readq_poll(adev, spec->sram->rom_status_offset, reg,
                                       (reg & 0xF) == AVS_ROM_INIT_DONE ||
                                       (reg & 0xF) == APL_ROM_FW_ENTERED,
                                       AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US);
index 06b4394cabd2042e9859f7abfcc3a7e18106344d..f874e4f0d95f454ba9e885c87777683ac4972d27 100644 (file)
@@ -381,6 +381,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
 
        msg.ext.set_d0ix.wake = enable_pg;
        msg.ext.set_d0ix.streaming = streaming;
+       msg.ext.set_d0ix.prevent_pg = !enable_pg;
 
        request.header = msg.val;
 
index d0344e242e5b58aed4de0e5c444c05d8c0a5e8e3..4e609a08863c8cb9eb14e9a83af555882e82a7be 100644 (file)
@@ -145,8 +145,12 @@ union avs_module_msg {
                                u32 src_queue:3;
                        } bind_unbind;
                        struct {
+                               /* pre-IceLake */
                                u32 wake:1;
                                u32 streaming:1;
+                               /* IceLake and onwards */
+                               u32 prevent_pg:1;
+                               u32 prevent_local_cg:1;
                        } set_d0ix;
                } ext;
        };
@@ -359,21 +363,45 @@ enum avs_skl_log_priority {
        AVS_SKL_LOG_VERBOSE,
 };
 
-struct skl_log_state {
+struct avs_skl_log_state {
        u32 enable;
        u32 min_priority;
 } __packed;
 
-struct skl_log_state_info {
+struct avs_skl_log_state_info {
        u32 core_mask;
-       struct skl_log_state logs_core[];
+       struct avs_skl_log_state logs_core[];
 } __packed;
 
-struct apl_log_state_info {
+struct avs_apl_log_state_info {
        u32 aging_timer_period;
        u32 fifo_full_timer_period;
        u32 core_mask;
-       struct skl_log_state logs_core[];
+       struct avs_skl_log_state logs_core[];
+} __packed;
+
+enum avs_icl_log_priority {
+       AVS_ICL_LOG_CRITICAL = 0,
+       AVS_ICL_LOG_HIGH,
+       AVS_ICL_LOG_MEDIUM,
+       AVS_ICL_LOG_LOW,
+       AVS_ICL_LOG_VERBOSE,
+};
+
+enum avs_icl_log_source {
+       AVS_ICL_LOG_INFRA = 0,
+       AVS_ICL_LOG_HAL,
+       AVS_ICL_LOG_MODULE,
+       AVS_ICL_LOG_AUDIO,
+       AVS_ICL_LOG_SENSING,
+       AVS_ICL_LOG_ULP_INFRA,
+};
+
+struct avs_icl_log_state_info {
+       u32 aging_timer_period;
+       u32 fifo_full_timer_period;
+       u32 enable;
+       u32 logs_priorities_mask[];
 } __packed;
 
 int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
index 3aa16ee8d34c1f0dbbab6154aa5aba8fb946e47b..e785fc2a7008fab675225f361c963f9fa918d293 100644 (file)
@@ -547,6 +547,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod
        return avs_modext_create(adev, mod);
 }
 
+static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_soc_component *acomp;
+
+       acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp);
+
+       u32 num_ids = mod->template->num_config_ids;
+       u32 *ids = mod->template->config_ids;
+
+       for (int i = 0; i < num_ids; i++) {
+               struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]];
+               size_t len = config->length;
+               void *data = config->data;
+               u32 param = config->param;
+               int ret;
+
+               ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id,
+                                              param, data, len);
+               if (ret) {
+                       dev_err(adev->dev, "send initial module config failed: %d\n", ret);
+                       return AVS_IPC_RET(ret);
+               }
+       }
+
+       return 0;
+}
+
 static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
 {
        kfree(mod);
@@ -580,6 +607,12 @@ avs_path_module_create(struct avs_dev *adev,
                return ERR_PTR(ret);
        }
 
+       ret = avs_path_module_send_init_configs(adev, mod);
+       if (ret) {
+               kfree(mod);
+               return ERR_PTR(ret);
+       }
+
        return mod;
 }
 
index 4dfc5a1ebb7c26803cb9b12e60aa6a814aa1adf0..2cafbc392cdbe50695150e61d835d29b82bfa0a4 100644 (file)
@@ -643,6 +643,79 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
        return 0;
 }
 
+static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+       struct hdac_stream *first_running = NULL;
+       struct hdac_stream *pos;
+       struct avs_dev *adev = hdac_to_avs(bus);
+
+       list_for_each_entry(pos, &bus->stream_list, list) {
+               if (pos->running) {
+                       if (first_running)
+                               break; /* more than one running */
+                       first_running = pos;
+               }
+       }
+
+       /*
+        * If host_stream is a CAPTURE stream and will be the only one running,
+        * disable L1SEN to avoid sound clipping.
+        */
+       if (!first_running) {
+               if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+                       avs_hda_l1sen_enable(adev, false);
+               snd_hdac_stream_start(hdac_stream(host_stream));
+               return;
+       }
+
+       snd_hdac_stream_start(hdac_stream(host_stream));
+       /*
+        * If host_stream is the first stream to break the rule above,
+        * re-enable L1SEN.
+        */
+       if (list_entry_is_head(pos, &bus->stream_list, list) &&
+           first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+               avs_hda_l1sen_enable(adev, true);
+}
+
+static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+       struct hdac_stream *first_running = NULL;
+       struct hdac_stream *pos;
+       struct avs_dev *adev = hdac_to_avs(bus);
+
+       list_for_each_entry(pos, &bus->stream_list, list) {
+               if (pos == hdac_stream(host_stream))
+                       continue; /* ignore stream that is about to be stopped */
+               if (pos->running) {
+                       if (first_running)
+                               break; /* more than one running */
+                       first_running = pos;
+               }
+       }
+
+       /*
+        * If host_stream is a CAPTURE stream and is the only one running,
+        * re-enable L1SEN.
+        */
+       if (!first_running) {
+               snd_hdac_stream_stop(hdac_stream(host_stream));
+               if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+                       avs_hda_l1sen_enable(adev, true);
+               return;
+       }
+
+       /*
+        * If by stopping host_stream there is only a single, CAPTURE stream running
+        * left, disable L1SEN to avoid sound clipping.
+        */
+       if (list_entry_is_head(pos, &bus->stream_list, list) &&
+           first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+               avs_hda_l1sen_enable(adev, false);
+
+       snd_hdac_stream_stop(hdac_stream(host_stream));
+}
+
 static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -664,7 +737,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                spin_lock_irqsave(&bus->reg_lock, flags);
-               snd_hdac_stream_start(hdac_stream(host_stream));
+               avs_hda_stream_start(bus, host_stream);
                spin_unlock_irqrestore(&bus->reg_lock, flags);
 
                /* Timeout on DRSM poll shall not stop the resume so ignore the result. */
@@ -694,7 +767,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
                        dev_err(dai->dev, "pause FE path failed: %d\n", ret);
 
                spin_lock_irqsave(&bus->reg_lock, flags);
-               snd_hdac_stream_stop(hdac_stream(host_stream));
+               avs_hda_stream_stop(bus, host_stream);
                spin_unlock_irqrestore(&bus->reg_lock, flags);
 
                ret = avs_path_reset(data->path);
index 078a0ebafa42e610dbffe7a31b28124ecad5f2f7..6126adca500ceffbcef66de72713731872805d3f 100644 (file)
 #define SKL_ADSP_HIPCIE_DONE           BIT(30)
 #define SKL_ADSP_HIPCT_BUSY            BIT(31)
 
+/* CNL Intel HD Audio Inter-Processor Communication Registers */
+#define CNL_ADSP_IPC_BASE               0xC0
+#define CNL_ADSP_REG_HIPCTDR            (CNL_ADSP_IPC_BASE + 0x00)
+#define CNL_ADSP_REG_HIPCTDA            (CNL_ADSP_IPC_BASE + 0x04)
+#define CNL_ADSP_REG_HIPCTDD            (CNL_ADSP_IPC_BASE + 0x08)
+#define CNL_ADSP_REG_HIPCIDR            (CNL_ADSP_IPC_BASE + 0x10)
+#define CNL_ADSP_REG_HIPCIDA            (CNL_ADSP_IPC_BASE + 0x14)
+#define CNL_ADSP_REG_HIPCIDD            (CNL_ADSP_IPC_BASE + 0x18)
+#define CNL_ADSP_REG_HIPCCTL            (CNL_ADSP_IPC_BASE + 0x28)
+
+#define CNL_ADSP_HIPCTDR_BUSY          BIT(31)
+#define CNL_ADSP_HIPCTDA_DONE          BIT(31)
+#define CNL_ADSP_HIPCIDR_BUSY          BIT(31)
+#define CNL_ADSP_HIPCIDA_DONE          BIT(31)
+
 /* Intel HD Audio SRAM windows base addresses */
 #define SKL_ADSP_SRAM_BASE_OFFSET      0x8000
 #define SKL_ADSP_SRAM_WINDOW_SIZE      0x2000
@@ -57,7 +72,7 @@
 #define APL_ADSP_SRAM_WINDOW_SIZE      0x20000
 
 /* Constants used when accessing SRAM, space shared with firmware */
-#define AVS_FW_REG_BASE(adev)          ((adev)->spec->sram_base_offset)
+#define AVS_FW_REG_BASE(adev)          ((adev)->spec->sram->base_offset)
 #define AVS_FW_REG_STATUS(adev)                (AVS_FW_REG_BASE(adev) + 0x0)
 #define AVS_FW_REG_ERROR_CODE(adev)    (AVS_FW_REG_BASE(adev) + 0x4)
 
@@ -72,8 +87,8 @@
 
 /* registry I/O helpers */
 #define avs_sram_offset(adev, window_idx) \
-       ((adev)->spec->sram_base_offset + \
-        (adev)->spec->sram_window_size * (window_idx))
+       ((adev)->spec->sram->base_offset + \
+        (adev)->spec->sram->window_size * (window_idx))
 
 #define avs_sram_addr(adev, window_idx) \
        ((adev)->dsp_ba + avs_sram_offset(adev, window_idx))
index 6bb8bbc70442bdf380c28c80b512c0e95f5a4728..d19f8953993f4365556bea2fb2b0f3b9a359d90e 100644 (file)
 #include "avs.h"
 #include "messages.h"
 
+irqreturn_t avs_skl_irq_thread(struct avs_dev *adev)
+{
+       union avs_reply_msg msg;
+       u32 hipct, hipcte;
+
+       hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
+       hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE);
+
+       /* Ensure DSP sent new response to process. */
+       if (!(hipct & SKL_ADSP_HIPCT_BUSY))
+               return IRQ_NONE;
+
+       msg.primary = hipct;
+       msg.ext.val = hipcte;
+       avs_dsp_process_response(adev, msg.val);
+
+       /* Tell DSP we accepted its message. */
+       snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY);
+       /* Unmask busy interrupt. */
+       snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, AVS_ADSP_HIPCCTL_BUSY,
+                             AVS_ADSP_HIPCCTL_BUSY);
+
+       return IRQ_HANDLED;
+}
+
 static int __maybe_unused
-skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
-               u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                   u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
 {
-       struct skl_log_state_info *info;
+       struct avs_skl_log_state_info *info;
        u32 size, num_cores = adev->hw_cfg.dsp_cores;
        int ret, i;
 
@@ -45,7 +70,7 @@ skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri
        return 0;
 }
 
-int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core)
 {
        return core * avs_log_buffer_size(adev);
 }
@@ -53,8 +78,7 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
 /* fw DbgLogWp registers */
 #define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
 
-static int
-skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
 {
        void __iomem *buf;
        u16 size, write, offset;
@@ -74,7 +98,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
        return 0;
 }
 
-static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
 {
        u8 *dump;
 
@@ -88,33 +112,32 @@ static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
        return 0;
 }
 
-static bool
-skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
 {
        /* unsupported on cAVS 1.5 hw */
        return false;
 }
 
-static int skl_set_d0ix(struct avs_dev *adev, bool enable)
+static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable)
 {
        /* unsupported on cAVS 1.5 hw */
        return 0;
 }
 
-const struct avs_dsp_ops skl_dsp_ops = {
+const struct avs_dsp_ops avs_skl_dsp_ops = {
        .power = avs_dsp_core_power,
        .reset = avs_dsp_core_reset,
        .stall = avs_dsp_core_stall,
-       .irq_handler = avs_dsp_irq_handler,
-       .irq_thread = avs_dsp_irq_thread,
+       .irq_handler = avs_irq_handler,
+       .irq_thread = avs_skl_irq_thread,
        .int_control = avs_dsp_interrupt_control,
        .load_basefw = avs_cldma_load_basefw,
        .load_lib = avs_cldma_load_library,
        .transfer_mods = avs_cldma_transfer_modules,
-       .log_buffer_offset = skl_log_buffer_offset,
-       .log_buffer_status = skl_log_buffer_status,
-       .coredump = skl_coredump,
-       .d0ix_toggle = skl_d0ix_toggle,
-       .set_d0ix = skl_set_d0ix,
+       .log_buffer_offset = avs_skl_log_buffer_offset,
+       .log_buffer_status = avs_skl_log_buffer_status,
+       .coredump = avs_skl_coredump,
+       .d0ix_toggle = avs_skl_d0ix_toggle,
+       .set_d0ix = avs_skl_set_d0ix,
        AVS_SET_ENABLE_LOGS_OP(skl)
 };
diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c
new file mode 100644 (file)
index 0000000..cce2163
--- /dev/null
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/sysfs.h>
+#include "avs.h"
+
+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct avs_dev *adev = to_avs_dev(dev);
+       struct avs_fw_version *fw_version = &adev->fw_cfg.fw_version;
+
+       return sysfs_emit(buf, "%d.%d.%d.%d\n", fw_version->major, fw_version->minor,
+                         fw_version->hotfix, fw_version->build);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static struct attribute *avs_fw_attrs[] = {
+       &dev_attr_fw_version.attr,
+       NULL
+};
+
+static const struct attribute_group avs_attr_group = {
+       .name = "avs",
+       .attrs = avs_fw_attrs,
+};
+
+const struct attribute_group *avs_attr_groups[] = {
+       &avs_attr_group,
+       NULL
+};
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
new file mode 100644 (file)
index 0000000..0e052e7
--- /dev/null
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include "avs.h"
+
+static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+       core_mask &= AVS_MAIN_CORE_MASK;
+
+       if (!core_mask)
+               return 0;
+       return avs_dsp_core_power(adev, core_mask, power);
+}
+
+static int avs_tgl_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
+{
+       core_mask &= AVS_MAIN_CORE_MASK;
+
+       if (!core_mask)
+               return 0;
+       return avs_dsp_core_reset(adev, core_mask, reset);
+}
+
+static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+       core_mask &= AVS_MAIN_CORE_MASK;
+
+       if (!core_mask)
+               return 0;
+       return avs_dsp_core_stall(adev, core_mask, stall);
+}
+
+const struct avs_dsp_ops avs_tgl_dsp_ops = {
+       .power = avs_tgl_dsp_core_power,
+       .reset = avs_tgl_dsp_core_reset,
+       .stall = avs_tgl_dsp_core_stall,
+       .irq_handler = avs_irq_handler,
+       .irq_thread = avs_cnl_irq_thread,
+       .int_control = avs_dsp_interrupt_control,
+       .load_basefw = avs_icl_load_basefw,
+       .load_lib = avs_hda_load_library,
+       .transfer_mods = avs_hda_transfer_modules,
+       .log_buffer_offset = avs_icl_log_buffer_offset,
+       .log_buffer_status = avs_apl_log_buffer_status,
+       .coredump = avs_apl_coredump,
+       .d0ix_toggle = avs_icl_d0ix_toggle,
+       .set_d0ix = avs_icl_set_d0ix,
+       AVS_SET_ENABLE_LOGS_OP(icl)
+};
index 48b3c67c91032c97b7da54e9d822876f0e66b994..13061bd1488bb4dd506b14aed5c87967bc19aee6 100644 (file)
@@ -1118,6 +1118,21 @@ static const struct avs_tplg_token_parser module_parsers[] = {
                .offset = offsetof(struct avs_tplg_module, ctl_id),
                .parse = avs_parse_byte_token,
        },
+       {
+               .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_module, num_config_ids),
+               .parse = avs_parse_byte_token,
+       },
+};
+
+static const struct avs_tplg_token_parser init_config_parsers[] = {
+       {
+               .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = 0,
+               .parse = avs_parse_word_token,
+       },
 };
 
 static struct avs_tplg_module *
@@ -1125,17 +1140,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline
                       struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
 {
        struct avs_tplg_module *module;
+       u32 esize;
        int ret;
 
+       /* See where config id block starts. */
+       ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                        AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize);
+       if (ret)
+               return ERR_PTR(ret);
+
        module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
        if (!module)
                return ERR_PTR(-ENOMEM);
 
        ret = avs_parse_tokens(comp, module, module_parsers,
-                              ARRAY_SIZE(module_parsers), tuples, block_size);
+                              ARRAY_SIZE(module_parsers), tuples, esize);
        if (ret < 0)
                return ERR_PTR(ret);
 
+       block_size -= esize;
+       /* Parse trailing config ids if any. */
+       if (block_size) {
+               u32 num_config_ids = module->num_config_ids;
+               u32 *config_ids;
+
+               if (!num_config_ids)
+                       return ERR_PTR(-EINVAL);
+
+               config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids),
+                                          GFP_KERNEL);
+               if (!config_ids)
+                       return ERR_PTR(-ENOMEM);
+
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+               ret = parse_dictionary_entries(comp, tuples, block_size,
+                                              config_ids, num_config_ids, sizeof(*config_ids),
+                                              AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+                                              init_config_parsers,
+                                              ARRAY_SIZE(init_config_parsers));
+               if (ret)
+                       return ERR_PTR(ret);
+
+               module->config_ids = config_ids;
+       }
+
        module->owner = owner;
        INIT_LIST_HEAD(&module->node);
 
@@ -1416,6 +1464,82 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
        return template;
 }
 
+static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
+       {
+               .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_init_config, id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_INIT_CONFIG_PARAM_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_init_config, param),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_INIT_CONFIG_LENGTH_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_init_config, length),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
+                                          struct snd_soc_tplg_vendor_array *tuples,
+                                          u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+       int ret, i;
+
+       /* Parse tuple section telling how many init configs there are. */
+       ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs,
+                                     &tplg->num_init_configs,
+                                     sizeof(*tplg->init_configs),
+                                     AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32);
+       if (ret)
+               return ret;
+
+       block_size -= le32_to_cpu(tuples->size);
+       /* With header parsed, move on to parsing entries. */
+       tuples = avs_tplg_vendor_array_next(tuples);
+
+       for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) {
+               struct avs_tplg_init_config *config = &tplg->init_configs[i];
+               struct snd_soc_tplg_vendor_array *tmp;
+               void *init_config_data;
+               u32 esize;
+
+               /*
+                * Usually to get section length we search for first token of next group of data,
+                * but in this case we can't as tuples are followed by raw data.
+                */
+               tmp = avs_tplg_vendor_array_next(tuples);
+               esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size);
+
+               ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+                                              AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+                                              mod_init_config_parsers,
+                                              ARRAY_SIZE(mod_init_config_parsers));
+
+               block_size -= esize;
+
+               /* handle raw data section */
+               init_config_data = (void *)tuples + esize;
+               esize = config->length;
+
+               config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL);
+               if (!config->data)
+                       return -ENOMEM;
+
+               tuples = init_config_data + esize;
+               block_size -= esize;
+       }
+
+       return 0;
+}
+
 static int avs_route_load(struct snd_soc_component *comp, int index,
                          struct snd_soc_dapm_route *route)
 {
@@ -1571,6 +1695,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
        struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
        struct avs_soc_component *acomp = to_avs_soc_component(comp);
        size_t remaining = le32_to_cpu(manifest->priv.size);
+       bool has_init_config = true;
        u32 offset;
        int ret;
 
@@ -1668,8 +1793,43 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
        remaining -= offset;
        tuples = avs_tplg_vendor_array_at(tuples, offset);
 
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "condpath lookup failed: %d\n", ret);
+               return ret;
+       }
+
        /* Bindings dictionary. */
-       return avs_tplg_parse_bindings(comp, tuples, remaining);
+       ret = avs_tplg_parse_bindings(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset);
+       if (ret == -ENOENT) {
+               dev_dbg(comp->dev, "init config lookup failed: %d\n", ret);
+               has_init_config = false;
+       } else if (ret) {
+               dev_err(comp->dev, "init config lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       if (!has_init_config)
+               return 0;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       /* Initial configs dictionary. */
+       ret = avs_tplg_parse_initial_configs(comp, tuples, remaining);
+       if (ret < 0)
+               return ret;
+
+       return 0;
 }
 
 #define AVS_CONTROL_OPS_VOLUME 257
index 6e1c8e9b24964117a43ffb45072777fd2fa8e3a5..6a59dd766603f4ac974763fc3e759f841f4f94b5 100644 (file)
@@ -33,6 +33,9 @@ struct avs_tplg {
        u32 num_pplcfgs;
        struct avs_tplg_binding *bindings;
        u32 num_bindings;
+       u32 num_condpath_tmpls;
+       struct avs_tplg_init_config *init_configs;
+       u32 num_init_configs;
 
        struct list_head path_tmpl_list;
 };
@@ -147,6 +150,14 @@ struct avs_tplg_path_template {
        struct list_head node;
 };
 
+struct avs_tplg_init_config {
+       u32 id;
+
+       u8 param;
+       size_t length;
+       void *data;
+};
+
 struct avs_tplg_path {
        u32 id;
 
@@ -183,6 +194,8 @@ struct avs_tplg_module {
        u8 domain;
        struct avs_tplg_modcfg_ext *cfg_ext;
        u32 ctl_id;
+       u32 num_config_ids;
+       u32 *config_ids;
 
        struct avs_tplg_pipeline *owner;
        /* Pipeline modules management. */
index 8fd5e7f8305405916b9fabdd696fb25fe265a3f9..18ac3ce0752ec22f293006754f3361a1c3ae1322 100644 (file)
@@ -677,6 +677,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
        depends on MFD_INTEL_LPSS || COMPILE_TEST
        depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST
        depends on SOUNDWIRE
+       select SND_SOC_INTEL_SOF_BOARD_HELPERS
        select SND_SOC_MAX98363
        select SND_SOC_MAX98373_I2C
        select SND_SOC_MAX98373_SDW
index 4f2cb8e529713a8b045446341c0dbc2eec5a3ea1..088894ff416578105975454e5a30acc766f38954 100644 (file)
@@ -73,6 +73,16 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd)
 /*
  * DAI Link Helpers
  */
+
+/* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */
+#define DEFAULT_LINK_ORDER     SOF_LINK_ORDER(SOF_LINK_CODEC, \
+                                       SOF_LINK_DMIC01,       \
+                                       SOF_LINK_DMIC16K,      \
+                                       SOF_LINK_IDISP_HDMI,   \
+                                       SOF_LINK_AMP,          \
+                                       SOF_LINK_BT_OFFLOAD,   \
+                                       SOF_LINK_HDMI_IN)
+
 static struct snd_soc_dai_link_component dmic_component[] = {
        {
                .name = "dmic-codec",
@@ -416,6 +426,7 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
        int idx = 0;
        int ret;
        int ssp_hdmi_in = 0;
+       unsigned long link_order, link;
 
        num_links = calculate_num_links(ctx);
 
@@ -424,94 +435,140 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
        if (!links)
                return -ENOMEM;
 
-       /* headphone codec */
-       if (ctx->codec_type != CODEC_NONE) {
-               ret = sof_intel_board_set_codec_link(dev, &links[idx], idx,
-                                                    ctx->codec_type,
-                                                    ctx->ssp_codec);
-               if (ret) {
-                       dev_err(dev, "fail to set codec link, ret %d\n", ret);
-                       return ret;
-               }
-
-               ctx->codec_link = &links[idx];
-               idx++;
-       }
-
-       /* dmic01 and dmic16k */
-       if (ctx->dmic_be_num > 0) {
-               /* at least we have dmic01 */
-               ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx,
-                                                   SOF_DMIC_01);
-               if (ret) {
-                       dev_err(dev, "fail to set dmic01 link, ret %d\n", ret);
-                       return ret;
-               }
-
-               idx++;
-       }
-
-       if (ctx->dmic_be_num > 1) {
-               /* set up 2 BE links at most */
-               ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx,
-                                                   SOF_DMIC_16K);
-               if (ret) {
-                       dev_err(dev, "fail to set dmic16k link, ret %d\n", ret);
-                       return ret;
-               }
-
-               idx++;
-       }
-
-       /* idisp HDMI */
-       for (i = 1; i <= ctx->hdmi_num; i++) {
-               ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx,
-                                                         i,
-                                                         ctx->hdmi.idisp_codec);
-               if (ret) {
-                       dev_err(dev, "fail to set hdmi link, ret %d\n", ret);
-                       return ret;
+       if (ctx->link_order_overwrite)
+               link_order = ctx->link_order_overwrite;
+       else
+               link_order = DEFAULT_LINK_ORDER;
+
+       dev_dbg(dev, "create dai links, link_order 0x%lx\n", link_order);
+
+       while (link_order) {
+               link = link_order & SOF_LINK_ORDER_MASK;
+               link_order >>= SOF_LINK_ORDER_SHIFT;
+
+               switch (link) {
+               case SOF_LINK_CODEC:
+                       /* headphone codec */
+                       if (ctx->codec_type == CODEC_NONE)
+                               continue;
+
+                       ret = sof_intel_board_set_codec_link(dev, &links[idx],
+                                                            idx,
+                                                            ctx->codec_type,
+                                                            ctx->ssp_codec);
+                       if (ret) {
+                               dev_err(dev, "fail to set codec link, ret %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       ctx->codec_link = &links[idx];
+                       idx++;
+                       break;
+               case SOF_LINK_DMIC01:
+                       /* dmic01 */
+                       if (ctx->dmic_be_num == 0)
+                               continue;
+
+                       /* at least we have dmic01 */
+                       ret = sof_intel_board_set_dmic_link(dev, &links[idx],
+                                                           idx, SOF_DMIC_01);
+                       if (ret) {
+                               dev_err(dev, "fail to set dmic01 link, ret %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       idx++;
+                       break;
+               case SOF_LINK_DMIC16K:
+                       /* dmic16k */
+                       if (ctx->dmic_be_num <= 1)
+                               continue;
+
+                       /* set up 2 BE links at most */
+                       ret = sof_intel_board_set_dmic_link(dev, &links[idx],
+                                                           idx, SOF_DMIC_16K);
+                       if (ret) {
+                               dev_err(dev, "fail to set dmic16k link, ret %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       idx++;
+                       break;
+               case SOF_LINK_IDISP_HDMI:
+                       /* idisp HDMI */
+                       for (i = 1; i <= ctx->hdmi_num; i++) {
+                               ret = sof_intel_board_set_intel_hdmi_link(dev,
+                                                                         &links[idx],
+                                                                         idx, i,
+                                                                         ctx->hdmi.idisp_codec);
+                               if (ret) {
+                                       dev_err(dev, "fail to set hdmi link, ret %d\n",
+                                               ret);
+                                       return ret;
+                               }
+
+                               idx++;
+                       }
+                       break;
+               case SOF_LINK_AMP:
+                       /* speaker amp */
+                       if (ctx->amp_type == CODEC_NONE)
+                               continue;
+
+                       ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx],
+                                                              idx,
+                                                              ctx->amp_type,
+                                                              ctx->ssp_amp);
+                       if (ret) {
+                               dev_err(dev, "fail to set amp link, ret %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       ctx->amp_link = &links[idx];
+                       idx++;
+                       break;
+               case SOF_LINK_BT_OFFLOAD:
+                       /* BT audio offload */
+                       if (!ctx->bt_offload_present)
+                               continue;
+
+                       ret = sof_intel_board_set_bt_link(dev, &links[idx], idx,
+                                                         ctx->ssp_bt);
+                       if (ret) {
+                               dev_err(dev, "fail to set bt link, ret %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       idx++;
+                       break;
+               case SOF_LINK_HDMI_IN:
+                       /* HDMI-In */
+                       for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
+                               ret = sof_intel_board_set_hdmi_in_link(dev,
+                                                                      &links[idx],
+                                                                      idx,
+                                                                      ssp_hdmi_in);
+                               if (ret) {
+                                       dev_err(dev, "fail to set hdmi-in link, ret %d\n",
+                                               ret);
+                                       return ret;
+                               }
+
+                               idx++;
+                       }
+                       break;
+               case SOF_LINK_NONE:
+                       /* caught here if it's not used as terminator in macro */
+                       fallthrough;
+               default:
+                       dev_err(dev, "invalid link type %ld\n", link);
+                       return -EINVAL;
                }
-
-               idx++;
-       }
-
-       /* speaker amp */
-       if (ctx->amp_type != CODEC_NONE) {
-               ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx,
-                                                      ctx->amp_type,
-                                                      ctx->ssp_amp);
-               if (ret) {
-                       dev_err(dev, "fail to set amp link, ret %d\n", ret);
-                       return ret;
-               }
-
-               ctx->amp_link = &links[idx];
-               idx++;
-       }
-
-       /* BT audio offload */
-       if (ctx->bt_offload_present) {
-               ret = sof_intel_board_set_bt_link(dev, &links[idx], idx,
-                                                 ctx->ssp_bt);
-               if (ret) {
-                       dev_err(dev, "fail to set bt link, ret %d\n", ret);
-                       return ret;
-               }
-
-               idx++;
-       }
-
-       /* HDMI-In */
-       for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
-               ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx,
-                                                      ssp_hdmi_in);
-               if (ret) {
-                       dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret);
-                       return ret;
-               }
-
-               idx++;
        }
 
        if (idx != num_links) {
@@ -527,6 +584,24 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
 
+struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd,
+                                         const char * const dai_name[], int num_dais)
+{
+       struct snd_soc_dai *dai;
+       int index;
+       int i;
+
+       for (index = 0; index < num_dais; index++)
+               for_each_rtd_codec_dais(rtd, i, dai)
+                       if (strstr(dai->name, dai_name[index])) {
+                               dev_dbg(rtd->card->dev, "get dai %s\n", dai->name);
+                               return dai;
+                       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_NS(get_codec_dai_by_name, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
 MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
 MODULE_LICENSE("GPL");
index 3b36058118ca595a53aa13165f455867e92271b1..f42d5d6403219e695d5da233d34aeb527dd3efcc 100644 (file)
 #include "sof_hdmi_common.h"
 #include "sof_ssp_common.h"
 
+enum {
+       SOF_LINK_NONE = 0,
+       SOF_LINK_CODEC,
+       SOF_LINK_DMIC01,
+       SOF_LINK_DMIC16K,
+       SOF_LINK_IDISP_HDMI,
+       SOF_LINK_AMP,
+       SOF_LINK_BT_OFFLOAD,
+       SOF_LINK_HDMI_IN,
+};
+
+#define SOF_LINK_ORDER_MASK    (0xF)
+#define SOF_LINK_ORDER_SHIFT   (4)
+
+#define SOF_LINK_ORDER(k1, k2, k3, k4, k5, k6, k7) \
+       ((((k1) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 0)) | \
+        (((k2) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 1)) | \
+        (((k3) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 2)) | \
+        (((k4) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 3)) | \
+        (((k5) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 4)) | \
+        (((k6) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 5)) | \
+        (((k7) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 6)))
+
 /*
  * sof_rt5682_private: private data for rt5682 machine driver
  *
@@ -37,6 +60,7 @@ struct sof_rt5682_private {
  * @bt_offload_present: true to create BT offload BE link
  * @codec_link: pointer to headset codec dai link
  * @amp_link: pointer to speaker amplifier dai link
+ * @link_order_overwrite: custom DAI link order
  * @rt5682: private data for rt5682 machine driver
  */
 struct sof_card_private {
@@ -59,6 +83,8 @@ struct sof_card_private {
        struct snd_soc_dai_link *codec_link;
        struct snd_soc_dai_link *amp_link;
 
+       unsigned long link_order_overwrite;
+
        union {
                struct sof_rt5682_private rt5682;
        };
@@ -92,4 +118,7 @@ int sof_intel_board_set_hdmi_in_link(struct device *dev,
                                     struct snd_soc_dai_link *link, int be_id,
                                     int ssp_hdmi);
 
+struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd,
+                                         const char * const dai_name[], int num_dais);
+
 #endif /* __SOF_INTEL_BOARD_HELPERS_H */
index c2442bf8ced060a7f0e96c8ff1a3306af5bd7c87..323b86c42ef954ea397fd630867208ba5208cf3c 100644 (file)
 #define SOF_CS42L42_NUM_HDMIDEV_MASK           (GENMASK(9, 7))
 #define SOF_CS42L42_NUM_HDMIDEV(quirk) \
        (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK)
-#define SOF_CS42L42_DAILINK_SHIFT              10
-#define SOF_CS42L42_DAILINK_MASK               (GENMASK(24, 10))
-#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \
-       ((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK)
 #define SOF_BT_OFFLOAD_PRESENT                 BIT(25)
 #define SOF_CS42L42_SSP_BT_SHIFT               26
 #define SOF_CS42L42_SSP_BT_MASK                        (GENMASK(28, 26))
 #define SOF_CS42L42_SSP_BT(quirk)      \
        (((quirk) << SOF_CS42L42_SSP_BT_SHIFT) & SOF_CS42L42_SSP_BT_MASK)
 
-enum {
-       LINK_NONE = 0,
-       LINK_HP = 1,
-       LINK_SPK = 2,
-       LINK_DMIC = 3,
-       LINK_HDMI = 4,
-       LINK_BT = 5,
-};
-
 static struct snd_soc_jack_pin jack_pins[] = {
        {
                .pin    = "Headphone Jack",
@@ -182,156 +169,63 @@ static struct snd_soc_dai_link_component cs42l42_component[] = {
        }
 };
 
-static struct snd_soc_dai_link *
-sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
-                         int ssp_codec, int ssp_amp, int ssp_bt,
-                         int dmic_be_num, int hdmi_num, bool idisp_codec)
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+                         struct sof_card_private *ctx)
 {
-       struct snd_soc_dai_link *links;
        int ret;
-       int id = 0;
-       int link_seq;
-       int i;
-
-       links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
-                           sizeof(struct snd_soc_dai_link), GFP_KERNEL);
-       if (!links)
-               goto devm_err;
-
-       link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;
-
-       while (link_seq) {
-               int link_type = link_seq & 0x07;
-
-               switch (link_type) {
-               case LINK_HP:
-                       ret = sof_intel_board_set_codec_link(dev, &links[id], id,
-                                                            CODEC_CS42L42,
-                                                            ssp_codec);
-                       if (ret) {
-                               dev_err(dev, "fail to create hp codec dai links, ret %d\n",
-                                       ret);
-                               goto devm_err;
-                       }
-
-                       /* codec-specific fields */
-                       links[id].codecs = cs42l42_component;
-                       links[id].num_codecs = ARRAY_SIZE(cs42l42_component);
-                       links[id].init = sof_cs42l42_init;
-                       links[id].exit = sof_cs42l42_exit;
-                       links[id].ops = &sof_cs42l42_ops;
-
-                       id++;
-                       break;
-               case LINK_SPK:
-                       if (amp_type != CODEC_NONE) {
-                               ret = sof_intel_board_set_ssp_amp_link(dev,
-                                                                      &links[id],
-                                                                      id,
-                                                                      amp_type,
-                                                                      ssp_amp);
-                               if (ret) {
-                                       dev_err(dev, "fail to create spk amp dai links, ret %d\n",
-                                               ret);
-                                       goto devm_err;
-                               }
-
-                               /* codec-specific fields */
-                               switch (amp_type) {
-                               case CODEC_MAX98357A:
-                                       max_98357a_dai_link(&links[id]);
-                                       break;
-                               case CODEC_MAX98360A:
-                                       max_98360a_dai_link(&links[id]);
-                                       break;
-                               default:
-                                       dev_err(dev, "invalid amp type %d\n",
-                                               amp_type);
-                                       goto devm_err;
-                               }
-
-                               id++;
-                       }
-                       break;
-               case LINK_DMIC:
-                       if (dmic_be_num > 0) {
-                               /* at least we have dmic01 */
-                               ret = sof_intel_board_set_dmic_link(dev,
-                                                                   &links[id],
-                                                                   id,
-                                                                   SOF_DMIC_01);
-                               if (ret) {
-                                       dev_err(dev, "fail to create dmic01 link, ret %d\n",
-                                               ret);
-                                       goto devm_err;
-                               }
-
-                               id++;
-                       }
-
-                       if (dmic_be_num > 1) {
-                               /* set up 2 BE links at most */
-                               ret = sof_intel_board_set_dmic_link(dev,
-                                                                   &links[id],
-                                                                   id,
-                                                                   SOF_DMIC_16K);
-                               if (ret) {
-                                       dev_err(dev, "fail to create dmic16k link, ret %d\n",
-                                               ret);
-                                       goto devm_err;
-                               }
-
-                               id++;
-                       }
-                       break;
-               case LINK_HDMI:
-                       for (i = 1; i <= hdmi_num; i++) {
-                               ret = sof_intel_board_set_intel_hdmi_link(dev,
-                                                                         &links[id],
-                                                                         id, i,
-                                                                         idisp_codec);
-                               if (ret) {
-                                       dev_err(dev, "fail to create hdmi link, ret %d\n",
-                                               ret);
-                                       goto devm_err;
-                               }
-
-                               id++;
-                       }
-                       break;
-               case LINK_BT:
-                       if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
-                               ret = sof_intel_board_set_bt_link(dev,
-                                                                 &links[id], id,
-                                                                 ssp_bt);
-                               if (ret) {
-                                       dev_err(dev, "fail to create bt offload dai links, ret %d\n",
-                                               ret);
-                                       goto devm_err;
-                               }
-
-                               id++;
-                       }
-                       break;
-               case LINK_NONE:
-                       /* caught here if it's not used as terminator in macro */
-               default:
-                       dev_err(dev, "invalid link type %d\n", link_type);
-                       goto devm_err;
-               }
-
-               link_seq >>= 3;
+
+       ret = sof_intel_board_set_dai_link(dev, card, ctx);
+       if (ret)
+               return ret;
+
+       if (!ctx->codec_link) {
+               dev_err(dev, "codec link not available");
+               return -EINVAL;
+       }
+
+       /* codec-specific fields for headphone codec */
+       ctx->codec_link->codecs = cs42l42_component;
+       ctx->codec_link->num_codecs = ARRAY_SIZE(cs42l42_component);
+       ctx->codec_link->init = sof_cs42l42_init;
+       ctx->codec_link->exit = sof_cs42l42_exit;
+       ctx->codec_link->ops = &sof_cs42l42_ops;
+
+       if (ctx->amp_type == CODEC_NONE)
+               return 0;
+
+       if (!ctx->amp_link) {
+               dev_err(dev, "amp link not available");
+               return -EINVAL;
        }
 
-       return links;
-devm_err:
-       return NULL;
+       /* codec-specific fields for speaker amplifier */
+       switch (ctx->amp_type) {
+       case CODEC_MAX98357A:
+               max_98357a_dai_link(ctx->amp_link);
+               break;
+       case CODEC_MAX98360A:
+               max_98360a_dai_link(ctx->amp_link);
+               break;
+       default:
+               dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
+#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP,         \
+                                       SOF_LINK_CODEC,      \
+                                       SOF_LINK_DMIC01,     \
+                                       SOF_LINK_IDISP_HDMI, \
+                                       SOF_LINK_NONE,       \
+                                       SOF_LINK_NONE,       \
+                                       SOF_LINK_NONE)
+
 static int sof_audio_probe(struct platform_device *pdev)
 {
        struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
-       struct snd_soc_dai_link *dai_links;
        struct sof_card_private *ctx;
        int ret;
 
@@ -348,6 +242,9 @@ static int sof_audio_probe(struct platform_device *pdev)
        if (soc_intel_is_glk()) {
                ctx->dmic_be_num = 1;
                ctx->hdmi_num = 3;
+
+               /* overwrite the DAI link order for GLK boards */
+               ctx->link_order_overwrite = GLK_LINK_ORDER;
        } else {
                ctx->dmic_be_num = 2;
                ctx->hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >>
@@ -371,25 +268,13 @@ static int sof_audio_probe(struct platform_device *pdev)
 
        ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK;
 
-       /* compute number of dai links */
-       sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
-
-       if (ctx->amp_type != CODEC_NONE)
-               sof_audio_card_cs42l42.num_links++;
-       if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
+       if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT)
                ctx->bt_offload_present = true;
-               sof_audio_card_cs42l42.num_links++;
-       }
-
-       dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
-                                             ctx->ssp_codec, ctx->ssp_amp,
-                                             ctx->ssp_bt, ctx->dmic_be_num,
-                                             ctx->hdmi_num,
-                                             ctx->hdmi.idisp_codec);
-       if (!dai_links)
-               return -ENOMEM;
 
-       sof_audio_card_cs42l42.dai_link = dai_links;
+       /* update dai_link */
+       ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_cs42l42, ctx);
+       if (ret)
+               return ret;
 
        sof_audio_card_cs42l42.dev = &pdev->dev;
 
@@ -409,14 +294,12 @@ static const struct platform_device_id board_ids[] = {
        {
                .name = "glk_cs4242_mx98357a",
                .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
-                                       SOF_CS42L42_SSP_AMP(1)) |
-                                       SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE),
+                                       SOF_CS42L42_SSP_AMP(1)),
        },
        {
                .name = "jsl_cs4242_mx98360a",
                .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
-                                       SOF_CS42L42_SSP_AMP(1)) |
-                                       SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE),
+                                       SOF_CS42L42_SSP_AMP(1)),
        },
        {
                .name = "adl_mx98360a_cs4242",
@@ -424,8 +307,7 @@ static const struct platform_device_id board_ids[] = {
                                SOF_CS42L42_SSP_AMP(1) |
                                SOF_CS42L42_NUM_HDMIDEV(4) |
                                SOF_BT_OFFLOAD_PRESENT |
-                               SOF_CS42L42_SSP_BT(2) |
-                               SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)),
+                               SOF_CS42L42_SSP_BT(2)),
        },
        { }
 };
index cd50f26d1edbedbc10074c472a3479114a823656..640d17c6cd35648d61d9965103c1857b0bb8399a 100644 (file)
@@ -154,46 +154,6 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = {
                                        SOF_RT5682_SSP_AMP(2) |
                                        SOF_RT5682_NUM_HDMIDEV(4)),
        },
-       {
-               .callback = sof_rt5682_quirk_cb,
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
-                       DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S"),
-               },
-               .driver_data = (void *)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(2) |
-                                       SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(3) |
-                                       SOF_BT_OFFLOAD_SSP(1) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT
-                                       ),
-       },
-       {
-               .callback = sof_rt5682_quirk_cb,
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
-                       DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_DISCRETE_I2S_BT"),
-               },
-               .driver_data = (void *)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(2) |
-                                       SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(3) |
-                                       SOF_BT_OFFLOAD_SSP(1) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT
-                                       ),
-       },
-       {
-               .callback = sof_rt5682_quirk_cb,
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
-                       DMI_MATCH(DMI_OEM_STRING, "AUDIO-ALC1019_ALC5682I_I2S"),
-               },
-               .driver_data = (void *)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(2) |
-                                       SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(3)
-                                       ),
-       },
        {
                .callback = sof_rt5682_quirk_cb,
                .matches = {
@@ -810,54 +770,13 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_RT5682_SSP_AMP(1)),
        },
        {
-               .name = "jsl_rt5682_rt1015",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1)),
-       },
-       {
-               .name = "jsl_rt5682_mx98360",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1)),
-       },
-       {
-               .name = "jsl_rt5682_rt1015p",
+               .name = "jsl_rt5682_def",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
                                        SOF_RT5682_SSP_CODEC(0) |
                                        SOF_RT5682_SSP_AMP(1)),
        },
        {
-               .name = "jsl_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0)),
-       },
-       {
-               .name = "jsl_rt5650",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1)),
-       },
-       {
-               .name = "tgl_mx98357_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "tgl_rt1011_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "tgl_mx98373_rt5682",
+               .name = "tgl_rt5682_def",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
                                        SOF_RT5682_SSP_CODEC(0) |
                                        SOF_RT5682_SSP_AMP(1) |
@@ -866,7 +785,7 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_SSP_BT_OFFLOAD_PRESENT),
        },
        {
-               .name = "adl_mx98373_rt5682",
+               .name = "adl_rt5682_def",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
                                        SOF_RT5682_SSP_CODEC(0) |
                                        SOF_RT5682_SSP_AMP(1) |
@@ -881,41 +800,6 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_RT5682_SSP_AMP(2) |
                                        SOF_RT5682_NUM_HDMIDEV(4)),
        },
-       {
-               .name = "adl_max98390_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "adl_mx98360_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "adl_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "adl_rt1019_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
        {
                .name = "adl_rt5682_c1_h02",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
@@ -924,15 +808,6 @@ static const struct platform_device_id board_ids[] = {
                                        /* SSP 0 and SSP 2 are used for HDMI IN */
                                        SOF_HDMI_CAPTURE_SSP_MASK(0x5)),
        },
-       {
-               .name = "adl_rt5650",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
        {
                .name = "rpl_mx98357_rt5682",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
@@ -941,16 +816,7 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_RT5682_NUM_HDMIDEV(4)),
        },
        {
-               .name = "rpl_mx98360_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(0) |
-                                       SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4) |
-                                       SOF_BT_OFFLOAD_SSP(2) |
-                                       SOF_SSP_BT_OFFLOAD_PRESENT),
-       },
-       {
-               .name = "rpl_rt1019_rt5682",
+               .name = "rpl_rt5682_def",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
                                        SOF_RT5682_SSP_CODEC(0) |
                                        SOF_RT5682_SSP_AMP(1) |
@@ -983,14 +849,7 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_RT5682_NUM_HDMIDEV(3)),
        },
        {
-               .name = "mtl_rt1019_rt5682",
-               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
-                                       SOF_RT5682_SSP_CODEC(2) |
-                                       SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(3)),
-       },
-       {
-               .name = "mtl_rt5650",
+               .name = "mtl_rt5682_def",
                .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
                                        SOF_RT5682_SSP_CODEC(2) |
                                        SOF_RT5682_SSP_AMP(0) |
index 300391fbc2fc2b29863d6ba19169d60e9a22502c..08f330ed5c2ea65b504600c5d12e329d0c64755a 100644 (file)
@@ -236,6 +236,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
                                        SOF_SDW_PCH_DMIC |
                                        RT711_JD2_100K),
        },
+       {
+               /* NUC15 LAPRC710 skews */
+               .callback = sof_sdw_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"),
+               },
+               .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+                                       SOF_SDW_PCH_DMIC |
+                                       RT711_JD2_100K),
+       },
        /* TigerLake-SDCA devices */
        {
                .callback = sof_sdw_quirk_cb,
@@ -650,7 +661,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt700-aif1",
                                .dai_type = SOF_SDW_DAI_TYPE_JACK,
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
-                               .init = sof_sdw_rt700_init,
+                               .rtd_init = rt700_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -666,6 +677,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
                                .init = sof_sdw_rt_sdca_jack_init,
                                .exit = sof_sdw_rt_sdca_jack_exit,
+                               .rtd_init = rt_sdca_jack_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -681,6 +693,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
                                .init = sof_sdw_rt711_init,
                                .exit = sof_sdw_rt711_exit,
+                               .rtd_init = rt711_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -696,13 +709,14 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
                                .init = sof_sdw_rt_sdca_jack_init,
                                .exit = sof_sdw_rt_sdca_jack_exit,
+                               .rtd_init = rt_sdca_jack_rtd_init,
                        },
                        {
                                .direction = {true, false},
                                .dai_name = "rt712-sdca-aif2",
                                .dai_type = SOF_SDW_DAI_TYPE_AMP,
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
-                               .init = sof_sdw_rt712_spk_init,
+                               .rtd_init = rt712_spk_rtd_init,
                        },
                },
                .dai_num = 2,
@@ -716,7 +730,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt712-sdca-dmic-aif1",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt712_sdca_dmic_init,
+                               .rtd_init = rt712_sdca_dmic_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -732,6 +746,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
                                .init = sof_sdw_rt_sdca_jack_init,
                                .exit = sof_sdw_rt_sdca_jack_exit,
+                               .rtd_init = rt_sdca_jack_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -745,7 +760,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt712-sdca-dmic-aif1",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt712_sdca_dmic_init,
+                               .rtd_init = rt712_sdca_dmic_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -761,6 +776,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
                                .init = sof_sdw_rt_amp_init,
                                .exit = sof_sdw_rt_amp_exit,
+                               .rtd_init = rt_amp_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -776,6 +792,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
                                .init = sof_sdw_rt_amp_init,
                                .exit = sof_sdw_rt_amp_exit,
+                               .rtd_init = rt_amp_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -790,6 +807,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
                                .init = sof_sdw_rt_amp_init,
                                .exit = sof_sdw_rt_amp_exit,
+                               .rtd_init = rt_amp_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -804,7 +822,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt715-aif2",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt715_sdca_init,
+                               .rtd_init = rt715_sdca_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -819,7 +837,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt715-aif2",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt715_sdca_init,
+                               .rtd_init = rt715_sdca_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -834,7 +852,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt715-aif2",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt715_init,
+                               .rtd_init = rt715_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -849,7 +867,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt715-aif2",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_rt715_init,
+                               .rtd_init = rt715_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -893,6 +911,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_type = SOF_SDW_DAI_TYPE_AMP,
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
                                .init = sof_sdw_maxim_init,
+                               .rtd_init = maxim_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -906,6 +925,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_type = SOF_SDW_DAI_TYPE_AMP,
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
                                .init = sof_sdw_maxim_init,
+                               .rtd_init = maxim_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -918,7 +938,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "rt5682-sdw",
                                .dai_type = SOF_SDW_DAI_TYPE_JACK,
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
-                               .init = sof_sdw_rt5682_init,
+                               .rtd_init = rt5682_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -932,6 +952,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_type = SOF_SDW_DAI_TYPE_AMP,
                                .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
                                .init = sof_sdw_cs_amp_init,
+                               .rtd_init = cs_spk_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -944,7 +965,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "cs42l42-sdw",
                                .dai_type = SOF_SDW_DAI_TYPE_JACK,
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
-                               .init = sof_sdw_cs42l42_init,
+                               .rtd_init = cs42l42_rtd_init,
                        },
                },
                .dai_num = 1,
@@ -958,14 +979,14 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                                .dai_name = "cs42l43-dp5",
                                .dai_type = SOF_SDW_DAI_TYPE_JACK,
                                .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
-                               .init = sof_sdw_cs42l43_hs_init,
+                               .rtd_init = cs42l43_hs_rtd_init,
                        },
                        {
                                .direction = {false, true},
                                .dai_name = "cs42l43-dp1",
                                .dai_type = SOF_SDW_DAI_TYPE_MIC,
                                .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
-                               .init = sof_sdw_cs42l43_dmic_init,
+                               .rtd_init = cs42l43_dmic_rtd_init,
                        },
                        {
                                .direction = {false, true},
@@ -1387,6 +1408,56 @@ static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps,
        }
 }
 
+static inline int find_codec_info_dai(const char *dai_name, int *dai_index)
+{
+       int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
+               for (j = 0; j < codec_info_list[i].dai_num; j++) {
+                       if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
+                               *dai_index = j;
+                               return i;
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int sof_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct sof_sdw_codec_info *codec_info;
+       struct snd_soc_dai *dai;
+       int codec_index;
+       int dai_index;
+       int ret;
+       int i;
+
+       for_each_rtd_codec_dais(rtd, i, dai) {
+               codec_index = find_codec_info_dai(dai->name, &dai_index);
+               if (codec_index < 0)
+                       return -EINVAL;
+
+               codec_info = &codec_info_list[codec_index];
+               /*
+                * A codec dai can be connected to different dai links for capture and playback,
+                * but we only need to call the rtd_init function once.
+                * The rtd_init for each codec dai is independent. So, the order of rtd_init
+                * doesn't matter.
+                */
+               if (codec_info->dais[dai_index].rtd_init_done)
+                       continue;
+               if (codec_info->dais[dai_index].rtd_init) {
+                       ret = codec_info->dais[dai_index].rtd_init(rtd);
+                       if (ret)
+                               return ret;
+               }
+               codec_info->dais[dai_index].rtd_init_done = true;
+       }
+
+       return 0;
+}
+
 static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
 
 static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
@@ -1547,7 +1618,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
 
                init_dai_link(dev, dai_links + *link_index, be_id, name,
                              playback, capture, cpus, cpu_dai_num, codecs, codec_num,
-                             NULL, &sdw_ops);
+                             sof_sdw_rtd_init, &sdw_ops);
 
                /*
                 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
@@ -1696,15 +1767,21 @@ out:
                                return codec_index;
 
                        for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) {
+                               int current_be_id;
+
                                ret = create_sdw_dailink(card, &link_index, dai_links,
                                                         sdw_be_num, adr_link,
                                                         codec_conf, codec_conf_num,
-                                                        &be_id, &codec_conf_index,
+                                                        &current_be_id, &codec_conf_index,
                                                         &ignore_pch_dmic, append_dai_type, i, j);
                                if (ret < 0) {
                                        dev_err(dev, "failed to create dai link %d\n", link_index);
                                        return ret;
                                }
+
+                               /* Update the be_id to match the highest ID used for SDW link */
+                               if (be_id < current_be_id)
+                                       be_id = current_be_id;
                        }
 
                        if (aggregation && endpoint->aggregated)
@@ -1880,6 +1957,7 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card)
 
        for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
                for (j = 0; j < codec_info_list[i].dai_num; j++) {
+                       codec_info_list[i].dais[j].rtd_init_done = false;
                        /* Check each dai in codec_info_lis to see if it is used in the link */
                        if (!codec_info_list[i].dais[j].exit)
                                continue;
index f16456945edb08e7096a3fd298152a0133d90ad7..b1d57034361c443aba0d8a77c6a9d459a5e99976 100644 (file)
@@ -78,6 +78,8 @@ struct sof_sdw_dai_info {
                     struct sof_sdw_codec_info *info,
                     bool playback);
        int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+       int (*rtd_init)(struct snd_soc_pcm_runtime *rtd);
+       bool rtd_init_done; /* Indicate that the rtd_init callback is done */
 };
 
 struct sof_sdw_codec_info {
@@ -137,25 +139,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
                              bool playback);
 int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
 
-/* RT712-SDCA support */
-int sof_sdw_rt712_spk_init(struct snd_soc_card *card,
-                          const struct snd_soc_acpi_link_adr *link,
-                          struct snd_soc_dai_link *dai_links,
-                          struct sof_sdw_codec_info *info,
-                          bool playback);
-int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card,
-                                const struct snd_soc_acpi_link_adr *link,
-                                struct snd_soc_dai_link *dai_links,
-                                struct sof_sdw_codec_info *info,
-                                bool playback);
-
-/* RT700 support */
-int sof_sdw_rt700_init(struct snd_soc_card *card,
-                      const struct snd_soc_acpi_link_adr *link,
-                      struct snd_soc_dai_link *dai_links,
-                      struct sof_sdw_codec_info *info,
-                      bool playback);
-
 /* RT1308 I2S support */
 extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops;
 
@@ -167,22 +150,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card,
                        bool playback);
 int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
 
-/* RT1316 support */
-
-/* RT715 support */
-int sof_sdw_rt715_init(struct snd_soc_card *card,
-                      const struct snd_soc_acpi_link_adr *link,
-                      struct snd_soc_dai_link *dai_links,
-                      struct sof_sdw_codec_info *info,
-                      bool playback);
-
-/* RT715-SDCA support */
-int sof_sdw_rt715_sdca_init(struct snd_soc_card *card,
-                           const struct snd_soc_acpi_link_adr *link,
-                           struct snd_soc_dai_link *dai_links,
-                           struct sof_sdw_codec_info *info,
-                           bool playback);
-
 /* RT722-SDCA support */
 int sof_sdw_rt722_spk_init(struct snd_soc_card *card,
                           const struct snd_soc_acpi_link_adr *link,
@@ -202,37 +169,28 @@ int sof_sdw_maxim_init(struct snd_soc_card *card,
                       struct sof_sdw_codec_info *info,
                       bool playback);
 
-/* RT5682 support */
-int sof_sdw_rt5682_init(struct snd_soc_card *card,
-                       const struct snd_soc_acpi_link_adr *link,
-                       struct snd_soc_dai_link *dai_links,
-                       struct sof_sdw_codec_info *info,
-                       bool playback);
-
-/* CS42L42 support */
-int sof_sdw_cs42l42_init(struct snd_soc_card *card,
-                        const struct snd_soc_acpi_link_adr *link,
-                        struct snd_soc_dai_link *dai_links,
-                        struct sof_sdw_codec_info *info,
-                        bool playback);
-
-/* CS42L43 support */
-int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card,
-                           const struct snd_soc_acpi_link_adr *link,
-                           struct snd_soc_dai_link *dai_links,
-                           struct sof_sdw_codec_info *info,
-                           bool playback);
-
-int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card,
-                             const struct snd_soc_acpi_link_adr *link,
-                             struct snd_soc_dai_link *dai_links,
-                             struct sof_sdw_codec_info *info,
-                             bool playback);
-
 /* CS AMP support */
 int sof_sdw_cs_amp_init(struct snd_soc_card *card,
                        const struct snd_soc_acpi_link_adr *link,
                        struct snd_soc_dai_link *dai_links,
                        struct sof_sdw_codec_info *info,
                        bool playback);
+
+/* dai_link init callbacks */
+
+int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd);
+int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd);
+
 #endif
index 436f41086da698bbf802a9901b163a266864e8b7..0dc297f7de0112f0ef0e659a62dcc429d2f5ce55 100644 (file)
@@ -15,6 +15,7 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
 #include <sound/jack.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 static const struct snd_soc_dapm_widget cs42l42_widgets[] = {
@@ -46,15 +47,24 @@ static struct snd_soc_jack_pin cs42l42_jack_pins[] = {
        },
 };
 
-static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static const char * const jack_codecs[] = {
+       "cs42l42"
+};
+
+int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
        struct snd_soc_jack *jack;
        int ret;
 
+       codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs));
+       if (!codec_dai)
+               return -EINVAL;
+
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s hs:cs42l42",
                                          card->components);
@@ -111,21 +121,4 @@ static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd)
 
        return ret;
 }
-
-int sof_sdw_cs42l42_init(struct snd_soc_card *card,
-                        const struct snd_soc_acpi_link_adr *link,
-                        struct snd_soc_dai_link *dai_links,
-                        struct sof_sdw_codec_info *info,
-                        bool playback)
-{
-       /*
-        * headset should be initialized once.
-        * Do it with dai link for playback.
-        */
-       if (!playback)
-               return 0;
-
-       dai_links->init = cs42l42_rtd_init;
-
-       return 0;
-}
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index 360f11b72aa2eea07eb7af08e08dcad7c9f2ddec..a9b6edac2ecd537bbebff2e166a46df25058ebd4 100644 (file)
@@ -50,7 +50,7 @@ static struct snd_soc_jack_pin sof_jack_pins[] = {
        },
 };
 
-static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd)
+int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
        struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);
@@ -108,20 +108,7 @@ static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd)
        return ret;
 }
 
-int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link,
-                           struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info,
-                           bool playback)
-{
-       /*
-        * No need to test if (!playback) like other codecs as cs42l43 uses separated dai for
-        * playback and capture, and sof_sdw_cs42l43_init is only linked to the playback dai.
-        */
-       dai_links->init = cs42l43_hs_rtd_init;
-
-       return 0;
-}
-
-static int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
+int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        int ret;
@@ -146,11 +133,3 @@ static int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
        return ret;
 }
 
-int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link,
-                             struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info,
-                             bool playback)
-{
-       dai_links->init = cs42l43_dmic_rtd_init;
-
-       return 0;
-}
index f88c01552a924cd159d8cf0c07fb9638b97a8093..56cf75bc6cc4c50379d8e51bd15efe1d0b29827a 100644 (file)
@@ -18,7 +18,7 @@ static const struct snd_soc_dapm_widget sof_widgets[] = {
        SND_SOC_DAPM_SPK("Speakers", NULL),
 };
 
-static int cs_spk_init(struct snd_soc_pcm_runtime *rtd)
+int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        const char *dai_name = rtd->dai_link->codecs->dai_name;
        struct snd_soc_card *card = rtd->card;
@@ -67,7 +67,6 @@ int sof_sdw_cs_amp_init(struct snd_soc_card *card,
                return 0;
 
        info->amp_num++;
-       dai_links->init = cs_spk_init;
 
        return 0;
 }
index e36b8d8c70c9cca648d4b9ee939672d5fdab8f38..0347304326713aeda3fc05b1d24c050c38595ac4 100644 (file)
@@ -27,7 +27,7 @@ static const struct snd_kcontrol_new maxim_controls[] = {
        SOC_DAPM_PIN_SWITCH("Right Spk"),
 };
 
-static int spk_init(struct snd_soc_pcm_runtime *rtd)
+int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        int ret;
@@ -145,8 +145,6 @@ int sof_sdw_maxim_init(struct snd_soc_card *card,
                       bool playback)
 {
        info->amp_num++;
-       if (info->amp_num == 2)
-               dai_links->init = spk_init;
 
        maxim_part_id = info->part_id;
        switch (maxim_part_id) {
index 7b7c9def398be298dbba2d99392edbe52e05a1e8..6b008a5a343bcd9288683f287a048e7a07ca53cf 100644 (file)
@@ -15,6 +15,7 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
 #include <sound/jack.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 static const struct snd_soc_dapm_widget rt5682_widgets[] = {
@@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt5682_jack_pins[] = {
        },
 };
 
-static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static const char * const jack_codecs[] = {
+       "rt5682"
+};
+
+int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
        struct snd_soc_jack *jack;
        int ret;
 
+       codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs));
+       if (!codec_dai)
+               return -EINVAL;
+
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s hs:rt5682",
                                          card->components);
@@ -110,21 +120,4 @@ static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd)
 
        return ret;
 }
-
-int sof_sdw_rt5682_init(struct snd_soc_card *card,
-                       const struct snd_soc_acpi_link_adr *link,
-                       struct snd_soc_dai_link *dai_links,
-                       struct sof_sdw_codec_info *info,
-                       bool playback)
-{
-       /*
-        * headset should be initialized once.
-        * Do it with dai link for playback.
-        */
-       if (!playback)
-               return 0;
-
-       dai_links->init = rt5682_rtd_init;
-
-       return 0;
-}
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index a1714afe85155331e77ef721b759ed5029d9ea73..88e785a54b16fb73376294d070294a7c41a03e4c 100644 (file)
@@ -13,6 +13,7 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
 #include <sound/jack.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 static const struct snd_soc_dapm_widget rt700_widgets[] = {
@@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt700_jack_pins[] = {
        },
 };
 
-static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static const char * const jack_codecs[] = {
+       "rt700"
+};
+
+int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
        struct snd_soc_jack *jack;
        int ret;
 
+       codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs));
+       if (!codec_dai)
+               return -EINVAL;
+
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s hs:rt700",
                                          card->components);
@@ -109,21 +119,4 @@ static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd)
 
        return ret;
 }
-
-int sof_sdw_rt700_init(struct snd_soc_card *card,
-                      const struct snd_soc_acpi_link_adr *link,
-                      struct snd_soc_dai_link *dai_links,
-                      struct sof_sdw_codec_info *info,
-                      bool playback)
-{
-       /*
-        * headset should be initialized once.
-        * Do it with dai link for playback.
-        */
-       if (!playback)
-               return 0;
-
-       dai_links->init = rt700_rtd_init;
-
-       return 0;
-}
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index 38782fdfcf1542d08a260dafbd2f1e70c7809b66..cdd1587b246c87aeb91585b7112f08518a61e99c 100644 (file)
@@ -15,6 +15,7 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
 #include <sound/jack.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 /*
@@ -69,15 +70,24 @@ static struct snd_soc_jack_pin rt711_jack_pins[] = {
        },
 };
 
-static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static const char * const jack_codecs[] = {
+       "rt711"
+};
+
+int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
        struct snd_soc_jack *jack;
        int ret;
 
+       codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs));
+       if (!codec_dai)
+               return -EINVAL;
+
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s hs:rt711",
                                          card->components);
@@ -176,7 +186,6 @@ int sof_sdw_rt711_init(struct snd_soc_card *card,
        }
        ctx->headset_codec_dev = sdw_dev;
 
-       dai_links->init = rt711_rtd_init;
-
        return 0;
 }
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index 3092029419dfbed45989b8859d5203bf8993589c..ebb4b58c198b9572f3339e04078c0e2c9f4e8fbe 100644 (file)
@@ -13,6 +13,7 @@
 #include <sound/soc.h>
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 static const struct snd_soc_dapm_widget rt712_spk_widgets[] = {
@@ -34,7 +35,7 @@ static const struct snd_kcontrol_new rt712_spk_controls[] = {
        SOC_DAPM_PIN_SWITCH("Speaker"),
 };
 
-static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd)
+int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        int ret;
@@ -66,23 +67,21 @@ static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd)
        return ret;
 }
 
-int sof_sdw_rt712_spk_init(struct snd_soc_card *card,
-                          const struct snd_soc_acpi_link_adr *link,
-                          struct snd_soc_dai_link *dai_links,
-                          struct sof_sdw_codec_info *info,
-                          bool playback)
-{
-       dai_links->init = rt712_spk_init;
-
-       return 0;
-}
+static const char * const dmics[] = {
+       "rt712-sdca-dmic"
+};
 
-static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
+int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
+
+       codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics));
+       if (!codec_dai)
+               return -EINVAL;
 
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s mic:%s",
                                          card->components, component->name_prefix);
@@ -91,14 +90,4 @@ static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
 
        return 0;
 }
-
-int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card,
-                                const struct snd_soc_acpi_link_adr *link,
-                                struct snd_soc_dai_link *dai_links,
-                                struct sof_sdw_codec_info *info,
-                                bool playback)
-{
-       dai_links->init = rt712_sdca_dmic_rtd_init;
-
-       return 0;
-}
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index 7c068dc6b9cf65467effec9efb25be77d90065f3..b5a886cd595dc6d23c1eedf81bf5988a5fe5d483 100644 (file)
@@ -11,7 +11,7 @@
 #include <sound/soc-acpi.h>
 #include "sof_sdw_common.h"
 
-static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd)
+int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
 
@@ -24,13 +24,3 @@ static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
-int sof_sdw_rt715_init(struct snd_soc_card *card,
-                      const struct snd_soc_acpi_link_adr *link,
-                      struct snd_soc_dai_link *dai_links,
-                      struct sof_sdw_codec_info *info,
-                      bool playback)
-{
-       dai_links->init = rt715_rtd_init;
-
-       return 0;
-}
index ca0cf3db2e4d8b238e3820d96acd5d41e1f22972..4b37a8a6dd2e27c83a456c76cb3800fedd79bfdf 100644 (file)
@@ -11,7 +11,7 @@
 #include <sound/soc-acpi.h>
 #include "sof_sdw_common.h"
 
-static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
+int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
 
@@ -24,13 +24,3 @@ static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
-int sof_sdw_rt715_sdca_init(struct snd_soc_card *card,
-                           const struct snd_soc_acpi_link_adr *link,
-                           struct snd_soc_dai_link *dai_links,
-                           struct sof_sdw_codec_info *info,
-                           bool playback)
-{
-       dai_links->init = rt715_sdca_rtd_init;
-
-       return 0;
-}
index 436975b6bdc1760aead908a2c48c1d5640e0f0d6..202edab950001dbc61470041db700b674a8b1a57 100644 (file)
@@ -185,12 +185,14 @@ static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_
                return rt1318_map;
 }
 
-static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
+int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        const struct snd_soc_dapm_route *rt_amp_map;
        char codec_name[CODEC_NAME_SIZE];
+       struct snd_soc_dai *dai;
        int ret;
+       int i;
 
        rt_amp_map = get_codec_name_and_route(rtd, codec_name);
 
@@ -214,40 +216,16 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
-       if (ret)
-               dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
-
-       return ret;
-}
-
-static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_card *card = rtd->card;
-       const struct snd_soc_dapm_route *rt_amp_map;
-       char codec_name[CODEC_NAME_SIZE];
-       int ret;
-
-       rt_amp_map = get_codec_name_and_route(rtd, codec_name);
-
-       ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
-       if (ret)
-               dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
+       for_each_rtd_codec_dais(rtd, i, dai) {
+               if (strstr(dai->component->name_prefix, "-1"))
+                       ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
+               else if (strstr(dai->component->name_prefix, "-2"))
+                       ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
+       }
 
        return ret;
 }
 
-static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
-       int ret;
-
-       ret = first_spk_init(rtd);
-       if (ret)
-               return ret;
-
-       return second_spk_init(rtd);
-}
-
 static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params)
 {
@@ -317,8 +295,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card,
                return 0;
 
        info->amp_num++;
-       if (info->amp_num == 1)
-               dai_links->init = first_spk_init;
 
        if (info->amp_num == 2) {
                sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
@@ -342,17 +318,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card,
                        return ret;
                }
                ctx->amp_dev2 = sdw_dev2;
-
-               /*
-                * if two amps are in one dai link, the init function
-                * in this dai link will be first set for the first speaker,
-                * and it should be reset to initialize all speakers when
-                * the second speaker is found.
-                */
-               if (dai_links->init)
-                       dai_links->init = all_spk_init;
-               else
-                       dai_links->init = second_spk_init;
        }
 
        return 0;
index d9c283829fc7ccf5d9b6718b9d1beee91746eaea..5253d8332780f78bb977e1ec1379d3aa4daa3275 100644 (file)
@@ -15,6 +15,7 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-dapm.h>
 #include <sound/jack.h>
+#include "sof_board_helpers.h"
 #include "sof_sdw_common.h"
 
 /*
@@ -84,15 +85,24 @@ static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {
        },
 };
 
-static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static const char * const jack_codecs[] = {
+       "rt711", "rt712", "rt713"
+};
+
+int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-       struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
-       struct snd_soc_component *component = codec_dai->component;
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
        struct snd_soc_jack *jack;
        int ret;
 
+       codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs));
+       if (!codec_dai)
+               return -EINVAL;
+
+       component = codec_dai->component;
        card->components = devm_kasprintf(card->dev, GFP_KERNEL,
                                          "%s hs:%s-sdca",
                                          card->components, component->name_prefix);
@@ -209,7 +219,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
        }
        ctx->headset_codec_dev = sdw_dev;
 
-       dai_links->init = rt_sdca_jack_rtd_init;
-
        return 0;
 }
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
index 6d827103479b5d1c504b1aeca33f95b83cb462e7..d24888bc99fde97808fc441dc74a59b620f05fe2 100644 (file)
@@ -67,6 +67,14 @@ enum sof_ssp_codec {
 
 enum sof_ssp_codec sof_ssp_detect_codec_type(struct device *dev);
 enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev);
+
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SOF_SSP_COMMON)
 const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type);
+#else
+static inline const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type)
+{
+       return NULL;
+}
+#endif
 
 #endif /* __SOF_SSP_COMMON_H */
index 346bec000306691a717a0981b6e7b8a521498bb0..5454c6d9ab5b1487c3a282e47bf55a4b0232e3ac 100644 (file)
@@ -387,7 +387,7 @@ int catpt_dsp_power_down(struct catpt_dev *cdev)
        mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
        catpt_updatel_pci(cdev, VDRTCTL0, mask, cdev->spec->d3pgd_bit);
 
-       catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D3hot);
+       catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot);
        /* give hw time to drop off */
        udelay(50);
 
@@ -411,7 +411,7 @@ int catpt_dsp_power_up(struct catpt_dev *cdev)
        val = mask & (~CATPT_VDRTCTL2_DTCGE);
        catpt_updatel_pci(cdev, VDRTCTL2, mask, val);
 
-       catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D0);
+       catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0);
 
        /* SRAM power gating none */
        mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
index d3d913458c607f20b7349679ab5807d26194226d..0da79a3ba1f0b0c8d531c608e579a329a7fcdee2 100644 (file)
@@ -500,7 +500,7 @@ static struct snd_soc_acpi_codecs adl_rt5650_amp = {
 struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        {
                .comp_ids = &adl_rt5682_rt5682s_hp,
-               .drv_name = "adl_mx98373_rt5682",
+               .drv_name = "adl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &adl_max98373_amp,
                .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg",
@@ -514,7 +514,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        },
        {
                .comp_ids = &adl_rt5682_rt5682s_hp,
-               .drv_name = "adl_mx98360_rt5682",
+               .drv_name = "adl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &adl_max98360a_amp,
                .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg",
@@ -542,7 +542,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        },
        {
                .comp_ids = &adl_rt5682_rt5682s_hp,
-               .drv_name = "adl_rt1019_rt5682",
+               .drv_name = "adl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &adl_rt1019p_amp,
                .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg",
@@ -568,7 +568,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        },
        {
                .comp_ids = &adl_rt5682_rt5682s_hp,
-               .drv_name = "adl_max98390_rt5682",
+               .drv_name = "adl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &adl_max98390_amp,
                .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg",
@@ -582,7 +582,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        },
        {
                .comp_ids = &adl_rt5682_rt5682s_hp,
-               .drv_name = "adl_rt5682",
+               .drv_name = "adl_rt5682_def",
                .sof_tplg_filename = "sof-adl-rt5682.tplg",
        },
        {
@@ -609,7 +609,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
        },
        {
                .id = "10EC5650",
-               .drv_name = "adl_rt5650",
+               .drv_name = "adl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &adl_rt5650_amp,
                .sof_tplg_filename = "sof-adl-rt5650.tplg",
index 342bbbb48ca7e4b93dbec3bdba491923cc9f4cfc..a6ac2525df175ead327ac987f3f5ab7e129ba19a 100644 (file)
@@ -66,28 +66,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
        },
        {
                .comp_ids = &rt5682_rt5682s_hp,
-               .drv_name = "jsl_rt5682_rt1015",
+               .drv_name = "jsl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &rt1015_spk,
                .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
        },
        {
                .comp_ids = &rt5682_rt5682s_hp,
-               .drv_name = "jsl_rt5682_rt1015p",
+               .drv_name = "jsl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &rt1015p_spk,
                .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
        },
        {
                .comp_ids = &rt5682_rt5682s_hp,
-               .drv_name = "jsl_rt5682_mx98360",
+               .drv_name = "jsl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &mx98360a_spk,
                .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg",
        },
        {
                .comp_ids = &rt5682_rt5682s_hp,
-               .drv_name = "jsl_rt5682",
+               .drv_name = "jsl_rt5682_def",
                .sof_tplg_filename = "sof-jsl-rt5682.tplg",
        },
        {
@@ -107,7 +107,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
        },
        {
                .id = "10EC5650",
-               .drv_name = "jsl_rt5650",
+               .drv_name = "jsl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &rt5650_spk,
                .sof_tplg_filename = "sof-jsl-rt5650.tplg",
index 5897bb6b28b887fcc8b77c4308f317077b301b65..74d6dcd7471f061aabe17e229577e274c5476c1d 100644 (file)
@@ -36,6 +36,46 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
        .group_id = 1,
 };
 
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+       {
+               .num = 0,
+               .aggregated = 0,
+               .group_position = 0,
+               .group_id = 0,
+       },
+       {
+               .num = 1,
+               .aggregated = 0,
+               .group_position = 0,
+               .group_id = 0,
+       },
+};
+
+/*
+ * 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[] = {
+       {
+               .num = 0,
+               .aggregated = 0,
+               .group_position = 0,
+               .group_id = 0,
+       },
+       {
+               .num = 1,
+               .aggregated = 0,
+               .group_position = 0,
+               .group_id = 0,
+       },
+       {
+               .num = 2,
+               .aggregated = 0,
+               .group_position = 0,
+               .group_id = 0,
+       },
+};
+
 static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
        {
                .adr = 0x000030025D071101ull,
@@ -45,6 +85,33 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
        }
 };
 
+static const struct snd_soc_acpi_adr_device rt712_2_single_adr[] = {
+       {
+               .adr = 0x000230025D071201ull,
+               .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+               .endpoints = rt712_endpoints,
+               .name_prefix = "rt712"
+       }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
+       {
+               .adr = 0x000330025D171201ull,
+               .num_endpoints = 1,
+               .endpoints = &single_endpoint,
+               .name_prefix = "rt712-dmic"
+       }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+       {
+               .adr = 0x000030025d072201ull,
+               .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+               .endpoints = rt722_endpoints,
+               .name_prefix = "rt722"
+       }
+};
+
 static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
        {
                .adr = 0x000230025D131601ull,
@@ -81,6 +148,29 @@ static const struct snd_soc_acpi_link_adr lnl_rvp[] = {
        {}
 };
 
+static const struct snd_soc_acpi_link_adr lnl_712_only[] = {
+       {
+               .mask = BIT(2),
+               .num_adr = ARRAY_SIZE(rt712_2_single_adr),
+               .adr_d = rt712_2_single_adr,
+       },
+       {
+               .mask = BIT(3),
+               .num_adr = ARRAY_SIZE(rt1712_3_single_adr),
+               .adr_d = rt1712_3_single_adr,
+       },
+       {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_rt722_only[] = {
+       {
+               .mask = BIT(0),
+               .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+               .adr_d = rt722_0_single_adr,
+       },
+       {}
+};
+
 static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = {
        {
                .mask = BIT(0),
@@ -138,6 +228,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
                .drv_name = "sof_sdw",
                .sof_tplg_filename = "sof-lnl-rt711.tplg",
        },
+       {
+               .link_mask = BIT(2) | BIT(3),
+               .links = lnl_712_only,
+               .drv_name = "sof_sdw",
+               .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg",
+       },
+       {
+               .link_mask = BIT(0),
+               .links = lnl_rt722_only,
+               .drv_name = "sof_sdw",
+               .sof_tplg_filename = "sof-lnl-rt722-l0.tplg",
+       },
        {},
 };
 EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines);
index feb12c6c85d15d01e9e539d3bd29e2fd861845c9..e9a5da0790890910273f49f7021db5efd1746284 100644 (file)
@@ -62,7 +62,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
        },
        {
                .comp_ids = &mtl_rt5682_rt5682s_hp,
-               .drv_name = "mtl_rt1019_rt5682",
+               .drv_name = "mtl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &mtl_rt1019p_amp,
                .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg",
@@ -84,7 +84,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
        },
        {
                .id = "10EC5650",
-               .drv_name = "mtl_rt5650",
+               .drv_name = "mtl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &mtl_rt5650_amp,
                .sof_tplg_filename = "sof-mtl-rt5650.tplg",
@@ -377,6 +377,37 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = {
        }
 };
 
+static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
+       {
+               .adr = 0x00023201FA355601ull,
+               .num_endpoints = 1,
+               .endpoints = &spk_r_endpoint,
+               .name_prefix = "AMP3"
+       },
+       {
+               .adr = 0x00023301FA355601ull,
+               .num_endpoints = 1,
+               .endpoints = &spk_3_endpoint,
+               .name_prefix = "AMP4"
+       }
+
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
+       {
+               .adr = 0x00033001fa355601ull,
+               .num_endpoints = 1,
+               .endpoints = &spk_l_endpoint,
+               .name_prefix = "AMP1"
+       },
+       {
+               .adr = 0x00033101fa355601ull,
+               .num_endpoints = 1,
+               .endpoints = &spk_2_endpoint,
+               .name_prefix = "AMP2"
+       }
+};
+
 static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = {
        /* Expected order: jack -> amp */
        {
@@ -554,6 +585,26 @@ static const struct snd_soc_acpi_link_adr mtl_cs42l43_cs35l56[] = {
        {}
 };
 
+static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] = {
+       /* Expected order: jack -> amp */
+       {
+               .mask = BIT(0),
+               .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+               .adr_d = cs42l43_0_adr,
+       },
+       {
+               .mask = BIT(2),
+               .num_adr = ARRAY_SIZE(cs35l56_2_r_adr),
+               .adr_d = cs35l56_2_r_adr,
+       },
+       {
+               .mask = BIT(3),
+               .num_adr = ARRAY_SIZE(cs35l56_3_l_adr),
+               .adr_d = cs35l56_3_l_adr,
+       },
+       {}
+};
+
 /* this table is used when there is no I2S codec present */
 struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
        /* mockup tests need to be first */
@@ -599,6 +650,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
                .drv_name = "sof_sdw",
                .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg"
        },
+       {
+               .link_mask = BIT(0) | BIT(2) | BIT(3),
+               .links = cs42l43_link0_cs35l56_link2_link3,
+               .drv_name = "sof_sdw",
+               .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg",
+       },
        {
                .link_mask = GENMASK(2, 0),
                .links = mtl_cs42l43_cs35l56,
index c0a643f4725aa9bd8fabb609aa6ae95b25a44a65..00a21af210fa43bef143fd3ae192e8a0c32a77b3 100644 (file)
@@ -395,7 +395,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
        },
        {
                .comp_ids = &rpl_rt5682_hp,
-               .drv_name = "rpl_mx98360_rt5682",
+               .drv_name = "rpl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &rpl_max98360a_amp,
                .sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg",
@@ -423,7 +423,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
        },
        {
                .comp_ids = &rpl_rt5682_hp,
-               .drv_name = "rpl_rt1019_rt5682",
+               .drv_name = "rpl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &rpl_rt1019p_amp,
                .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg",
index e5f721ba5ed478cdb108cc7be1564566aa53bb0a..0fba0a60d9c79361c5fb6856458224e21f5eb4c4 100644 (file)
@@ -495,21 +495,21 @@ static const struct snd_soc_acpi_codecs tgl_lt6911_hdmi = {
 struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
        {
                .comp_ids = &tgl_rt5682_rt5682s_hp,
-               .drv_name = "tgl_mx98357_rt5682",
+               .drv_name = "tgl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &tgl_codecs,
                .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg",
        },
        {
                .comp_ids = &tgl_rt5682_rt5682s_hp,
-               .drv_name = "tgl_mx98373_rt5682",
+               .drv_name = "tgl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &tgl_max98373_amp,
                .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg",
        },
        {
                .comp_ids = &tgl_rt5682_rt5682s_hp,
-               .drv_name = "tgl_rt1011_rt5682",
+               .drv_name = "tgl_rt5682_def",
                .machine_quirk = snd_soc_acpi_codec_list,
                .quirk_data = &tgl_rt1011_amp,
                .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg",
index 7109b81cc3d0a76e5df30b5f3f39627dbac4d36b..5d1419ed7a62d97faef3f9681c1b3a526b95a684 100644 (file)
@@ -212,11 +212,12 @@ static const char * const aiu_spdif_ids[] = {
 static int aiu_clk_get(struct device *dev)
 {
        struct aiu *aiu = dev_get_drvdata(dev);
+       struct clk *pclk;
        int ret;
 
-       aiu->pclk = devm_clk_get(dev, "pclk");
-       if (IS_ERR(aiu->pclk))
-               return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n");
+       pclk = devm_clk_get_enabled(dev, "pclk");
+       if (IS_ERR(pclk))
+               return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n");
 
        aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
        if (IS_ERR(aiu->spdif_mclk))
@@ -233,18 +234,6 @@ static int aiu_clk_get(struct device *dev)
        if (ret)
                return dev_err_probe(dev, ret, "Can't get the spdif clocks\n");
 
-       ret = clk_prepare_enable(aiu->pclk);
-       if (ret) {
-               dev_err(dev, "peripheral clock enable failed\n");
-               return ret;
-       }
-
-       ret = devm_add_action_or_reset(dev,
-                                      (void(*)(void *))clk_disable_unprepare,
-                                      aiu->pclk);
-       if (ret)
-               dev_err(dev, "failed to add reset action on pclk");
-
        return ret;
 }
 
index 393b6c2307e49ff212c6e9676b3072a6f82b587b..0f94c8bf608181112d78402532b832eb50c2d409 100644 (file)
@@ -33,7 +33,6 @@ struct aiu_platform_data {
 };
 
 struct aiu {
-       struct clk *pclk;
        struct clk *spdif_mclk;
        struct aiu_interface i2s;
        struct aiu_interface spdif;
index 65541fdb0038a34c3729837a9cac92f75dddf8e7..bebee0ca8e3889a920a486b01c1a8730eb1988d4 100644 (file)
@@ -3,6 +3,7 @@
 // Copyright (c) 2018 BayLibre, SAS.
 // Author: Jerome Brunet <jbrunet@baylibre.com>
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
@@ -145,8 +146,8 @@ int axg_fifo_pcm_hw_params(struct snd_soc_component *component,
        /* Enable irq if necessary  */
        irq_en = runtime->no_period_wakeup ? 0 : FIFO_INT_COUNT_REPEAT;
        regmap_update_bits(fifo->map, FIFO_CTRL0,
-                          CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT),
-                          CTRL0_INT_EN(irq_en));
+                          CTRL0_INT_EN,
+                          FIELD_PREP(CTRL0_INT_EN, irq_en));
 
        return 0;
 }
@@ -176,9 +177,9 @@ int axg_fifo_pcm_hw_free(struct snd_soc_component *component,
 {
        struct axg_fifo *fifo = axg_fifo_data(ss);
 
-       /* Disable the block count irq */
+       /* Disable irqs */
        regmap_update_bits(fifo->map, FIFO_CTRL0,
-                          CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0);
+                          CTRL0_INT_EN, 0);
 
        return 0;
 }
@@ -187,13 +188,13 @@ EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free);
 static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask)
 {
        regmap_update_bits(fifo->map, FIFO_CTRL1,
-                          CTRL1_INT_CLR(FIFO_INT_MASK),
-                          CTRL1_INT_CLR(mask));
+                          CTRL1_INT_CLR,
+                          FIELD_PREP(CTRL1_INT_CLR, mask));
 
        /* Clear must also be cleared */
        regmap_update_bits(fifo->map, FIFO_CTRL1,
-                          CTRL1_INT_CLR(FIFO_INT_MASK),
-                          0);
+                          CTRL1_INT_CLR,
+                          FIELD_PREP(CTRL1_INT_CLR, 0));
 }
 
 static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
@@ -204,7 +205,7 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
 
        regmap_read(fifo->map, FIFO_STATUS1, &status);
 
-       status = STATUS1_INT_STS(status) & FIFO_INT_MASK;
+       status = FIELD_GET(STATUS1_INT_STS, status);
        if (status & FIFO_INT_COUNT_REPEAT)
                snd_pcm_period_elapsed(ss);
        else
@@ -254,15 +255,15 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
 
        /* Setup status2 so it reports the memory pointer */
        regmap_update_bits(fifo->map, FIFO_CTRL1,
-                          CTRL1_STATUS2_SEL_MASK,
-                          CTRL1_STATUS2_SEL(STATUS2_SEL_DDR_READ));
+                          CTRL1_STATUS2_SEL,
+                          FIELD_PREP(CTRL1_STATUS2_SEL, STATUS2_SEL_DDR_READ));
 
        /* Make sure the dma is initially disabled */
        __dma_enable(fifo, false);
 
        /* Disable irqs until params are ready */
        regmap_update_bits(fifo->map, FIFO_CTRL0,
-                          CTRL0_INT_EN(FIFO_INT_MASK), 0);
+                          CTRL0_INT_EN, 0);
 
        /* Clear any pending interrupt */
        axg_fifo_ack_irq(fifo, FIFO_INT_MASK);
index df528e8cb7c91339acfdebfb8a6d91358fdcfaaa..4c48c0a0848141de42095a15062c14f74f447a91 100644 (file)
@@ -21,8 +21,6 @@ struct snd_soc_dai_driver;
 struct snd_soc_pcm_runtime;
 
 #define AXG_FIFO_CH_MAX                        128
-#define AXG_FIFO_RATES                 (SNDRV_PCM_RATE_5512 |          \
-                                        SNDRV_PCM_RATE_8000_384000)
 #define AXG_FIFO_FORMATS               (SNDRV_PCM_FMTBIT_S8 |          \
                                         SNDRV_PCM_FMTBIT_S16_LE |      \
                                         SNDRV_PCM_FMTBIT_S20_LE |      \
@@ -42,21 +40,19 @@ struct snd_soc_pcm_runtime;
 
 #define FIFO_CTRL0                     0x00
 #define  CTRL0_DMA_EN                  BIT(31)
-#define  CTRL0_INT_EN(x)               ((x) << 16)
+#define  CTRL0_INT_EN                  GENMASK(23, 16)
 #define  CTRL0_SEL_MASK                        GENMASK(2, 0)
 #define  CTRL0_SEL_SHIFT               0
 #define FIFO_CTRL1                     0x04
-#define  CTRL1_INT_CLR(x)              ((x) << 0)
-#define  CTRL1_STATUS2_SEL_MASK                GENMASK(11, 8)
-#define  CTRL1_STATUS2_SEL(x)          ((x) << 8)
+#define  CTRL1_INT_CLR                 GENMASK(7, 0)
+#define  CTRL1_STATUS2_SEL             GENMASK(11, 8)
 #define   STATUS2_SEL_DDR_READ         0
-#define  CTRL1_FRDDR_DEPTH_MASK                GENMASK(31, 24)
-#define  CTRL1_FRDDR_DEPTH(x)          ((x) << 24)
+#define  CTRL1_FRDDR_DEPTH             GENMASK(31, 24)
 #define FIFO_START_ADDR                        0x08
 #define FIFO_FINISH_ADDR               0x0c
 #define FIFO_INT_ADDR                  0x10
 #define FIFO_STATUS1                   0x14
-#define  STATUS1_INT_STS(x)            ((x) << 0)
+#define  STATUS1_INT_STS               GENMASK(7, 0)
 #define FIFO_STATUS2                   0x18
 #define FIFO_INIT_ADDR                 0x24
 #define FIFO_CTRL2                     0x28
index 8c166a5f338ced6fdee51bb02d2051ead1700a8e..e97d43ae7fd291da826634de659147a319f400c1 100644 (file)
@@ -7,6 +7,7 @@
  * This driver implements the frontend playback DAI of AXG and G12A based SoCs
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/regmap.h>
 #include <linux/module.h>
@@ -59,8 +60,8 @@ static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream,
        /* Trim the FIFO depth if the period is small to improve latency */
        depth = min(period, fifo->depth);
        val = (depth / AXG_FIFO_BURST) - 1;
-       regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH_MASK,
-                          CTRL1_FRDDR_DEPTH(val));
+       regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH,
+                          FIELD_PREP(CTRL1_FRDDR_DEPTH, val));
 
        return 0;
 }
@@ -109,7 +110,9 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = {
                .stream_name    = "Playback",
                .channels_min   = 1,
                .channels_max   = AXG_FIFO_CH_MAX,
-               .rates          = AXG_FIFO_RATES,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min       = 5515,
+               .rate_max       = 384000,
                .formats        = AXG_FIFO_FORMATS,
        },
        .ops            = &axg_frddr_ops,
@@ -184,7 +187,9 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = {
                .stream_name    = "Playback",
                .channels_min   = 1,
                .channels_max   = AXG_FIFO_CH_MAX,
-               .rates          = AXG_FIFO_RATES,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min       = 5515,
+               .rate_max       = 384000,
                .formats        = AXG_FIFO_FORMATS,
        },
        .ops            = &g12a_frddr_ops,
index bc2f2849ecfbce1f5e12d8cdc7d56858b6c56798..e721f579321ec22dd18af220f48d224ff8c8cfc3 100644 (file)
@@ -179,9 +179,9 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai,
                           SPDIFIN_CTRL1_BASE_TIMER,
                           FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000));
 
-       /* Threshold based on the minimum width between two edges */
+       /* Threshold based on the maximum width between two edges */
        regmap_update_bits(priv->map, SPDIFIN_CTRL0,
-                          SPDIFIN_CTRL0_WIDTH_SEL, SPDIFIN_CTRL0_WIDTH_SEL);
+                          SPDIFIN_CTRL0_WIDTH_SEL, 0);
 
        /* Calculate the last timer which has no threshold */
        t_next = axg_spdifin_mode_timer(priv, i, rate);
@@ -199,7 +199,7 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai,
                axg_spdifin_write_timer(priv->map, i, t);
 
                /* Set the threshold value */
-               axg_spdifin_write_threshold(priv->map, i, t + t_next);
+               axg_spdifin_write_threshold(priv->map, i, 3 * (t + t_next));
 
                /* Save the current timer for the next threshold calculation */
                t_next = t;
index 1c3d433cefd23c400850429d1f9211f9d0da47a9..bf708717635bf6531195a1039aee41c3166d8154 100644 (file)
@@ -12,6 +12,9 @@
 
 #include "axg-tdm.h"
 
+/* Maximum bit clock frequency according the datasheets */
+#define MAX_SCLK 100000000 /* Hz */
+
 enum {
        TDM_IFACE_PAD,
        TDM_IFACE_LOOPBACK,
@@ -130,7 +133,7 @@ static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
        case SND_SOC_DAIFMT_BP_FC:
        case SND_SOC_DAIFMT_BC_FP:
-               dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n");
+               dev_err(dai->dev, "only BP_FP and BC_FC are supported\n");
                fallthrough;
        default:
                return -EINVAL;
@@ -153,19 +156,27 @@ static int axg_tdm_iface_startup(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /* Apply component wide rate symmetry */
        if (snd_soc_component_active(dai->component)) {
+               /* Apply component wide rate symmetry */
                ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                   SNDRV_PCM_HW_PARAM_RATE,
                                                   iface->rate);
-               if (ret < 0) {
-                       dev_err(dai->dev,
-                               "can't set iface rate constraint\n");
-                       return ret;
-               }
+
+       } else {
+               /* Limit rate according to the slot number and width */
+               unsigned int max_rate =
+                       MAX_SCLK / (iface->slots * iface->slot_width);
+               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                                  SNDRV_PCM_HW_PARAM_RATE,
+                                                  0, max_rate);
        }
 
-       return 0;
+       if (ret < 0)
+               dev_err(dai->dev, "can't set iface rate constraint\n");
+       else
+               ret = 0;
+
+       return ret;
 }
 
 static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream,
@@ -264,8 +275,8 @@ static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai,
        srate = iface->slots * iface->slot_width * params_rate(params);
 
        if (!iface->mclk_rate) {
-               /* If no specific mclk is requested, default to bit clock * 4 */
-               clk_set_rate(iface->mclk, 4 * srate);
+               /* If no specific mclk is requested, default to bit clock * 2 */
+               clk_set_rate(iface->mclk, 2 * srate);
        } else {
                /* Check if we can actually get the bit clock from mclk */
                if (iface->mclk_rate % srate) {
index 1a0be177b8fe77b13730dfb8f3e5e9450502dacc..e03a6e21c1c649e6e9aa5a55fb3c3a2bb2e899e9 100644 (file)
@@ -5,6 +5,7 @@
 
 /* This driver implements the frontend capture DAI of AXG based SoCs */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/regmap.h>
 #include <linux/module.h>
 #define CTRL0_TODDR_EXT_SIGNED         BIT(29)
 #define CTRL0_TODDR_PP_MODE            BIT(28)
 #define CTRL0_TODDR_SYNC_CH            BIT(27)
-#define CTRL0_TODDR_TYPE_MASK          GENMASK(15, 13)
-#define CTRL0_TODDR_TYPE(x)            ((x) << 13)
-#define CTRL0_TODDR_MSB_POS_MASK       GENMASK(12, 8)
-#define CTRL0_TODDR_MSB_POS(x)         ((x) << 8)
-#define CTRL0_TODDR_LSB_POS_MASK       GENMASK(7, 3)
-#define CTRL0_TODDR_LSB_POS(x)         ((x) << 3)
+#define CTRL0_TODDR_TYPE               GENMASK(15, 13)
+#define CTRL0_TODDR_MSB_POS            GENMASK(12, 8)
+#define CTRL0_TODDR_LSB_POS            GENMASK(7, 3)
 #define CTRL1_TODDR_FORCE_FINISH       BIT(25)
 #define CTRL1_SEL_SHIFT                        28
 
@@ -76,12 +74,12 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream,
        width = params_width(params);
 
        regmap_update_bits(fifo->map, FIFO_CTRL0,
-                          CTRL0_TODDR_TYPE_MASK |
-                          CTRL0_TODDR_MSB_POS_MASK |
-                          CTRL0_TODDR_LSB_POS_MASK,
-                          CTRL0_TODDR_TYPE(type) |
-                          CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) |
-                          CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1)));
+                          CTRL0_TODDR_TYPE |
+                          CTRL0_TODDR_MSB_POS |
+                          CTRL0_TODDR_LSB_POS,
+                          FIELD_PREP(CTRL0_TODDR_TYPE, type) |
+                          FIELD_PREP(CTRL0_TODDR_MSB_POS, TODDR_MSB_POS) |
+                          FIELD_PREP(CTRL0_TODDR_LSB_POS, TODDR_MSB_POS - (width - 1)));
 
        return 0;
 }
@@ -131,7 +129,9 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = {
                .stream_name    = "Capture",
                .channels_min   = 1,
                .channels_max   = AXG_FIFO_CH_MAX,
-               .rates          = AXG_FIFO_RATES,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min       = 5515,
+               .rate_max       = 384000,
                .formats        = AXG_FIFO_FORMATS,
        },
        .ops            = &axg_toddr_ops,
@@ -226,7 +226,9 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = {
                .stream_name    = "Capture",
                .channels_min   = 1,
                .channels_max   = AXG_FIFO_CH_MAX,
-               .rates          = AXG_FIFO_RATES,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min       = 5515,
+               .rate_max       = 384000,
                .formats        = AXG_FIFO_FORMATS,
        },
        .ops            = &g12a_toddr_ops,
index 9c6b4dac689320d1f6a1ce5ce81e518fbffee44d..571f65788c592050abdca264f5656d4d1a9d99f6 100644 (file)
@@ -48,7 +48,6 @@
 #define POWER_CFG      0x10
 
 struct t9015 {
-       struct clk *pclk;
        struct regulator *avdd;
 };
 
@@ -249,6 +248,7 @@ static int t9015_probe(struct platform_device *pdev)
        struct t9015 *priv;
        void __iomem *regs;
        struct regmap *regmap;
+       struct clk *pclk;
        int ret;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -256,26 +256,14 @@ static int t9015_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, priv);
 
-       priv->pclk = devm_clk_get(dev, "pclk");
-       if (IS_ERR(priv->pclk))
-               return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n");
+       pclk = devm_clk_get_enabled(dev, "pclk");
+       if (IS_ERR(pclk))
+               return dev_err_probe(dev, PTR_ERR(pclk), "failed to get core clock\n");
 
        priv->avdd = devm_regulator_get(dev, "AVDD");
        if (IS_ERR(priv->avdd))
                return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n");
 
-       ret = clk_prepare_enable(priv->pclk);
-       if (ret) {
-               dev_err(dev, "core clock enable failed\n");
-               return ret;
-       }
-
-       ret = devm_add_action_or_reset(dev,
-                       (void(*)(void *))clk_disable_unprepare,
-                       priv->pclk);
-       if (ret)
-               return ret;
-
        ret = device_reset(dev);
        if (ret) {
                dev_err(dev, "reset failed\n");
index f03c74809324ea217dd3d5bf51e7f7ef50fbe593..e05d6ce4c8fa2663b10c90d842127c9aa396d867 100644 (file)
@@ -8,9 +8,6 @@ config SND_PXA2XX_SOC
          the PXA2xx AC97, I2S or SSP interface. You will also need
          to select the audio interfaces to support below.
 
-config SND_PXA2XX_AC97
-       tristate
-
 config SND_PXA2XX_SOC_AC97
        tristate "SoC AC97 support for PXA2xx"
        depends on SND_PXA2XX_SOC
index e73bd62c033c5330313cb9f8f7fb039eb02e9ca6..80e0ea0ec9fb3af9fd29e4d70badfbfe3223c961 100644 (file)
@@ -271,7 +271,6 @@ static void pxa2xx_ac97_dev_remove(struct platform_device *pdev)
        pxa2xx_ac97_hw_remove(pdev);
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int pxa2xx_ac97_dev_suspend(struct device *dev)
 {
        return pxa2xx_ac97_hw_suspend();
@@ -282,18 +281,15 @@ static int pxa2xx_ac97_dev_resume(struct device *dev)
        return pxa2xx_ac97_hw_resume();
 }
 
-static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
                pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume);
-#endif
 
 static struct platform_driver pxa2xx_ac97_driver = {
        .probe          = pxa2xx_ac97_dev_probe,
        .remove_new     = pxa2xx_ac97_dev_remove,
        .driver         = {
                .name   = "pxa2xx-ac97",
-#ifdef CONFIG_PM_SLEEP
                .pm     = &pxa2xx_ac97_pm_ops,
-#endif
                .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids),
        },
 };
index 756706d5b4930193cbe8558dbfbf5108b23eb9a8..747041fa7866579d8bf614537bbeab8665e60d3b 100644 (file)
@@ -73,7 +73,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
        link = card->dai_link;
 
        for_each_available_child_of_node(dev->of_node, np) {
-               dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
+               dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL);
                if (!dlc) {
                        ret = -ENOMEM;
                        goto err_put_np;
index 14cf1a41fb0d19c6b360fb42559ec1db85c5c8a6..9d103646973ad8656f42dbc498fdf450fe9e4e7e 100644 (file)
@@ -1015,7 +1015,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
                                               dev_name(&pdev->dev), ssi);
                        if (ret < 0)
                                return dev_err_probe(&pdev->dev, ret,
-                                               "irq request error (dma_tx)\n");
+                                                    "irq request error (dma_rt)\n");
                } else {
                        if (ssi->irq_tx < 0)
                                return ssi->irq_tx;
index 516350533e73f8ee1084164e44068f825eb7fafe..1e94edba12eb85706d0f1f0acf7c75ad3a8249eb 100644 (file)
@@ -238,8 +238,8 @@ static inline void snd_soc_debugfs_exit(void) { }
 
 #endif
 
-static int snd_soc_is_match_dai_args(struct of_phandle_args *args1,
-                                    struct of_phandle_args *args2)
+static int snd_soc_is_match_dai_args(const struct of_phandle_args *args1,
+                                    const struct of_phandle_args *args2)
 {
        if (!args1 || !args2)
                return 0;
@@ -283,11 +283,11 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
 
        /* see snd_soc_dai_name_get() */
 
-       if (strcmp(dlc->dai_name, dai->name) == 0)
+       if (dai->driver->name &&
+           strcmp(dlc->dai_name, dai->driver->name) == 0)
                return 1;
 
-       if (dai->driver->name &&
-           strcmp(dai->driver->name, dlc->dai_name) == 0)
+       if (strcmp(dlc->dai_name, dai->name) == 0)
                return 1;
 
        if (dai->component->name &&
@@ -300,12 +300,12 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
 const char *snd_soc_dai_name_get(struct snd_soc_dai *dai)
 {
        /* see snd_soc_is_matching_dai() */
-       if (dai->name)
-               return dai->name;
-
        if (dai->driver->name)
                return dai->driver->name;
 
+       if (dai->name)
+               return dai->name;
+
        if (dai->component->name)
                return dai->component->name;
 
@@ -831,7 +831,8 @@ static struct device_node
        return of_node;
 }
 
-struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args)
+struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev,
+                                             const struct of_phandle_args *args)
 {
        struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL);
 
@@ -3597,7 +3598,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
 
-struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args)
+struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args)
 {
        struct snd_soc_dai *dai;
        struct snd_soc_component *component;
index bffeea80277fc0b1f726c319d9ce4ed1eac83411..ad8ba8fbbaeec01db0b30bb5457b89ec66c49a61 100644 (file)
@@ -725,7 +725,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
        struct snd_soc_card *card = dapm->card;
        int ret = 0;
 
-       trace_snd_soc_bias_level_start(card, level);
+       trace_snd_soc_bias_level_start(dapm, level);
 
        ret = snd_soc_card_set_bias_level(card, dapm, level);
        if (ret != 0)
@@ -739,7 +739,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
 
        ret = snd_soc_card_set_bias_level_post(card, dapm, level);
 out:
-       trace_snd_soc_bias_level_done(card, level);
+       trace_snd_soc_bias_level_done(dapm, level);
 
        return ret;
 }
@@ -1963,7 +1963,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 
        snd_soc_dapm_mutex_assert_held(card);
 
-       trace_snd_soc_dapm_start(card);
+       trace_snd_soc_dapm_start(card, event);
 
        for_each_card_dapms(card, d) {
                if (dapm_idle_bias_off(d))
@@ -2088,7 +2088,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
                "DAPM sequencing finished, waiting %dms\n", card->pop_time);
        pop_wait(card->pop_time);
 
-       trace_snd_soc_dapm_done(card);
+       trace_snd_soc_dapm_done(card, event);
 
        return 0;
 }
index 19c5e27a193f29708e33091e37ad4bfd3e620555..2729c6eb3feb6b38caf0c2782942257ca113fc2d 100644 (file)
@@ -6,6 +6,7 @@
 
 config SND_SOC_SOF_AMD_TOPLEVEL
        tristate "SOF support for AMD audio DSPs"
+       depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
        depends on X86 || COMPILE_TEST
        help
          This adds support for Sound Open Firmware for AMD platforms.
@@ -60,10 +61,27 @@ config SND_SOC_SOF_ACP_PROBES
          This option is not user-selectable but automatically handled by
          'select' statements at a higher level
 
+config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+       tristate
+       select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_SOF_AMD_SOUNDWIRE
+       tristate "SOF support for SoundWire based AMD platforms"
+       default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+       depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+       depends on ACPI
+       depends on SOUNDWIRE_AMD
+       help
+         This adds support for SoundWire with Sound Open Firmware
+         for AMD platforms.
+         Say Y if you want to enable SoundWire links with SOF.
+         If unsure select "N".
+
 config SND_SOC_SOF_AMD_ACP63
        tristate "SOF support for ACP6.3 platform"
        depends on SND_SOC_SOF_PCI
        select SND_SOC_SOF_AMD_COMMON
+       select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
        help
          Select this option for SOF support on
          AMD ACP6.3 version based platforms.
index 2d72c6d55dc80f581c940b2c61121da97a9676b0..0fc4e20ec6737a6ec1a285061044043b830a26f6 100644 (file)
@@ -118,16 +118,72 @@ void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags)
                                 &panic_info, stack, AMD_STACK_DUMP_SIZE);
 }
 
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
+static int amd_sof_sdw_get_slave_info(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
+
+       return sdw_amd_get_slave_info(acp_data->sdw);
+}
+
+static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
+{
+       struct snd_soc_acpi_mach *mach;
+       const struct snd_soc_acpi_link_adr *link;
+       struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
+       int ret, i;
+
+       if (acp_data->info.count) {
+               ret = amd_sof_sdw_get_slave_info(sdev);
+               if (ret) {
+                       dev_info(sdev->dev, "failed to read slave information\n");
+                       return NULL;
+               }
+               for (mach = sdev->pdata->desc->alt_machines; mach; mach++) {
+                       if (!mach->links)
+                               break;
+                       link = mach->links;
+                       for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+                               if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link,
+                                                                       acp_data->sdw->ids,
+                                                                       acp_data->sdw->num_slaves))
+                                       break;
+                       }
+                       if (i == acp_data->info.count || !link->num_adr)
+                               break;
+               }
+               if (mach && mach->link_mask) {
+                       mach->mach_params.links = mach->links;
+                       mach->mach_params.link_mask = mach->link_mask;
+                       mach->mach_params.platform = dev_name(sdev->dev);
+                       return mach;
+               }
+       }
+       dev_info(sdev->dev, "No SoundWire machine driver found\n");
+       return NULL;
+}
+
+#else
+static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
+{
+       return NULL;
+}
+#endif
+
 struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
 {
        struct snd_sof_pdata *sof_pdata = sdev->pdata;
        const struct sof_dev_desc *desc = sof_pdata->desc;
-       struct snd_soc_acpi_mach *mach;
+       struct snd_soc_acpi_mach *mach = NULL;
 
-       mach = snd_soc_acpi_find_machine(desc->machines);
+       if (desc->machines)
+               mach = snd_soc_acpi_find_machine(desc->machines);
        if (!mach) {
-               dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
-               return NULL;
+               mach = amd_sof_sdw_machine_select(sdev);
+               if (!mach) {
+                       dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
+                       return NULL;
+               }
        }
 
        sof_pdata->tplg_filename = mach->sof_tplg_filename;
@@ -204,5 +260,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON);
 
 MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
 MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT);
 MODULE_DESCRIPTION("ACP SOF COMMON Driver");
 MODULE_LICENSE("Dual BSD/GPL");
index a913f1cc4c80970400f6bfa2da24524b1e4bc6bd..59afbe2e0f42059f6d84429bcb601c59d8af0566 100644 (file)
 /* Registers from ACP_INTR block */
 #define ACP3X_EXT_INTR_STAT                    0x1808
 #define ACP5X_EXT_INTR_STAT                    0x1808
+#define ACP6X_EXTERNAL_INTR_ENB                        0x1A00
+#define ACP6X_EXTERNAL_INTR_CNTL               0x1A04
 #define ACP6X_EXT_INTR_STAT                     0x1A0C
+#define ACP6X_EXT_INTR_STAT1                   0x1A10
 
 #define ACP3X_DSP_SW_INTR_BASE                 0x1814
 #define ACP5X_DSP_SW_INTR_BASE                 0x1814
 #define ACP5X_AXI2DAGB_SEM_0                   0x1884
 #define ACP6X_AXI2DAGB_SEM_0                   0x1874
 
+/* ACP common registers to report errors related to I2S & SoundWire interfaces */
+#define ACP_SW0_I2S_ERROR_REASON               0x18B4
+#define ACP_SW1_I2S_ERROR_REASON               0x1A50
+
 /* Registers from ACP_SHA block */
 #define ACP_SHA_DSP_FW_QUALIFIER               0x1C70
 #define ACP_SHA_DMA_CMD                                0x1CB0
 /* Cache window registers */
 #define ACP_DSP0_CACHE_OFFSET0                 0x0420
 #define ACP_DSP0_CACHE_SIZE0                   0x0424
+
+#define ACP_SW0_EN                             0x3000
+#define ACP_SW1_EN                             0x3C00
 #endif
index e05eb7a86dd44a68709983ed88b970beb825ecad..d2d21478399e02d1fd010ad4897b08e58602fd73 100644 (file)
@@ -267,29 +267,49 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev)
 {
        struct snd_sof_pdata *plat_data = sdev->pdata;
        struct acp_dev_data *adata = plat_data->hw_pdata;
+       const char *fw_filename;
        int ret;
 
-       ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev);
+       fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
+                               plat_data->fw_filename_prefix,
+                               adata->fw_code_bin);
+       if (!fw_filename)
+               return -ENOMEM;
+
+       ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
        if (ret < 0) {
+               kfree(fw_filename);
                dev_err(sdev->dev, "sof signed firmware code bin is missing\n");
                return ret;
        } else {
-               dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin);
+               dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename);
        }
+       kfree(fw_filename);
+
        ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0,
-                                     (void *)sdev->basefw.fw->data, sdev->basefw.fw->size);
+                                     (void *)sdev->basefw.fw->data,
+                                     sdev->basefw.fw->size);
+
+       fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
+                               plat_data->fw_filename_prefix,
+                               adata->fw_data_bin);
+       if (!fw_filename)
+               return -ENOMEM;
 
-       ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev);
+       ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev);
        if (ret < 0) {
+               kfree(fw_filename);
                dev_err(sdev->dev, "sof signed firmware data bin is missing\n");
                return ret;
 
        } else {
-               dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin);
+               dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename);
        }
+       kfree(fw_filename);
 
        ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0,
-                                     (void *)adata->fw_dbin->data, adata->fw_dbin->size);
+                                     (void *)adata->fw_dbin->data,
+                                     adata->fw_dbin->size);
        return ret;
 }
 EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON);
index 07632ae6ccf5ec058565d50f1992475795c490d2..9b3c26210db38f97581bf0bb5d0346aa2d06fc49 100644 (file)
@@ -28,11 +28,10 @@ MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug");
 
 const struct dmi_system_id acp_sof_quirk_table[] = {
        {
-               /* Valve Jupiter device */
+               /* Steam Deck OLED device */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
-                       DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"),
                },
                .driver_data = (void *)SECURED_FIRMWARE,
        },
@@ -374,10 +373,13 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
 
 static irqreturn_t acp_irq_handler(int irq, void *dev_id)
 {
+       struct amd_sdw_manager *amd_manager;
        struct snd_sof_dev *sdev = dev_id;
        const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+       struct acp_dev_data *adata = sdev->pdata->hw_pdata;
        unsigned int base = desc->dsp_intr_base;
        unsigned int val;
+       int irq_flag = 0;
 
        val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
        if (val & ACP_DSP_TO_HOST_IRQ) {
@@ -386,7 +388,38 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
                return IRQ_WAKE_THREAD;
        }
 
-       return IRQ_NONE;
+       val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
+       if (val & ACP_SDW0_IRQ_MASK) {
+               amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SDW0_IRQ_MASK);
+               if (amd_manager)
+                       schedule_work(&amd_manager->amd_sdw_irq_thread);
+               irq_flag = 1;
+       }
+
+       if (val & ACP_ERROR_IRQ_MASK) {
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW0_I2S_ERROR_REASON, 0);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW1_I2S_ERROR_REASON, 0);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_ERROR_STATUS, 0);
+               irq_flag = 1;
+       }
+
+       if (desc->ext_intr_stat1) {
+               val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
+               if (val & ACP_SDW1_IRQ_MASK) {
+                       amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+                       snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
+                                         ACP_SDW1_IRQ_MASK);
+                       if (amd_manager)
+                               schedule_work(&amd_manager->amd_sdw_irq_thread);
+                       irq_flag = 1;
+               }
+       }
+       if (irq_flag)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
 }
 
 static int acp_power_on(struct snd_sof_dev *sdev)
@@ -442,6 +475,33 @@ static int acp_reset(struct snd_sof_dev *sdev)
        if (desc->ext_intr_enb)
                snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
 
+       if (desc->ext_intr_cntl)
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK);
+       return ret;
+}
+
+static int acp_dsp_reset(struct snd_sof_dev *sdev)
+{
+       unsigned int val;
+       int ret;
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_ASSERT_RESET);
+
+       ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
+                                           val & ACP_DSP_SOFT_RESET_DONE_MASK,
+                                           ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+       if (ret < 0) {
+               dev_err(sdev->dev, "timeout asserting reset\n");
+               return ret;
+       }
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_RELEASE_RESET);
+
+       ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
+                                           ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+       if (ret < 0)
+               dev_err(sdev->dev, "timeout in releasing reset\n");
+
        return ret;
 }
 
@@ -461,10 +521,34 @@ static int acp_init(struct snd_sof_dev *sdev)
        return acp_reset(sdev);
 }
 
+static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *acp_data;
+       u32 sdw0_en, sdw1_en;
+
+       acp_data = sdev->pdata->hw_pdata;
+       if (!acp_data->sdw)
+               return false;
+
+       sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN);
+       sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN);
+       acp_data->sdw_en_stat = sdw0_en || sdw1_en;
+       return acp_data->sdw_en_stat;
+}
+
 int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state)
 {
        int ret;
 
+       /* When acp_reset() function is invoked, it will apply ACP SOFT reset and
+        * DSP reset. ACP Soft reset sequence will cause all ACP IP registers will
+        * be reset to default values which will break the ClockStop Mode functionality.
+        * Add a condition check to apply DSP reset when SoundWire ClockStop mode
+        * is selected. For the rest of the scenarios, apply acp reset sequence.
+        */
+       if (check_acp_sdw_enable_status(sdev))
+               return acp_dsp_reset(sdev);
+
        ret = acp_reset(sdev);
        if (ret) {
                dev_err(sdev->dev, "ACP Reset failed\n");
@@ -480,20 +564,100 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON);
 int amd_sof_acp_resume(struct snd_sof_dev *sdev)
 {
        int ret;
+       struct acp_dev_data *acp_data;
 
-       ret = acp_init(sdev);
-       if (ret) {
-               dev_err(sdev->dev, "ACP Init failed\n");
-               return ret;
+       acp_data = sdev->pdata->hw_pdata;
+       if (!acp_data->sdw_en_stat) {
+               ret = acp_init(sdev);
+               if (ret) {
+                       dev_err(sdev->dev, "ACP Init failed\n");
+                       return ret;
+               }
+               return acp_memory_init(sdev);
+       } else {
+               return acp_dsp_reset(sdev);
        }
-       return acp_memory_init(sdev);
 }
 EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON);
 
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
+static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
+{
+       struct acpi_device *sdw_dev;
+       struct acp_dev_data *acp_data;
+       const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+
+       if (!addr)
+               return -ENODEV;
+
+       acp_data = sdev->pdata->hw_pdata;
+       sdw_dev = acpi_find_child_device(ACPI_COMPANION(sdev->dev), addr, 0);
+       if (!sdw_dev)
+               return -ENODEV;
+
+       acp_data->info.handle = sdw_dev->handle;
+       acp_data->info.count = desc->sdw_max_link_count;
+
+       return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *acp_data;
+       struct sdw_amd_res sdw_res;
+       int ret;
+
+       acp_data = sdev->pdata->hw_pdata;
+
+       memset(&sdw_res, 0, sizeof(sdw_res));
+       sdw_res.addr = acp_data->addr;
+       sdw_res.reg_range = acp_data->reg_range;
+       sdw_res.handle = acp_data->info.handle;
+       sdw_res.parent = sdev->dev;
+       sdw_res.dev = sdev->dev;
+       sdw_res.acp_lock = &acp_data->acp_lock;
+       sdw_res.count = acp_data->info.count;
+       sdw_res.link_mask = acp_data->info.link_mask;
+       sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR];
+
+       ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+       if (ret)
+               dev_err(sdev->dev, "SoundWire probe failed\n");
+       return ret;
+}
+
+static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *acp_data;
+
+       acp_data = sdev->pdata->hw_pdata;
+       if (acp_data->sdw)
+               sdw_amd_exit(acp_data->sdw);
+       acp_data->sdw = NULL;
+
+       return 0;
+}
+
+#else
+static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
+{
+       return 0;
+}
+
+static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
+{
+       return 0;
+}
+
+static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
+{
+       return 0;
+}
+#endif
+
 int amd_sof_acp_probe(struct snd_sof_dev *sdev)
 {
        struct pci_dev *pci = to_pci_dev(sdev->dev);
-       struct snd_sof_pdata *plat_data = sdev->pdata;
        struct acp_dev_data *adata;
        const struct sof_amd_acp_desc *chip;
        const struct dmi_system_id *dmi_id;
@@ -526,7 +690,9 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        }
 
        pci_set_master(pci);
-
+       adata->addr = addr;
+       adata->reg_range = chip->reg_end_addr - chip->reg_start_addr;
+       mutex_init(&adata->acp_lock);
        sdev->pdata->hw_pdata = adata;
        adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
        if (!adata->smn_dev) {
@@ -548,6 +714,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        if (ret < 0)
                goto free_ipc_irq;
 
+       /* scan SoundWire capabilities exposed by DSDT */
+       ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr);
+       if (ret < 0) {
+               dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n");
+               goto skip_soundwire;
+       }
+       ret = amd_sof_sdw_probe(sdev);
+       if (ret < 0) {
+               dev_err(sdev->dev, "error: SoundWire probe error\n");
+               free_irq(sdev->ipc_irq, sdev);
+               pci_dev_put(adata->smn_dev);
+               return ret;
+       }
+
+skip_soundwire:
        sdev->dsp_box.offset = 0;
        sdev->dsp_box.size = BOX_SIZE_512;
 
@@ -560,17 +741,25 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        adata->signed_fw_image = false;
        dmi_id = dmi_first_match(acp_sof_quirk_table);
        if (dmi_id && dmi_id->driver_data) {
-               adata->fw_code_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-code.bin",
-                                              plat_data->fw_filename_prefix,
-                                              chip->name);
-               adata->fw_data_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-data.bin",
-                                              plat_data->fw_filename_prefix,
-                                              chip->name);
-               adata->signed_fw_image = dmi_id->driver_data;
+               adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
+                                                   "sof-%s-code.bin",
+                                                   chip->name);
+               if (!adata->fw_code_bin) {
+                       ret = -ENOMEM;
+                       goto free_ipc_irq;
+               }
 
-               dev_dbg(sdev->dev, "fw_code_bin:%s, fw_data_bin:%s\n", adata->fw_code_bin,
-                       adata->fw_data_bin);
+               adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
+                                                   "sof-%s-data.bin",
+                                                   chip->name);
+               if (!adata->fw_data_bin) {
+                       ret = -ENOMEM;
+                       goto free_ipc_irq;
+               }
+
+               adata->signed_fw_image = dmi_id->driver_data;
        }
+
        adata->enable_fw_debug = enable_fw_debug;
        acp_memory_init(sdev);
 
@@ -595,6 +784,9 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev)
        if (adata->smn_dev)
                pci_dev_put(adata->smn_dev);
 
+       if (adata->sdw)
+               amd_sof_sdw_exit(sdev);
+
        if (sdev->ipc_irq)
                free_irq(sdev->ipc_irq, sdev);
 
@@ -606,4 +798,6 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev)
 EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
 
 MODULE_DESCRIPTION("AMD ACP sof driver");
+MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT);
+MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI);
 MODULE_LICENSE("Dual BSD/GPL");
index c645aee216fd0b5fa94f4d69e482f103d4015419..947068da39b5350e0f14d05a47b3e4ba1630a7e0 100644 (file)
@@ -12,7 +12,7 @@
 #define __SOF_AMD_ACP_H
 
 #include <linux/dmi.h>
-
+#include <linux/soundwire/sdw_amd.h>
 #include "../sof-priv.h"
 #include "../sof-audio.h"
 
@@ -31,6 +31,9 @@
 #define ACP_ASSERT_RESET                       0x01
 #define ACP_RELEASE_RESET                      0x00
 #define ACP_SOFT_RESET_DONE_MASK               0x00010001
+#define ACP_DSP_ASSERT_RESET                   0x04
+#define ACP_DSP_RELEASE_RESET                  0x00
+#define ACP_DSP_SOFT_RESET_DONE_MASK           0x00050004
 
 #define ACP_DSP_INTR_EN_MASK                   0x00000001
 #define ACP3X_SRAM_PTE_OFFSET                  0x02050000
 #define PROBE_STATUS_BIT                       BIT(31)
 
 #define ACP_FIRMWARE_SIGNATURE                 0x100
+#define ACP_ERROR_IRQ_MASK                     BIT(29)
+#define ACP_SDW0_IRQ_MASK                      BIT(21)
+#define ACP_SDW1_IRQ_MASK                      BIT(2)
+#define SDW_ACPI_ADDR_ACP63                    5
 #define ACP_DEFAULT_SRAM_LENGTH                        0x00080000
 #define ACP_SRAM_PAGE_COUNT                    128
+#define ACP6X_SDW_MAX_MANAGER_COUNT            2
 
 enum clock_source {
        ACP_CLOCK_96M = 0,
@@ -184,13 +192,19 @@ struct sof_amd_acp_desc {
        unsigned int host_bridge_id;
        u32 pgfsm_base;
        u32 ext_intr_enb;
+       u32 ext_intr_cntl;
        u32 ext_intr_stat;
+       u32 ext_intr_stat1;
        u32 dsp_intr_base;
        u32 sram_pte_offset;
        u32 hw_semaphore_offset;
        u32 acp_clkmux_sel;
        u32 fusion_dsp_offset;
        u32 probe_reg_offset;
+       u32 reg_start_addr;
+       u32 reg_end_addr;
+       u32 sdw_max_link_count;
+       u64 sdw_acpi_dev_addr;
 };
 
 /* Common device data struct for ACP devices */
@@ -199,6 +213,12 @@ struct acp_dev_data {
        const struct firmware *fw_dbin;
        /* DMIC device */
        struct platform_device *dmic_dev;
+       /* mutex lock to protect ACP common registers access */
+       struct mutex acp_lock;
+       /* ACPI information stored between scan and probe steps */
+       struct sdw_amd_acpi_info info;
+       /* sdw context allocated by SoundWire driver */
+       struct sdw_amd_ctx *sdw;
        unsigned int fw_bin_size;
        unsigned int fw_data_bin_size;
        unsigned int fw_sram_data_bin_size;
@@ -207,6 +227,9 @@ struct acp_dev_data {
        const char *fw_sram_data_bin;
        u32 fw_bin_page_count;
        u32 fw_data_bin_page_count;
+       u32 addr;
+       u32 reg_range;
+       u32 blk_type;
        dma_addr_t sha_dma_addr;
        u8 *bin_buf;
        dma_addr_t dma_addr;
@@ -222,6 +245,7 @@ struct acp_dev_data {
        bool enable_fw_debug;
        bool is_dram_in_use;
        bool is_sram_in_use;
+       bool sdw_en_stat;
 };
 
 void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
index bceb94ac80a9d0a6a1c2e38255c39e58cf7c3344..eeaa12cceb23311312d5104ae4452c0e745fb22a 100644 (file)
@@ -31,12 +31,19 @@ static const struct sof_amd_acp_desc acp63_chip_info = {
        .rev            = 6,
        .host_bridge_id = HOST_BRIDGE_ACP63,
        .pgfsm_base     = ACP6X_PGFSM_BASE,
+       .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB,
+       .ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL,
        .ext_intr_stat  = ACP6X_EXT_INTR_STAT,
+       .ext_intr_stat1 = ACP6X_EXT_INTR_STAT1,
        .dsp_intr_base  = ACP6X_DSP_SW_INTR_BASE,
        .sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
        .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
        .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
        .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0,
+       .sdw_max_link_count = ACP6X_SDW_MAX_MANAGER_COUNT,
+       .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP63,
+       .reg_start_addr = ACP6x_REG_START,
+       .reg_end_addr = ACP6x_REG_END,
 };
 
 static const struct sof_dev_desc acp63_desc = {
index 425b023b03b419957eaf01f8e15930fe6c7e0dc7..9b00ede2a486a2ff2d619ab714ed2c1665eb3463 100644 (file)
@@ -679,6 +679,16 @@ int snd_sof_device_remove(struct device *dev)
         */
        snd_sof_machine_unregister(sdev, pdata);
 
+       /*
+        * Balance the runtime pm usage count in case we are faced with an
+        * exception and we forcably prevented D3 power state to preserve
+        * context
+        */
+       if (sdev->d3_prevented) {
+               sdev->d3_prevented = false;
+               pm_runtime_put_noidle(sdev->dev);
+       }
+
        if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
                sof_fw_trace_free(sdev);
                ret = snd_sof_dsp_power_down_notify(sdev);
index d547318e0d32f43feb075d18bcf4d7feba452862..7c8aafca8fdef8eb65211dcd8cbead13077a6068 100644 (file)
@@ -433,13 +433,15 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
 
 void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg)
 {
-       if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
-           sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) {
+       if ((IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
+           sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) && !sdev->d3_prevented) {
                /* should we prevent DSP entering D3 ? */
                if (!sdev->ipc_dump_printed)
                        dev_info(sdev->dev,
                                 "Attempting to prevent DSP from entering D3 state to preserve context\n");
-               pm_runtime_get_if_in_use(sdev->dev);
+
+               if (pm_runtime_get_if_in_use(sdev->dev) == 1)
+                       sdev->d3_prevented = true;
        }
 
        /* dump vital information to the logs */
index 138a1ca2c4a855fecccfc5677cb30e0591f6e643..b56b14232444c7711029f3dbe3c67b7f07ef8a36 100644 (file)
@@ -89,6 +89,12 @@ static int sof_test_topology_file(struct device *dev,
        return ret;
 }
 
+static bool sof_platform_uses_generic_loader(struct snd_sof_dev *sdev)
+{
+       return (sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_raw ||
+               sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_memcpy);
+}
+
 static int
 sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev,
                              enum sof_ipc_type ipc_type,
@@ -130,7 +136,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev,
         * For default path and firmware name do a verification before
         * continuing further.
         */
-       if (base_profile->fw_path || base_profile->fw_name) {
+       if ((base_profile->fw_path || base_profile->fw_name) &&
+           sof_platform_uses_generic_loader(sdev)) {
                ret = sof_test_firmware_file(dev, out_profile, &ipc_type);
                if (ret)
                        return ret;
@@ -181,7 +188,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev,
        out_profile->ipc_type = ipc_type;
 
        /* Test only default firmware file */
-       if (!base_profile->fw_path && !base_profile->fw_name)
+       if ((!base_profile->fw_path && !base_profile->fw_name) &&
+           sof_platform_uses_generic_loader(sdev))
                ret = sof_test_firmware_file(dev, out_profile, NULL);
 
        if (!ret)
@@ -267,7 +275,11 @@ static void sof_print_profile_info(struct snd_sof_dev *sdev,
 
        dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type);
 
-       dev_info(dev, " Firmware file:     %s/%s\n", profile->fw_path, profile->fw_name);
+       /* The firmware path is only valid when generic loader is used */
+       if (sof_platform_uses_generic_loader(sdev))
+               dev_info(dev, " Firmware file:     %s/%s\n",
+                        profile->fw_path, profile->fw_name);
+
        if (profile->fw_lib_path)
                dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path);
        dev_info(dev, " Topology file:     %s/%s\n", profile->tplg_path, profile->tplg_name);
index d777e70250efc006739e1edbf20a4f5fd344ee88..07f51489d6c9d298053a177afad91e3290955ccd 100644 (file)
@@ -607,7 +607,22 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = {
                        SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
 };
 
+static struct snd_sof_of_mach sof_imx8_machs[] = {
+       {
+               .compatible = "fsl,imx8qxp-mek",
+               .sof_tplg_filename = "sof-imx8-wm8960.tplg",
+               .drv_name = "asoc-audio-graph-card2",
+       },
+       {
+               .compatible = "fsl,imx8qm-mek",
+               .sof_tplg_filename = "sof-imx8-wm8960.tplg",
+               .drv_name = "asoc-audio-graph-card2",
+       },
+       {}
+};
+
 static struct sof_dev_desc sof_of_imx8qxp_desc = {
+       .of_machines    = sof_imx8_machs,
        .ipc_supported_mask     = BIT(SOF_IPC_TYPE_3),
        .ipc_default            = SOF_IPC_TYPE_3,
        .default_fw_path = {
@@ -624,6 +639,7 @@ static struct sof_dev_desc sof_of_imx8qxp_desc = {
 };
 
 static struct sof_dev_desc sof_of_imx8qm_desc = {
+       .of_machines    = sof_imx8_machs,
        .ipc_supported_mask     = BIT(SOF_IPC_TYPE_3),
        .ipc_default            = SOF_IPC_TYPE_3,
        .default_fw_path = {
index 1b976fa500aa2725aca8373117e340c5354adec4..222cd1467da67ea1635d6dc8de0550d6ce762477 100644 (file)
@@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = {
                SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 };
 
+static struct snd_sof_of_mach sof_imx8mp_machs[] = {
+       {
+               .compatible = "fsl,imx8mp-evk",
+               .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
+               .drv_name = "asoc-audio-graph-card2",
+       },
+       {}
+};
+
 static struct sof_dev_desc sof_of_imx8mp_desc = {
+       .of_machines    = sof_imx8mp_machs,
        .ipc_supported_mask     = BIT(SOF_IPC_TYPE_3),
        .ipc_default            = SOF_IPC_TYPE_3,
        .default_fw_path = {
index 2badca75782b28aa4fb0ead18a03b22c233cc96c..7b527ffde488f9a1deecc1137ecf9f21c7c546b5 100644 (file)
@@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = {
        .set_power_state        = imx8ulp_dsp_set_power_state,
 };
 
+static struct snd_sof_of_mach sof_imx8ulp_machs[] = {
+       {
+               .compatible = "fsl,imx8ulp-evk",
+               .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
+               .drv_name = "asoc-audio-graph-card2",
+       },
+       {}
+};
+
 static struct sof_dev_desc sof_of_imx8ulp_desc = {
+       .of_machines = sof_imx8ulp_machs,
        .ipc_supported_mask     = BIT(SOF_IPC_TYPE_3),
        .ipc_default            = SOF_IPC_TYPE_3,
        .default_fw_path = {
index 26105d8f1bdc717e54e205a2283aa8fc43d3cd42..2b385cddc385c5bd59e11acfe8e6bda45704fdd1 100644 (file)
@@ -83,6 +83,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
        /* DAI drivers */
        .drv            = skl_dai,
        .num_drv        = SOF_SKL_NUM_DAIS,
+       .is_chain_dma_supported = hda_is_chain_dma_supported,
 
        /* PM */
        .suspend                = hda_dsp_suspend,
index 55ce75db23e52001a0ae3753eaa6549d2fa0e73e..c50ca9e72d37385816ddb3cd6ef7456ed50a58e9 100644 (file)
@@ -522,6 +522,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
        .get_hlink = hda_get_hlink,
 };
 
+static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
+       .get_hext_stream = hda_get_hext_stream,
+       .assign_hext_stream = hda_assign_hext_stream,
+       .release_hext_stream = hda_release_hext_stream,
+       .setup_hext_stream = hda_setup_hext_stream,
+       .reset_hext_stream = hda_reset_hext_stream,
+       .trigger = hda_trigger,
+       .calc_stream_format = generic_calc_stream_format,
+       .get_hlink = sdw_get_hlink,
+};
+
 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
                                 struct snd_pcm_substream *substream, int cmd)
 {
@@ -596,6 +607,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
        .get_hlink = hda_get_hlink,
 };
 
+static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
+       .get_hext_stream = hda_dspless_get_hext_stream,
+       .setup_hext_stream = hda_dspless_setup_hext_stream,
+       .calc_stream_format = generic_calc_stream_format,
+       .get_hlink = sdw_get_hlink,
+};
+
 #endif
 
 const struct hda_dai_widget_dma_ops *
@@ -603,12 +621,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
 {
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
        struct snd_sof_dai *sdai;
+       const struct sof_intel_dsp_desc *chip;
 
-       if (sdev->dspless_mode_selected)
-               return &hda_dspless_dma_ops;
-
+       chip = get_chip_info(sdev->pdata);
        sdai = swidget->private;
 
+       if (sdev->dspless_mode_selected) {
+               switch (sdai->type) {
+               case SOF_DAI_INTEL_HDA:
+                       return &hda_dspless_dma_ops;
+               case SOF_DAI_INTEL_ALH:
+                       if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+                               return NULL;
+                       return &sdw_dspless_dma_ops;
+               default:
+                       return NULL;
+               }
+       }
+
        switch (sdev->pdata->ipc_type) {
        case SOF_IPC_TYPE_3:
        {
@@ -620,22 +650,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
        }
        case SOF_IPC_TYPE_4:
        {
-               struct sof_ipc4_copier *ipc4_copier = sdai->private;
-               const struct sof_intel_dsp_desc *chip;
-
-               chip = get_chip_info(sdev->pdata);
+               struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+               struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
 
-               switch (ipc4_copier->dai_type) {
+               switch (sdai->type) {
                case SOF_DAI_INTEL_HDA:
-               {
-                       struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
-                       struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
                        if (pipeline->use_chain_dma)
                                return &hda_ipc4_chain_dma_ops;
 
                        return &hda_ipc4_dma_ops;
-               }
                case SOF_DAI_INTEL_SSP:
                        if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
                                return NULL;
@@ -647,6 +670,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
                case SOF_DAI_INTEL_ALH:
                        if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
                                return NULL;
+                       if (pipeline->use_chain_dma)
+                               return &sdw_ipc4_chain_dma_ops;
                        return &sdw_ipc4_dma_ops;
 
                default:
index f4cbc0ad5de3b427583964f8cd49c41acc53e581..c1682bcdb5a6636091d92319162a206cfe08723c 100644 (file)
@@ -83,12 +83,13 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai
 
        sdev = widget_to_sdev(w);
 
-       /*
-        * The swidget parameter of hda_select_dai_widget_ops() is ignored in
-        * case of DSPless mode
-        */
+       if (!swidget) {
+               dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
+               return NULL;
+       }
+
        if (sdev->dspless_mode_selected)
-               return hda_select_dai_widget_ops(sdev, NULL);
+               return hda_select_dai_widget_ops(sdev, swidget);
 
        sdai = swidget->private;
 
@@ -368,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       /* get stream_id */
        sdev = widget_to_sdev(w);
+       if (sdev->dspless_mode_selected)
+               goto skip_tlv;
+
+       /* get stream_id */
        hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
 
        if (!hext_stream) {
@@ -402,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
        dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
        dma_config->dma_priv_config_size = 0;
 
+skip_tlv:
        return 0;
 }
 
index 2445ae7f6b2e924fa0f192b2322dbd77eeee1b29..31ffa1a8f2ac04ddd5c31aadec5400c52757dd19 100644 (file)
@@ -748,6 +748,7 @@ skip_dsp:
 
 static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
 {
+       const struct sof_intel_dsp_desc *chip;
        int ret;
 
        /* display codec must be powered before link reset */
@@ -780,6 +781,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
                hda_dsp_ctrl_ppcap_int_enable(sdev, true);
        }
 
+       chip = get_chip_info(sdev->pdata);
+       if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0)
+               hda_sdw_int_enable(sdev, true);
+
 cleanup:
        /* display codec can powered off after controller init */
        hda_codec_i915_display_power(sdev, false);
index f2ebadbbcc10e30492445eae41d470a227cd1a91..b387b1a69d7ea3ceaed9fe814b174d9040e3eae1 100644 (file)
@@ -21,6 +21,7 @@
 #include <trace/events/sof_intel.h>
 #include "../ops.h"
 #include "../sof-audio.h"
+#include "../ipc4-priv.h"
 #include "hda.h"
 
 #define HDA_LTRP_GB_VALUE_US   95
@@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
        /* store total stream count (playback + capture) from GCAP */
        sof_hda->stream_max = num_total;
 
+       /* store stream count from GCAP required for CHAIN_DMA */
+       if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+               struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+               ipc4_data->num_playback_streams = num_playback;
+               ipc4_data->num_capture_streams = num_capture;
+       }
+
        return 0;
 }
 
index fe4ae349dad51fb6b23bd0f485bb723029b1e7b7..7fe72b06545147aee2bde593de7c7a5129de4a10 100644 (file)
 #define EXCEPT_MAX_HDR_SIZE    0x400
 #define HDA_EXT_ROM_STATUS_SIZE 8
 
-static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
+static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask)
 {
        const struct sof_intel_dsp_desc *chip;
-       u32 interface_mask[2] = { 0 };
 
        chip = get_chip_info(sdev->pdata);
        switch (chip->hw_ip_version) {
        case SOF_INTEL_TANGIER:
        case SOF_INTEL_BAYTRAIL:
        case SOF_INTEL_BROADWELL:
-               interface_mask[0] =  BIT(SOF_DAI_INTEL_SSP);
+               interface_mask[SOF_DAI_DSP_ACCESS] =  BIT(SOF_DAI_INTEL_SSP);
                break;
        case SOF_INTEL_CAVS_1_5:
        case SOF_INTEL_CAVS_1_5_PLUS:
-               interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
-                                   BIT(SOF_DAI_INTEL_HDA);
-               interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+               interface_mask[SOF_DAI_DSP_ACCESS] =
+                       BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA);
+               interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
                break;
        case SOF_INTEL_CAVS_1_8:
        case SOF_INTEL_CAVS_2_0:
        case SOF_INTEL_CAVS_2_5:
        case SOF_INTEL_ACE_1_0:
-               interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
-                                   BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
-               interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+               interface_mask[SOF_DAI_DSP_ACCESS] =
+                       BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+                       BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+               interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
                break;
        case SOF_INTEL_ACE_2_0:
-               interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
-                                   BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
-               interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */
+               interface_mask[SOF_DAI_DSP_ACCESS] =
+                       BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+                       BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+                /* all interfaces accessible without DSP */
+               interface_mask[SOF_DAI_HOST_ACCESS] =
+                       interface_mask[SOF_DAI_DSP_ACCESS];
                break;
        default:
                break;
        }
+}
+
+static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
+{
+       u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+
+       hda_get_interfaces(sdev, interface_mask);
 
        return interface_mask[sdev->dspless_mode_selected];
 }
 
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
+{
+       u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+       const struct sof_intel_dsp_desc *chip;
+
+       if (sdev->dspless_mode_selected)
+               return false;
+
+       hda_get_interfaces(sdev, interface_mask);
+
+       if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type)))
+               return false;
+
+       if (dai_type == SOF_DAI_INTEL_HDA)
+               return true;
+
+       switch (dai_type) {
+       case SOF_DAI_INTEL_SSP:
+       case SOF_DAI_INTEL_DMIC:
+       case SOF_DAI_INTEL_ALH:
+               chip = get_chip_info(sdev->pdata);
+               if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+                       return false;
+               return true;
+       default:
+               return false;
+       }
+}
+
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
 
 /*
@@ -1192,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
 {
        struct pci_dev *pci = to_pci_dev(sdev->dev);
        struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+       const struct sof_intel_dsp_desc *chip;
        int ret = 0;
 
        hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
@@ -1305,12 +1345,28 @@ skip_dsp_setup:
                INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
        }
 
+       chip = get_chip_info(sdev->pdata);
+       if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+               ret = hda_sdw_startup(sdev);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "could not startup SoundWire links\n");
+                       goto disable_pp_cap;
+               }
+
+               hda_sdw_int_enable(sdev, true);
+       }
+
        init_waitqueue_head(&hdev->waitq);
 
        hdev->nhlt = intel_nhlt_init(sdev->dev);
 
        return 0;
 
+disable_pp_cap:
+       if (!sdev->dspless_mode_selected) {
+               hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+               hda_dsp_ctrl_ppcap_enable(sdev, false);
+       }
 free_ipc_irq:
        free_irq(sdev->ipc_irq, sdev);
 free_irq_vector:
index 1592e27bc14daf48002f46c42a8d22098da02068..b36eb7c7891335a3038d5e1402d6f73ede754b81 100644 (file)
@@ -573,6 +573,11 @@ struct sof_intel_hda_stream {
 
 #define SOF_STREAM_SD_OFFSET_CRST 0x1
 
+/*
+ * DAI support
+ */
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type);
+
 /*
  * DSP Core services.
  */
index 30712ea05a7a54086c798aa9e737cf0f7283ebbb..7ae017a00184e52371052c188d75f13fbfc053df 100644 (file)
@@ -77,6 +77,19 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
        return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev));
 }
 
+static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+       if (sdev->first_boot) {
+               struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+
+               /* Check if IMR boot is usable */
+               if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT))
+                       hda->imrboot_supported = true;
+       }
+
+       return 0;
+}
+
 int sof_lnl_ops_init(struct snd_sof_dev *sdev)
 {
        struct sof_ipc4_fw_data *ipc4_data;
@@ -85,7 +98,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
        memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
 
        /* probe */
-       sof_lnl_ops.probe = lnl_hda_dsp_probe;
+       if (!sdev->dspless_mode_selected)
+               sof_lnl_ops.probe = lnl_hda_dsp_probe;
 
        /* shutdown */
        sof_lnl_ops.shutdown = hda_dsp_shutdown;
@@ -106,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
 
        /* pre/post fw run */
        sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run;
-       sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run;
+       sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run;
 
        /* parse platform specific extended manifest */
        sof_lnl_ops.parse_platform_ext_manifest = NULL;
@@ -115,8 +129,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
        /* TODO: add core_get and core_put */
 
        /* PM */
-       sof_lnl_ops.resume                      = lnl_hda_dsp_resume;
-       sof_lnl_ops.runtime_resume              = lnl_hda_dsp_runtime_resume;
+       if (!sdev->dspless_mode_selected) {
+               sof_lnl_ops.resume = lnl_hda_dsp_resume;
+               sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume;
+       }
 
        sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
 
index 28218766d2114f17d16d31667146fdd3a8796f9f..6e3ef067211068a615d1f9d7f2ff2155922b0000 100644 (file)
@@ -148,6 +148,8 @@ static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev)
 
        head = (struct sof_ext_man_header *)fw->data;
        remaining = head->full_size - head->header_size;
+       if (remaining < 0 || remaining > sdev->basefw.fw->size)
+               return -EINVAL;
        ext_man_size = ipc3_fw_ext_man_size(sdev, fw);
 
        /* Assert firmware starts with extended manifest */
index 330f04bcd75d2b7bc63544e718cb89ad12e64c7b..35769dd7905ebe6f66492f650a72d8624712f28a 100644 (file)
@@ -395,6 +395,31 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
                dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n",
                        channels->min, channels->max);
                break;
+       case SOF_DAI_AMD_SDW:
+               /* change the default trigger sequence as per HW implementation */
+               for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
+                       struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+                       fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
+                                       SND_SOC_DPCM_TRIGGER_POST;
+               }
+
+               for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
+                       struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+                       fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
+                                       SND_SOC_DPCM_TRIGGER_POST;
+               }
+               rate->min = private->dai_config->acp_sdw.rate;
+               rate->max = private->dai_config->acp_sdw.rate;
+               channels->min = private->dai_config->acp_sdw.channels;
+               channels->max = private->dai_config->acp_sdw.channels;
+
+               dev_dbg(component->dev,
+                       "AMD_SDW rate_min: %d rate_max: %d\n", rate->min, rate->max);
+               dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n",
+                       channels->min, channels->max);
+               break;
        default:
                dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
                break;
index d47698f4be2deb6f5bb943e2de2611923c45c387..ab7f46a162da7664623e3ab6e28d223372a9204d 100644 (file)
@@ -298,6 +298,14 @@ static const struct sof_topology_token micfil_pdm_tokens[] = {
                offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)},
 };
 
+/* ACP_SDW */
+static const struct sof_topology_token acp_sdw_tokens[] = {
+       {SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc_dai_acp_sdw_params, rate)},
+       {SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc_dai_acp_sdw_params, channels)},
+};
+
 /* Core tokens */
 static const struct sof_topology_token core_tokens[] = {
        {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
@@ -336,6 +344,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {
        [SOF_ACPI2S_TOKENS]   = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)},
        [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens",
                micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)},
+       [SOF_ACP_SDW_TOKENS]   = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)},
 };
 
 /**
@@ -1315,6 +1324,34 @@ static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_
        return 0;
 }
 
+static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+                                struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+       struct sof_dai_private_data *private = dai->private;
+       u32 size = sizeof(*config);
+       int ret;
+
+       /* parse the required set of ACP_SDW tokens based on num_hw_cfgs */
+       ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples,
+                                   slink->num_tuples, size, slink->num_hw_configs);
+       if (ret < 0)
+               return ret;
+
+       /* init IPC */
+       config->hdr.size = size;
+       dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n",
+               config->acp_sdw.rate, config->acp_sdw.channels);
+
+       /* set config for all DAI's with name matching the link name */
+       dai->number_configs = 1;
+       dai->current_config = 0;
+       private->dai_config = kmemdup(config, size, GFP_KERNEL);
+       if (!private->dai_config)
+               return -ENOMEM;
+
+       return 0;
+}
+
 static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
                             struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
 {
@@ -1629,6 +1666,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget)
                case SOF_DAI_MEDIATEK_AFE:
                        ret = sof_link_afe_load(scomp, slink, config, dai);
                        break;
+               case SOF_DAI_AMD_SDW:
+                       ret = sof_link_acp_sdw_load(scomp, slink, config, dai);
+                       break;
                default:
                        break;
                }
index 07eb5c6d4adf3246877e4881c1927a6a4f8c39ee..0f332c8cdbe6afe6fc9449b48194ebb749db5d3d 100644 (file)
@@ -231,9 +231,11 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
  */
 
 static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
+                                     int direction,
                                      struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
                                      int state, int cmd)
 {
+       struct sof_ipc4_fw_data *ipc4_data = sdev->private;
        bool allocate, enable, set_fifo_size;
        struct sof_ipc4_msg msg = {{ 0 }};
        int i;
@@ -294,6 +296,20 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
                        msg.extension |= pipeline->msg.extension;
        }
 
+       if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+               /*
+                * For ChainDMA the DMA ids are unique with the following mapping:
+                * playback:  0 - (num_playback_streams - 1)
+                * capture:   num_playback_streams - (num_playback_streams +
+                *                                    num_capture_streams - 1)
+                *
+                * Add the num_playback_streams offset to the DMA ids stored in
+                * msg.primary in case capture
+                */
+               msg.primary +=  SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(ipc4_data->num_playback_streams);
+               msg.primary +=  SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(ipc4_data->num_playback_streams);
+       }
+
        if (allocate)
                msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
 
@@ -340,7 +356,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
         * trigger function that handles the rest for the substream.
         */
        if (pipeline->use_chain_dma)
-               return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd);
+               return sof_ipc4_chain_dma_trigger(sdev, substream->stream,
+                                                 pipeline_list, state, cmd);
 
        /* allocate memory for the pipeline data */
        trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids,
index 1d39836d5efacc880163f3c8449d24bde87c3434..f3b908b093f9562ddeb6932b4f1743a27c2b3c09 100644 (file)
@@ -66,6 +66,8 @@ struct sof_ipc4_fw_library {
  * @nhlt: NHLT table either from the BIOS or the topology manifest
  * @mtrace_type: mtrace type supported on the booted platform
  * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply
+ * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset
+ * @num_capture_streams: max number of capture DMAs
  * @max_num_pipelines: max number of pipelines
  * @max_libs_count: Maximum number of libraries support by the FW including the
  *                 base firmware
@@ -79,6 +81,8 @@ struct sof_ipc4_fw_data {
        void *nhlt;
        enum sof_ipc4_mtrace_type mtrace_type;
        u32 mtrace_log_bytes;
+       int num_playback_streams;
+       int num_capture_streams;
        int max_num_pipelines;
        u32 max_libs_count;
        bool fw_context_save;
index f779156fe0e600965b493e5da9c92bf43a75a917..da4a83afb87a8a2c4163f34a8d6e9993b92bde77 100644 (file)
@@ -509,6 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
 {
        struct sof_ipc4_available_audio_format *available_fmt;
        struct snd_soc_component *scomp = swidget->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_sof_dai *dai = swidget->private;
        struct sof_ipc4_copier *ipc4_copier;
        struct snd_sof_widget *pipe_widget;
@@ -548,14 +549,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
        dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
                node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);
 
+       dai->type = ipc4_copier->dai_type;
        ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
 
        pipe_widget = swidget->spipe->pipe_widget;
        pipeline = pipe_widget->private;
-       if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
-               dev_err(scomp->dev,
-                       "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
-                       ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);
+
+       if (pipeline->use_chain_dma &&
+           !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) {
+               dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n",
+                       ipc4_copier->dai_type);
                ret = -ENODEV;
                goto free_available_fmt;
        }
@@ -598,7 +601,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
                }
 
                ipc4_copier->copier_config = (uint32_t *)blob;
-               ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2;
+               /* set data.gtw_cfg.config_length based on device_count */
+               ipc4_copier->data.gtw_cfg.config_length = (sizeof(blob->gw_attr) +
+                                                          sizeof(blob->alh_cfg.device_count) +
+                                                          sizeof(*blob->alh_cfg.mapping) *
+                                                          blob->alh_cfg.device_count) >> 2;
                break;
        }
        case SOF_DAI_INTEL_SSP:
@@ -2796,13 +2803,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
        if (!data)
                return 0;
 
+       if (pipeline->use_chain_dma) {
+               pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
+               pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
+               return 0;
+       }
+
        switch (ipc4_copier->dai_type) {
        case SOF_DAI_INTEL_HDA:
-               if (pipeline->use_chain_dma) {
-                       pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
-                       pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
-                       break;
-               }
                gtw_attr = ipc4_copier->gtw_attr;
                gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
                fallthrough;
index 6538d9f4fe96e02ca2fc45b55deb8738f505787e..6cf21e829e07272ccf4c7005f74f9ae61403d39b 100644 (file)
@@ -567,6 +567,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach,
                sof_ops(sdev)->set_mach_params(mach, sdev);
 }
 
+static inline bool
+snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
+{
+       if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported)
+               return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type);
+
+       return false;
+}
+
 /**
  * snd_sof_dsp_register_poll_timeout - Periodically poll an address
  * until a condition is met or a timeout occurs
index 9163975c9c3fb905d0df5064879a1e3237d9155c..e693dcb475e4d6a28273d920b36b706c21df6dd9 100644 (file)
@@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
 {
        const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
        struct snd_sof_pipeline *spipe = swidget->spipe;
-       struct snd_sof_widget *pipe_widget;
        int err = 0;
        int ret;
 
@@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
        if (--swidget->use_count)
                return 0;
 
-       pipe_widget = swidget->spipe->pipe_widget;
-
        /* reset route setup status for all routes that contain this widget */
        sof_reset_route_setup_status(sdev, swidget);
 
@@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
         * free the scheduler widget (same as pipe_widget) associated with the current swidget.
         * skip for static pipelines
         */
-       if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
-               ret = sof_widget_free_unlocked(sdev, pipe_widget);
+       if (swidget->spipe && swidget->dynamic_pipeline_widget &&
+           swidget->id != snd_soc_dapm_scheduler) {
+               ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
                if (ret < 0 && !err)
                        err = ret;
        }
index 8874ee5f557fb3892d1571730a6ffc4e12131246..9ea2ac5adac79ee322f82060b908ce529cd9c43b 100644 (file)
@@ -276,6 +276,7 @@ enum sof_tokens {
        SOF_ACPDMIC_TOKENS,
        SOF_ACPI2S_TOKENS,
        SOF_MICFIL_TOKENS,
+       SOF_ACP_SDW_TOKENS,
 
        /* this should be the last */
        SOF_TOKEN_COUNT,
@@ -513,6 +514,7 @@ struct snd_sof_route {
 struct snd_sof_dai {
        struct snd_soc_component *scomp;
        const char *name;
+       u32 type;
 
        int number_configs;
        int current_config;
index 6d7897bf96075e05dae0f708155455bf10a38b44..d453a4ce3b219d601813310c22cbf11029a08a77 100644 (file)
@@ -157,6 +157,13 @@ struct sof_firmware {
        u32 payload_offset;
 };
 
+enum sof_dai_access {
+       SOF_DAI_DSP_ACCESS,     /* access from DSP only */
+       SOF_DAI_HOST_ACCESS,    /* access from host only */
+
+       SOF_DAI_ACCESS_NUM
+};
+
 /*
  * SOF DSP HW abstraction operations.
  * Used to abstract DSP HW architecture and any IO busses between host CPU
@@ -338,6 +345,8 @@ struct snd_sof_dsp_ops {
        struct snd_soc_dai_driver *drv;
        int num_drv;
 
+       bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */
+
        /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
        u32 hw_info;
 
@@ -595,6 +604,7 @@ struct snd_sof_dev {
        struct list_head dfsentry_list;
        bool dbg_dump_printed;
        bool ipc_dump_printed;
+       bool d3_prevented; /* runtime pm use count incremented to prevent context lost */
 
        /* firmware loader */
        struct sof_ipc_fw_ready fw_ready;
index 617a225fff24b6f63d6347d3e1ff43b4cb701090..bcdb499c96a02261a155201c8a604667d29aeb1e 100644 (file)
@@ -297,6 +297,7 @@ static const struct sof_dai_types sof_dais[] = {
        {"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL},
        {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL},
        {"MICFIL", SOF_DAI_IMX_MICFIL},
+       {"ACP_SDW", SOF_DAI_AMD_SDW},
 
 };
 
@@ -1968,6 +1969,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
                token_id = SOF_MICFIL_TOKENS;
                num_tuples += token_list[SOF_MICFIL_TOKENS].count;
                break;
+       case SOF_DAI_AMD_SDW:
+               token_id = SOF_ACP_SDW_TOKENS;
+               num_tuples += token_list[SOF_ACP_SDW_TOKENS].count;
+               break;
        default:
                break;
        }
@@ -2349,25 +2354,43 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
                                    struct snd_soc_tplg_dapm_widget *tw)
 {
        if (WIDGET_IS_DAI(w->id)) {
+               static const struct sof_topology_token dai_tokens[] = {
+                       {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}};
                struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+               struct snd_soc_tplg_private *priv = &tw->priv;
                struct snd_sof_widget *swidget;
-               struct snd_sof_dai dai;
+               struct snd_sof_dai *sdai;
                int ret;
 
                swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
                if (!swidget)
                        return -ENOMEM;
 
-               memset(&dai, 0, sizeof(dai));
+               sdai = kzalloc(sizeof(*sdai), GFP_KERNEL);
+               if (!sdai) {
+                       kfree(swidget);
+                       return -ENOMEM;
+               }
+
+               ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens),
+                                      priv->array, le32_to_cpu(priv->size));
+               if (ret < 0) {
+                       dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name);
+                       kfree(swidget);
+                       kfree(sdai);
+                       return ret;
+               }
 
-               ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+               ret = sof_connect_dai_widget(scomp, w, tw, sdai);
                if (ret) {
                        kfree(swidget);
+                       kfree(sdai);
                        return ret;
                }
 
                swidget->scomp = scomp;
                swidget->widget = w;
+               swidget->private = sdai;
                mutex_init(&swidget->setup_mutex);
                w->dobj.private = swidget;
                list_add(&swidget->list, &sdev->widget_list);
@@ -2391,6 +2414,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
 
                /* remove and free swidget object */
                list_del(&swidget->list);
+               kfree(swidget->private);
                kfree(swidget);
        }
 
index b4b158dc736eb4b1572c05742a256aa18dcda8ff..d9d1e021f5b2eef5e95afca7c8ac5c7f44201226 100644 (file)
@@ -649,7 +649,7 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
         * Link 2: McASP10 <- pcm3168a_1 ADC
         */
        comp_count = 6;
-       compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
+       compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent),
                                GFP_KERNEL);
        if (!compnent) {
                ret = -ENOMEM;
@@ -763,7 +763,7 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
         *                 \ pcm3168a_b ADC
         */
        comp_count = 8;
-       compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
+       compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent),
                                GFP_KERNEL);
        if (!compnent) {
                ret = -ENOMEM;
index 29bff9e6337b621ee22a6a1032161e62f0a7871f..4513b527ab97050c0fff55deaad69adffdfed6df 100644 (file)
@@ -379,7 +379,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
        card->num_links = 1;
        card->dev = dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(dev, card);
        if (ret) {
                dev_err(dev, "snd_soc_register_card failed (%d)\n", ret);
                return ret;
@@ -393,19 +393,11 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static void omap_hdmi_audio_remove(struct platform_device *pdev)
-{
-       struct hdmi_audio_data *ad = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(ad->card);
-}
-
 static struct platform_driver hdmi_audio_driver = {
        .driver = {
                .name = DRV_NAME,
        },
        .probe = omap_hdmi_audio_probe,
-       .remove_new = omap_hdmi_audio_remove,
 };
 
 module_platform_driver(hdmi_audio_driver);
index 1e8765d75d8f8dcac08ad2a5443695449293b3bb..5648d744aa79ff4a92e0bfe5b50fe60d704305ca 100644 (file)
@@ -1076,8 +1076,6 @@ out:
        snd_card_free(card);
 }
 
-#ifdef CONFIG_PM_SLEEP
-
 static int snd_at73c213_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -1109,18 +1107,13 @@ static int snd_at73c213_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend,
+static DEFINE_SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend,
                snd_at73c213_resume);
-#define AT73C213_PM_OPS (&at73c213_pm_ops)
-
-#else
-#define AT73C213_PM_OPS NULL
-#endif
 
 static struct spi_driver at73c213_driver = {
        .driver         = {
                .name   = "at73c213",
-               .pm     = AT73C213_PM_OPS,
+               .pm     = &at73c213_pm_ops,
        },
        .probe          = snd_at73c213_probe,
        .remove         = snd_at73c213_remove,
index 0006c3ddb51d608bcef2b61d42710003fb88cb2a..a82af937485263d402add34b268cc02137288215 100644 (file)
@@ -85,7 +85,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
                return -EINVAL;
 
        emu->card = card;
-       emu->name = kstrdup(name, GFP_KERNEL);
+       emu->name = kstrdup_const(name, GFP_KERNEL);
        emu->voices = kcalloc(emu->max_voices, sizeof(struct snd_emux_voice),
                              GFP_KERNEL);
        if (emu->name == NULL || emu->voices == NULL)
@@ -140,7 +140,7 @@ int snd_emux_free(struct snd_emux *emu)
        snd_emux_delete_hwdep(emu);
        snd_sf_free(emu->sflist);
        kfree(emu->voices);
-       kfree(emu->name);
+       kfree_const(emu->name);
        kfree(emu);
        return 0;
 }
index 409fc11646948e40ec6b03be6840c150c04db8b7..81256ab56835ede6fafe4bc325526fa68943291e 100644 (file)
@@ -1652,6 +1652,34 @@ static const struct usb_feature_control_info *get_feature_control_info(int contr
        return NULL;
 }
 
+static int feature_unit_mutevol_ctl_name(struct usb_mixer_interface *mixer,
+                                        struct snd_kcontrol *kctl,
+                                        struct usb_audio_term *iterm,
+                                        struct usb_audio_term *oterm)
+{
+       struct usb_audio_term *aterm, *bterm;
+       bool output_first;
+       int len = 0;
+
+       /*
+        * If the input terminal is USB Streaming, we try getting the name of
+        * the output terminal first in hopes of getting something more
+        * descriptive than "PCM".
+        */
+       output_first = iterm && !(iterm->type >> 16) && (iterm->type & 0xff00) == 0x0100;
+
+       aterm = output_first ? oterm : iterm;
+       bterm = output_first ? iterm : oterm;
+
+       if (aterm)
+               len = get_term_name(mixer->chip, aterm, kctl->id.name,
+                                   sizeof(kctl->id.name), 1);
+       if (!len && bterm)
+               len = get_term_name(mixer->chip, bterm, kctl->id.name,
+                                   sizeof(kctl->id.name), 1);
+       return len;
+}
+
 static void __build_feature_ctl(struct usb_mixer_interface *mixer,
                                const struct usbmix_name_map *imap,
                                unsigned int ctl_mask, int control,
@@ -1733,22 +1761,15 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
        case UAC_FU_MUTE:
        case UAC_FU_VOLUME:
                /*
-                * determine the control name.  the rule is:
-                * - if a name id is given in descriptor, use it.
-                * - if the connected input can be determined, then use the name
-                *   of terminal type.
-                * - if the connected output can be determined, use it.
-                * - otherwise, anonymous name.
+                * Determine the control name:
+                * - If a name id is given in descriptor, use it.
+                * - If input and output terminals are present, try to derive
+                *   the name from either of these.
+                * - Otherwise, make up a name using the feature unit ID.
                 */
                if (!len) {
-                       if (iterm)
-                               len = get_term_name(mixer->chip, iterm,
-                                                   kctl->id.name,
-                                                   sizeof(kctl->id.name), 1);
-                       if (!len && oterm)
-                               len = get_term_name(mixer->chip, oterm,
-                                                   kctl->id.name,
-                                                   sizeof(kctl->id.name), 1);
+                       len = feature_unit_mutevol_ctl_name(mixer, kctl, iterm,
+                                                           oterm);
                        if (!len)
                                snprintf(kctl->id.name, sizeof(kctl->id.name),
                                         "Feature %d", unitid);
index 6de605a601e5f89ff7a9c12b36db81eed6d876c3..bd114be537d7aed32f75b955b5c9bd7db199cc2f 100644 (file)
 
 /* some gui mixers can't handle negative ctl values */
 #define SCARLETT2_VOLUME_BIAS 127
-#define SCARLETT2_GAIN_BIAS 70
+
+/* maximum preamp input gain and value
+ * values are from 0 to 70, preamp gain is from 0 to 69 dB
+ */
+#define SCARLETT2_MAX_GAIN_VALUE 70
+#define SCARLETT2_MAX_GAIN_DB 69
 
 /* mixer range from -80dB to +6dB in 0.5dB steps */
 #define SCARLETT2_MIXER_MIN_DB -80
@@ -284,14 +289,22 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
        "Mute Playback Switch", "Dim Playback Switch"
 };
 
-/* Autogain Status Values */
-enum {
-       SCARLETT2_AUTOGAIN_STATUS_STOPPED,
-       SCARLETT2_AUTOGAIN_STATUS_RUNNING,
-       SCARLETT2_AUTOGAIN_STATUS_FAILED,
-       SCARLETT2_AUTOGAIN_STATUS_CANCELLED,
-       SCARLETT2_AUTOGAIN_STATUS_UNKNOWN,
-       SCARLETT2_AUTOGAIN_STATUS_COUNT
+/* The autogain_status is set based on the autogain_switch and
+ * raw_autogain_status values.
+ *
+ * If autogain_switch is set, autogain_status is set to 0 (Running).
+ * The other status values are from the raw_autogain_status value + 1.
+ */
+static const char *const scarlett2_autogain_status_texts[] = {
+       "Running",
+       "Success",
+       "SuccessDRover",
+       "WarnMinGainLimit",
+       "FailDRunder",
+       "FailMaxGainLimit",
+       "FailClipped",
+       "Cancelled",
+       "Invalid"
 };
 
 /* Power Status Values */
@@ -415,7 +428,7 @@ enum {
        SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
        SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
        SCARLETT2_CONFIG_POWER_EXT,
-       SCARLETT2_CONFIG_POWER_STATUS,
+       SCARLETT2_CONFIG_POWER_LOW,
        SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
        SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
        SCARLETT2_CONFIG_COUNT
@@ -723,8 +736,8 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
                [SCARLETT2_CONFIG_POWER_EXT] = {
                        .offset = 0x168 },
 
-               [SCARLETT2_CONFIG_POWER_STATUS] = {
-                       .offset = 0x66 }
+               [SCARLETT2_CONFIG_POWER_LOW] = {
+                       .offset = 0x16d }
        }
 };
 
@@ -2835,9 +2848,9 @@ static int scarlett2_autogain_is_running(struct scarlett2_data *private)
 {
        int i;
 
+       /* autogain_status[] is 0 if autogain is running */
        for (i = 0; i < private->info->gain_input_count; i++)
-               if (private->autogain_status[i] ==
-                   SCARLETT2_AUTOGAIN_STATUS_RUNNING)
+               if (!private->autogain_status[i])
                        return 1;
 
        return 0;
@@ -2867,25 +2880,25 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
                return err;
 
        /* Translate autogain_switch and raw_autogain_status into
-        * autogain_status
+        * autogain_status.
+        *
+        * When autogain_switch[] is set, the status is the first
+        * element in scarlett2_autogain_status_texts[] (Running). The
+        * subsequent elements correspond to the status value from the
+        * device (raw_autogain_status[]) + 1. The last element is
+        * "Invalid", in case the device reports a status outside the
+        * range of scarlett2_autogain_status_texts[].
         */
        for (i = 0; i < info->gain_input_count; i++)
                if (private->autogain_switch[i])
+                       private->autogain_status[i] = 0;
+               else if (raw_autogain_status[i] <
+                               ARRAY_SIZE(scarlett2_autogain_status_texts) - 1)
                        private->autogain_status[i] =
-                               SCARLETT2_AUTOGAIN_STATUS_RUNNING;
-               else if (raw_autogain_status[i] == 0)
-                       private->autogain_status[i] =
-                               SCARLETT2_AUTOGAIN_STATUS_STOPPED;
-               else if (raw_autogain_status[i] >= 2 &&
-                        raw_autogain_status[i] <= 5)
-                       private->autogain_status[i] =
-                               SCARLETT2_AUTOGAIN_STATUS_FAILED;
-               else if (raw_autogain_status[i] == 6)
-                       private->autogain_status[i] =
-                               SCARLETT2_AUTOGAIN_STATUS_CANCELLED;
+                               raw_autogain_status[i] + 1;
                else
                        private->autogain_status[i] =
-                               SCARLETT2_AUTOGAIN_STATUS_UNKNOWN;
+                               ARRAY_SIZE(scarlett2_autogain_status_texts) - 1;
 
        return 0;
 }
@@ -3111,12 +3124,10 @@ unlock:
 static int scarlett2_autogain_status_ctl_info(
        struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
 {
-       static const char *const values[SCARLETT2_AUTOGAIN_STATUS_COUNT] = {
-               "Stopped", "Running", "Failed", "Cancelled", "Unknown"
-       };
-
        return snd_ctl_enum_info(
-               uinfo, 1, SCARLETT2_AUTOGAIN_STATUS_COUNT, values);
+               uinfo, 1,
+               ARRAY_SIZE(scarlett2_autogain_status_texts),
+               scarlett2_autogain_status_texts);
 }
 
 static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
@@ -3458,7 +3469,7 @@ static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl,
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = elem->channels;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = SCARLETT2_GAIN_BIAS;
+       uinfo->value.integer.max = SCARLETT2_MAX_GAIN_VALUE;
        uinfo->value.integer.step = 1;
 
 unlock:
@@ -3535,7 +3546,7 @@ unlock:
 }
 
 static const DECLARE_TLV_DB_MINMAX(
-       db_scale_scarlett2_gain, -SCARLETT2_GAIN_BIAS * 100, 0
+       db_scale_scarlett2_gain, 0, SCARLETT2_MAX_GAIN_DB * 100
 );
 
 static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
@@ -6294,8 +6305,7 @@ static int scarlett2_update_power_status(struct usb_mixer_interface *mixer)
 {
        struct scarlett2_data *private = mixer->private_data;
        int err;
-       u8 power_ext;
-       u8 power_status;
+       u8 power_ext, power_low;
 
        private->power_status_updated = 0;
 
@@ -6304,12 +6314,12 @@ static int scarlett2_update_power_status(struct usb_mixer_interface *mixer)
        if (err < 0)
                return err;
 
-       err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_STATUS,
-                                      1, &power_status);
+       err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_LOW,
+                                      1, &power_low);
        if (err < 0)
                return err;
 
-       if (power_status > 1)
+       if (power_low)
                private->power_status = SCARLETT2_POWER_STATUS_FAIL;
        else if (power_ext)
                private->power_status = SCARLETT2_POWER_STATUS_EXT;
index 3d4add94e367d63d1329f22ddb78af4264685580..d5409f3879455961182713d24327836219e73457 100644 (file)
@@ -300,9 +300,12 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
        c = 0;
 
        if (bits) {
-               for (; bits && *maps; maps++, bits >>= 1)
+               for (; bits && *maps; maps++, bits >>= 1) {
                        if (bits & 1)
                                chmap->map[c++] = *maps;
+                       if (c == chmap->channels)
+                               break;
+               }
        } else {
                /* If we're missing wChannelConfig, then guess something
                    to make sure the channel map is not skipped entirely */
index 2742bddb887414d7bb97f0efd32325e02b3513f9..a839f8c8b5e6478c9f7b6ec3c8b60e6be4750ecb 100644 (file)
@@ -7,6 +7,7 @@ virtio_snd-objs := \
        virtio_chmap.o \
        virtio_ctl_msg.o \
        virtio_jack.o \
+       virtio_kctl.o \
        virtio_pcm.o \
        virtio_pcm_msg.o \
        virtio_pcm_ops.o
index b158c3cb8e5f5fce75e22306c7707935465bc57f..2da20c6252477cabe4c2dd3adbc29cc422500ee5 100644 (file)
@@ -64,6 +64,9 @@ static void virtsnd_event_dispatch(struct virtio_snd *snd,
        case VIRTIO_SND_EVT_PCM_XRUN:
                virtsnd_pcm_event(snd, event);
                break;
+       case VIRTIO_SND_EVT_CTL_NOTIFY:
+               virtsnd_kctl_event(snd, event);
+               break;
        }
 }
 
@@ -233,6 +236,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd)
        if (rc)
                return rc;
 
+       if (virtio_has_feature(vdev, VIRTIO_SND_F_CTLS)) {
+               rc = virtsnd_kctl_parse_cfg(snd);
+               if (rc)
+                       return rc;
+       }
+
        if (snd->njacks) {
                rc = virtsnd_jack_build_devs(snd);
                if (rc)
@@ -251,6 +260,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd)
                        return rc;
        }
 
+       if (snd->nkctls) {
+               rc = virtsnd_kctl_build_devs(snd);
+               if (rc)
+                       return rc;
+       }
+
        return snd_card_register(snd->card);
 }
 
@@ -417,10 +432,16 @@ static const struct virtio_device_id id_table[] = {
        { 0 },
 };
 
+static unsigned int features[] = {
+       VIRTIO_SND_F_CTLS
+};
+
 static struct virtio_driver virtsnd_driver = {
        .driver.name = KBUILD_MODNAME,
        .driver.owner = THIS_MODULE,
        .id_table = id_table,
+       .feature_table = features,
+       .feature_table_size = ARRAY_SIZE(features),
        .validate = virtsnd_validate,
        .probe = virtsnd_probe,
        .remove = virtsnd_remove,
index 86ef3941895ed25c9bfa1d46267bef99f2ad7101..3ceee4e416fc7be8c016064bfe08f26f8fe86fb8 100644 (file)
@@ -31,6 +31,16 @@ struct virtio_snd_queue {
        struct virtqueue *vqueue;
 };
 
+/**
+ * struct virtio_kctl - VirtIO control element.
+ * @kctl: ALSA control element.
+ * @items: Items for the ENUMERATED element type.
+ */
+struct virtio_kctl {
+       struct snd_kcontrol *kctl;
+       struct virtio_snd_ctl_enum_item *items;
+};
+
 /**
  * struct virtio_snd - VirtIO sound card device.
  * @vdev: Underlying virtio device.
@@ -45,6 +55,9 @@ struct virtio_snd_queue {
  * @nsubstreams: Number of PCM substreams.
  * @chmaps: VirtIO channel maps.
  * @nchmaps: Number of channel maps.
+ * @kctl_infos: VirtIO control element information.
+ * @kctls: VirtIO control elements.
+ * @nkctls: Number of control elements.
  */
 struct virtio_snd {
        struct virtio_device *vdev;
@@ -59,6 +72,9 @@ struct virtio_snd {
        u32 nsubstreams;
        struct virtio_snd_chmap_info *chmaps;
        u32 nchmaps;
+       struct virtio_snd_ctl_info *kctl_infos;
+       struct virtio_kctl *kctls;
+       u32 nkctls;
 };
 
 /* Message completion timeout in milliseconds (module parameter). */
@@ -108,4 +124,10 @@ int virtsnd_chmap_parse_cfg(struct virtio_snd *snd);
 
 int virtsnd_chmap_build_devs(struct virtio_snd *snd);
 
+int virtsnd_kctl_parse_cfg(struct virtio_snd *snd);
+
+int virtsnd_kctl_build_devs(struct virtio_snd *snd);
+
+void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event);
+
 #endif /* VIRTIO_SND_CARD_H */
diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c
new file mode 100644 (file)
index 0000000..7aa79c0
--- /dev/null
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * virtio-snd: Virtio sound device
+ * Copyright (C) 2022 OpenSynergy GmbH
+ */
+#include <sound/control.h>
+#include <linux/virtio_config.h>
+
+#include "virtio_card.h"
+
+/* Map for converting VirtIO types to ALSA types. */
+static const snd_ctl_elem_type_t g_v2a_type_map[] = {
+       [VIRTIO_SND_CTL_TYPE_BOOLEAN] = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+       [VIRTIO_SND_CTL_TYPE_INTEGER] = SNDRV_CTL_ELEM_TYPE_INTEGER,
+       [VIRTIO_SND_CTL_TYPE_INTEGER64] = SNDRV_CTL_ELEM_TYPE_INTEGER64,
+       [VIRTIO_SND_CTL_TYPE_ENUMERATED] = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+       [VIRTIO_SND_CTL_TYPE_BYTES] = SNDRV_CTL_ELEM_TYPE_BYTES,
+       [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958
+};
+
+/* Map for converting VirtIO access rights to ALSA access rights. */
+static const unsigned int g_v2a_access_map[] = {
+       [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ,
+       [VIRTIO_SND_CTL_ACCESS_WRITE] = SNDRV_CTL_ELEM_ACCESS_WRITE,
+       [VIRTIO_SND_CTL_ACCESS_VOLATILE] = SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       [VIRTIO_SND_CTL_ACCESS_INACTIVE] = SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+       [VIRTIO_SND_CTL_ACCESS_TLV_READ] = SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+       [VIRTIO_SND_CTL_ACCESS_TLV_WRITE] = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE,
+       [VIRTIO_SND_CTL_ACCESS_TLV_COMMAND] = SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND
+};
+
+/* Map for converting VirtIO event masks to ALSA event masks. */
+static const unsigned int g_v2a_mask_map[] = {
+       [VIRTIO_SND_CTL_EVT_MASK_VALUE] = SNDRV_CTL_EVENT_MASK_VALUE,
+       [VIRTIO_SND_CTL_EVT_MASK_INFO] = SNDRV_CTL_EVENT_MASK_INFO,
+       [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV
+};
+
+/**
+ * virtsnd_kctl_info() - Returns information about the control.
+ * @kcontrol: ALSA control element.
+ * @uinfo: Element information.
+ *
+ * Context: Process context.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_kctl_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
+{
+       struct virtio_snd *snd = kcontrol->private_data;
+       struct virtio_kctl *kctl = &snd->kctls[kcontrol->private_value];
+       struct virtio_snd_ctl_info *kinfo =
+               &snd->kctl_infos[kcontrol->private_value];
+       unsigned int i;
+
+       uinfo->type = g_v2a_type_map[le32_to_cpu(kinfo->type)];
+       uinfo->count = le32_to_cpu(kinfo->count);
+
+       switch (uinfo->type) {
+       case SNDRV_CTL_ELEM_TYPE_INTEGER:
+               uinfo->value.integer.min =
+                       le32_to_cpu(kinfo->value.integer.min);
+               uinfo->value.integer.max =
+                       le32_to_cpu(kinfo->value.integer.max);
+               uinfo->value.integer.step =
+                       le32_to_cpu(kinfo->value.integer.step);
+
+               break;
+       case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+               uinfo->value.integer64.min =
+                       le64_to_cpu(kinfo->value.integer64.min);
+               uinfo->value.integer64.max =
+                       le64_to_cpu(kinfo->value.integer64.max);
+               uinfo->value.integer64.step =
+                       le64_to_cpu(kinfo->value.integer64.step);
+
+               break;
+       case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+               uinfo->value.enumerated.items =
+                       le32_to_cpu(kinfo->value.enumerated.items);
+               i = uinfo->value.enumerated.item;
+               if (i >= uinfo->value.enumerated.items)
+                       return -EINVAL;
+
+               strscpy(uinfo->value.enumerated.name, kctl->items[i].item,
+                       sizeof(uinfo->value.enumerated.name));
+
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * virtsnd_kctl_get() - Read the value from the control.
+ * @kcontrol: ALSA control element.
+ * @uvalue: Element value.
+ *
+ * Context: Process context.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *uvalue)
+{
+       struct virtio_snd *snd = kcontrol->private_data;
+       struct virtio_snd_ctl_info *kinfo =
+               &snd->kctl_infos[kcontrol->private_value];
+       unsigned int type = le32_to_cpu(kinfo->type);
+       unsigned int count = le32_to_cpu(kinfo->count);
+       struct virtio_snd_msg *msg;
+       struct virtio_snd_ctl_hdr *hdr;
+       struct virtio_snd_ctl_value *kvalue;
+       size_t request_size = sizeof(*hdr);
+       size_t response_size = sizeof(struct virtio_snd_hdr) + sizeof(*kvalue);
+       unsigned int i;
+       int rc;
+
+       msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       virtsnd_ctl_msg_ref(msg);
+
+       hdr = virtsnd_ctl_msg_request(msg);
+       hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_READ);
+       hdr->control_id = cpu_to_le32(kcontrol->private_value);
+
+       rc = virtsnd_ctl_msg_send_sync(snd, msg);
+       if (rc)
+               goto on_failure;
+
+       kvalue = (void *)((u8 *)virtsnd_ctl_msg_response(msg) +
+                         sizeof(struct virtio_snd_hdr));
+
+       switch (type) {
+       case VIRTIO_SND_CTL_TYPE_BOOLEAN:
+       case VIRTIO_SND_CTL_TYPE_INTEGER:
+               for (i = 0; i < count; ++i)
+                       uvalue->value.integer.value[i] =
+                               le32_to_cpu(kvalue->value.integer[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_INTEGER64:
+               for (i = 0; i < count; ++i)
+                       uvalue->value.integer64.value[i] =
+                               le64_to_cpu(kvalue->value.integer64[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_ENUMERATED:
+               for (i = 0; i < count; ++i)
+                       uvalue->value.enumerated.item[i] =
+                               le32_to_cpu(kvalue->value.enumerated[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_BYTES:
+               memcpy(uvalue->value.bytes.data, kvalue->value.bytes, count);
+               break;
+       case VIRTIO_SND_CTL_TYPE_IEC958:
+               memcpy(&uvalue->value.iec958, &kvalue->value.iec958,
+                      sizeof(uvalue->value.iec958));
+               break;
+       }
+
+on_failure:
+       virtsnd_ctl_msg_unref(msg);
+
+       return rc;
+}
+
+/**
+ * virtsnd_kctl_put() - Write the value to the control.
+ * @kcontrol: ALSA control element.
+ * @uvalue: Element value.
+ *
+ * Context: Process context.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_kctl_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *uvalue)
+{
+       struct virtio_snd *snd = kcontrol->private_data;
+       struct virtio_snd_ctl_info *kinfo =
+               &snd->kctl_infos[kcontrol->private_value];
+       unsigned int type = le32_to_cpu(kinfo->type);
+       unsigned int count = le32_to_cpu(kinfo->count);
+       struct virtio_snd_msg *msg;
+       struct virtio_snd_ctl_hdr *hdr;
+       struct virtio_snd_ctl_value *kvalue;
+       size_t request_size = sizeof(*hdr) + sizeof(*kvalue);
+       size_t response_size = sizeof(struct virtio_snd_hdr);
+       unsigned int i;
+
+       msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = virtsnd_ctl_msg_request(msg);
+       hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_WRITE);
+       hdr->control_id = cpu_to_le32(kcontrol->private_value);
+
+       kvalue = (void *)((u8 *)hdr + sizeof(*hdr));
+
+       switch (type) {
+       case VIRTIO_SND_CTL_TYPE_BOOLEAN:
+       case VIRTIO_SND_CTL_TYPE_INTEGER:
+               for (i = 0; i < count; ++i)
+                       kvalue->value.integer[i] =
+                               cpu_to_le32(uvalue->value.integer.value[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_INTEGER64:
+               for (i = 0; i < count; ++i)
+                       kvalue->value.integer64[i] =
+                               cpu_to_le64(uvalue->value.integer64.value[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_ENUMERATED:
+               for (i = 0; i < count; ++i)
+                       kvalue->value.enumerated[i] =
+                               cpu_to_le32(uvalue->value.enumerated.item[i]);
+               break;
+       case VIRTIO_SND_CTL_TYPE_BYTES:
+               memcpy(kvalue->value.bytes, uvalue->value.bytes.data, count);
+               break;
+       case VIRTIO_SND_CTL_TYPE_IEC958:
+               memcpy(&kvalue->value.iec958, &uvalue->value.iec958,
+                      sizeof(kvalue->value.iec958));
+               break;
+       }
+
+       return virtsnd_ctl_msg_send_sync(snd, msg);
+}
+
+/**
+ * virtsnd_kctl_tlv_op() - Perform an operation on the control's metadata.
+ * @kcontrol: ALSA control element.
+ * @op_flag: Operation code (SNDRV_CTL_TLV_OP_XXX).
+ * @size: Size of the TLV data in bytes.
+ * @utlv: TLV data.
+ *
+ * Context: Process context.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_kctl_tlv_op(struct snd_kcontrol *kcontrol, int op_flag,
+                              unsigned int size, unsigned int __user *utlv)
+{
+       struct virtio_snd *snd = kcontrol->private_data;
+       struct virtio_snd_msg *msg;
+       struct virtio_snd_ctl_hdr *hdr;
+       unsigned int *tlv;
+       struct scatterlist sg;
+       int rc;
+
+       msg = virtsnd_ctl_msg_alloc(sizeof(*hdr), sizeof(struct virtio_snd_hdr),
+                                   GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       tlv = kzalloc(size, GFP_KERNEL);
+       if (!tlv) {
+               rc = -ENOMEM;
+               goto on_msg_unref;
+       }
+
+       sg_init_one(&sg, tlv, size);
+
+       hdr = virtsnd_ctl_msg_request(msg);
+       hdr->control_id = cpu_to_le32(kcontrol->private_value);
+
+       switch (op_flag) {
+       case SNDRV_CTL_TLV_OP_READ:
+               hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_READ);
+
+               rc = virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false);
+               if (!rc) {
+                       if (copy_to_user(utlv, tlv, size))
+                               rc = -EFAULT;
+               }
+
+               break;
+       case SNDRV_CTL_TLV_OP_WRITE:
+       case SNDRV_CTL_TLV_OP_CMD:
+               if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
+                       hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_WRITE);
+               else
+                       hdr->hdr.code =
+                               cpu_to_le32(VIRTIO_SND_R_CTL_TLV_COMMAND);
+
+               if (copy_from_user(tlv, utlv, size)) {
+                       rc = -EFAULT;
+                       goto on_msg_unref;
+               } else {
+                       rc = virtsnd_ctl_msg_send(snd, msg, &sg, NULL, false);
+               }
+
+               break;
+       default:
+               rc = -EINVAL;
+               /* We never get here - we listed all values for op_flag */
+               WARN_ON(1);
+               goto on_msg_unref;
+       }
+       kfree(tlv);
+       return rc;
+
+on_msg_unref:
+       virtsnd_ctl_msg_unref(msg);
+       kfree(tlv);
+
+       return rc;
+}
+
+/**
+ * virtsnd_kctl_get_enum_items() - Query items for the ENUMERATED element type.
+ * @snd: VirtIO sound device.
+ * @cid: Control element ID.
+ *
+ * This function is called during initial device initialization.
+ *
+ * Context: Any context that permits to sleep.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_kctl_get_enum_items(struct virtio_snd *snd, unsigned int cid)
+{
+       struct virtio_device *vdev = snd->vdev;
+       struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid];
+       struct virtio_kctl *kctl = &snd->kctls[cid];
+       struct virtio_snd_msg *msg;
+       struct virtio_snd_ctl_hdr *hdr;
+       unsigned int n = le32_to_cpu(kinfo->value.enumerated.items);
+       struct scatterlist sg;
+
+       msg = virtsnd_ctl_msg_alloc(sizeof(*hdr),
+                                   sizeof(struct virtio_snd_hdr), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       kctl->items = devm_kcalloc(&vdev->dev, n, sizeof(*kctl->items),
+                                  GFP_KERNEL);
+       if (!kctl->items) {
+               virtsnd_ctl_msg_unref(msg);
+               return -ENOMEM;
+       }
+
+       sg_init_one(&sg, kctl->items, n * sizeof(*kctl->items));
+
+       hdr = virtsnd_ctl_msg_request(msg);
+       hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_ENUM_ITEMS);
+       hdr->control_id = cpu_to_le32(cid);
+
+       return virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false);
+}
+
+/**
+ * virtsnd_kctl_parse_cfg() - Parse the control element configuration.
+ * @snd: VirtIO sound device.
+ *
+ * This function is called during initial device initialization.
+ *
+ * Context: Any context that permits to sleep.
+ * Return: 0 on success, -errno on failure.
+ */
+int virtsnd_kctl_parse_cfg(struct virtio_snd *snd)
+{
+       struct virtio_device *vdev = snd->vdev;
+       u32 i;
+       int rc;
+
+       virtio_cread_le(vdev, struct virtio_snd_config, controls,
+                       &snd->nkctls);
+       if (!snd->nkctls)
+               return 0;
+
+       snd->kctl_infos = devm_kcalloc(&vdev->dev, snd->nkctls,
+                                      sizeof(*snd->kctl_infos), GFP_KERNEL);
+       if (!snd->kctl_infos)
+               return -ENOMEM;
+
+       snd->kctls = devm_kcalloc(&vdev->dev, snd->nkctls, sizeof(*snd->kctls),
+                                 GFP_KERNEL);
+       if (!snd->kctls)
+               return -ENOMEM;
+
+       rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CTL_INFO, 0, snd->nkctls,
+                                   sizeof(*snd->kctl_infos), snd->kctl_infos);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < snd->nkctls; ++i) {
+               struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i];
+               unsigned int type = le32_to_cpu(kinfo->type);
+
+               if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) {
+                       rc = virtsnd_kctl_get_enum_items(snd, i);
+                       if (rc)
+                               return rc;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * virtsnd_kctl_build_devs() - Build ALSA control elements.
+ * @snd: VirtIO sound device.
+ *
+ * Context: Any context that permits to sleep.
+ * Return: 0 on success, -errno on failure.
+ */
+int virtsnd_kctl_build_devs(struct virtio_snd *snd)
+{
+       unsigned int cid;
+
+       for (cid = 0; cid < snd->nkctls; ++cid) {
+               struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid];
+               struct virtio_kctl *kctl = &snd->kctls[cid];
+               struct snd_kcontrol_new kctl_new;
+               unsigned int i;
+               int rc;
+
+               memset(&kctl_new, 0, sizeof(kctl_new));
+
+               kctl_new.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               kctl_new.name = kinfo->name;
+               kctl_new.index = le32_to_cpu(kinfo->index);
+
+               for (i = 0; i < ARRAY_SIZE(g_v2a_access_map); ++i)
+                       if (le32_to_cpu(kinfo->access) & (1 << i))
+                               kctl_new.access |= g_v2a_access_map[i];
+
+               if (kctl_new.access & (SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                                      SNDRV_CTL_ELEM_ACCESS_TLV_WRITE |
+                                      SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND)) {
+                       kctl_new.access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+                       kctl_new.tlv.c = virtsnd_kctl_tlv_op;
+               }
+
+               kctl_new.info = virtsnd_kctl_info;
+               kctl_new.get = virtsnd_kctl_get;
+               kctl_new.put = virtsnd_kctl_put;
+               kctl_new.private_value = cid;
+
+               kctl->kctl = snd_ctl_new1(&kctl_new, snd);
+               if (!kctl->kctl)
+                       return -ENOMEM;
+
+               rc = snd_ctl_add(snd->card, kctl->kctl);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * virtsnd_kctl_event() - Handle the control element event notification.
+ * @snd: VirtIO sound device.
+ * @event: VirtIO sound event.
+ *
+ * Context: Interrupt context.
+ */
+void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event)
+{
+       struct virtio_snd_ctl_event *kevent =
+               (struct virtio_snd_ctl_event *)event;
+       struct virtio_kctl *kctl;
+       unsigned int cid = le16_to_cpu(kevent->control_id);
+       unsigned int mask = 0;
+       unsigned int i;
+
+       if (cid >= snd->nkctls)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(g_v2a_mask_map); ++i)
+               if (le16_to_cpu(kevent->mask) & (1 << i))
+                       mask |= g_v2a_mask_map[i];
+
+
+       kctl = &snd->kctls[cid];
+
+       snd_ctl_notify(snd->card, mask, &kctl->kctl->id);
+}