Merge tag 'sound-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 27 Apr 2023 17:58:37 +0000 (10:58 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 27 Apr 2023 17:58:37 +0000 (10:58 -0700)
Pull sound updates from Takashi Iwai:
 "At this time, it's an interesting mixture of changes for both old and
  new stuff. Majority of changes are about ASoC (lots of systematic
  changes for converting remove callbacks to void, and cleanups), while
  we got the fixes and the enhancements of very old PCI cards, too.

  Here are some highlights:

  ALSA/ASoC Core:
   - Continued effort of more ASoC core cleanups
   - Minor improvements for XRUN handling in indirect PCM helpers
   - Code refactoring of PCM core code

  ASoC:
   - Continued feature and simplification work on SOF, including
     addition of a no-DSP mode for bringup, HDA MLink and extensions to
     the IPC4 protocol
   - Hibernation support for CS35L45
   - More DT binding conversions
   - Support for Cirrus Logic CS35L56, Freescale QMC, Maxim MAX98363,
     nVidia systems with MAX9809x and RT5631, Realtek RT712, Renesas
     R-Car Gen4, Rockchip RK3588 and TI TAS5733

  ALSA:
   - Lots of works for legacy emu10k1 and ymfpci PCI drivers
   - PCM kselftest fixes and enhancements"

* tag 'sound-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (586 commits)
  ALSA: emu10k1: use high-level I/O in set_filterQ()
  ALSA: emu10k1: use high-level I/O functions also during init
  ALSA: emu10k1: fix error handling in snd_audigy_i2c_volume_put()
  ALSA: emu10k1: don't stop DSP in _snd_emu10k1_{,audigy_}init_efx()
  ALSA: emu10k1: fix SNDRV_EMU10K1_IOCTL_SINGLE_STEP
  ALSA: emu10k1: skip Sound Blaster-specific hacks for E-MU cards
  ALSA: emu10k1: fixup DSP defines
  ALSA: emu10k1: pull in some register definitions from kX-project
  ALSA: emu10k1: remove some bogus defines
  ALSA: emu10k1: eliminate some unused defines
  ALSA: emu10k1: fix lineup of EMU_HANA_* defines
  ALSA: emu10k1: comment updates
  ALSA: emu10k1: fix snd_emu1010_fpga_read() input masking for rev2 cards
  ALSA: emu10k1: remove unused emu->pcm_playback_efx_substream field
  ALSA: emu10k1: remove unused `resume` parameter from snd_emu10k1_init()
  ALSA: emu10k1: minor optimizations
  ALSA: emu10k1: remove remaining cruft from snd_emu10k1_emu1010_init()
  ALSA: emu10k1: remove apparently pointless EMU_HANA_OPTION_CARDS reads
  ALSA: emu10k1: remove apparently pointless FPGA reads
  ALSA: emu10k1: stop doing weird things with HCFG in snd_emu10k1_emu1010_init()
  ...

520 files changed:
Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/adi,adau1372.yaml
Documentation/devicetree/bindings/sound/adi,adau17x1.txt [deleted file]
Documentation/devicetree/bindings/sound/adi,adau17x1.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/adi,max98363.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/adi,max98396.yaml
Documentation/devicetree/bindings/sound/ak4458.txt [deleted file]
Documentation/devicetree/bindings/sound/ak5558.txt [deleted file]
Documentation/devicetree/bindings/sound/alc5632.txt [deleted file]
Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/asahi-kasei,ak5558.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/audio-graph-port.yaml
Documentation/devicetree/bindings/sound/audio-graph.yaml
Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml
Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
Documentation/devicetree/bindings/sound/cirrus,ep9301-i2s.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/everest,es8316.yaml
Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/max98371.txt [deleted file]
Documentation/devicetree/bindings/sound/max9867.txt [deleted file]
Documentation/devicetree/bindings/sound/maxim,max9759.txt [deleted file]
Documentation/devicetree/bindings/sound/maxim,max9759.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/maxim,max98371.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/maxim,max9867.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml
Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml
Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml
Documentation/devicetree/bindings/sound/nau8825.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5631.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml
Documentation/devicetree/bindings/sound/qcom,wcd9335.txt [deleted file]
Documentation/devicetree/bindings/sound/qcom,wcd9335.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
Documentation/devicetree/bindings/sound/realtek,alc5632.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml
Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml
Documentation/devicetree/bindings/sound/rockchip-i2s.yaml
Documentation/devicetree/bindings/sound/sgtl5000.yaml
Documentation/devicetree/bindings/sound/simple-card.yaml
Documentation/devicetree/bindings/sound/socionext,uniphier-aio.yaml
Documentation/devicetree/bindings/sound/tas571x.txt
Documentation/devicetree/bindings/sound/wlf,wm8510.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8523.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8524.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8580.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8711.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8728.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8737.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8753.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8960.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8994.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8510.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8523.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8524.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8580.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8711.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8728.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8737.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8753.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8960.txt [deleted file]
Documentation/devicetree/bindings/sound/wm8994.txt [deleted file]
Documentation/sound/alsa-configuration.rst
Documentation/sound/cards/audigy-mixer.rst
Documentation/sound/cards/sb-live-mixer.rst
Documentation/sound/hd-audio/index.rst
Documentation/sound/hd-audio/intel-multi-link.rst [new file with mode: 0644]
Documentation/sound/kernel-api/writing-an-alsa-driver.rst
MAINTAINERS
arch/powerpc/platforms/8xx/cpm1.c
drivers/firmware/cirrus/cs_dsp.c
drivers/soc/fsl/qe/Kconfig
drivers/soc/fsl/qe/Makefile
drivers/soc/fsl/qe/qmc.c [new file with mode: 0644]
drivers/soc/fsl/qe/tsa.c [new file with mode: 0644]
drivers/soc/fsl/qe/tsa.h [new file with mode: 0644]
include/dt-bindings/soc/cpm1-fsl,tsa.h [new file with mode: 0644]
include/dt-bindings/sound/cs35l45.h
include/linux/firmware/cirrus/cs_dsp.h
include/soc/fsl/qe/qmc.h [new file with mode: 0644]
include/sound/ac97_codec.h
include/sound/cs35l41.h
include/sound/cs35l56.h [new file with mode: 0644]
include/sound/emu10k1.h
include/sound/hda-mlink.h [new file with mode: 0644]
include/sound/hda_register.h
include/sound/pcm-indirect.h
include/sound/pcm.h
include/sound/soc-card.h
include/sound/soc-dapm.h
include/sound/soc-dpcm.h
include/sound/soc.h
include/sound/sof.h
include/sound/sof/ipc4/header.h
include/uapi/sound/asound.h
include/uapi/sound/emu10k1.h
include/uapi/sound/sof/abi.h
include/uapi/sound/sof/header.h
include/uapi/sound/sof/tokens.h
sound/ac97_bus.c
sound/arm/pxa2xx-ac97.c
sound/atmel/ac97c.c
sound/core/pcm_lib.c
sound/core/pcm_local.h
sound/core/pcm_native.c
sound/drivers/mts64.c
sound/drivers/portman2x4.c
sound/mips/hal2.c
sound/mips/sgio2audio.c
sound/pci/Kconfig
sound/pci/asihpi/hpi6000.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1_callback.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/emumixer.c
sound/pci/emu10k1/emupcm.c
sound/pci/emu10k1/emuproc.c
sound/pci/emu10k1/io.c
sound/pci/emu10k1/irq.c
sound/pci/emu10k1/p16v.c
sound/pci/emu10k1/p16v.h
sound/pci/emu10k1/p17v.h
sound/pci/hda/cs35l41_hda.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_tegra.c
sound/pci/hda/patch_hdmi.c
sound/pci/rme9652/hdspm.c
sound/pci/ymfpci/ymfpci.c
sound/pci/ymfpci/ymfpci.h
sound/pci/ymfpci/ymfpci_main.c
sound/ppc/powermac.c
sound/ppc/tumbler.c
sound/sh/aica.c
sound/sh/sh_dac_audio.c
sound/soc/adi/axi-i2s.c
sound/soc/adi/axi-spdif.c
sound/soc/amd/Kconfig
sound/soc/amd/acp-pcm-dma.c
sound/soc/amd/acp/acp-rembrandt.c
sound/soc/amd/acp/acp-renoir.c
sound/soc/amd/ps/acp63.h
sound/soc/amd/ps/pci-ps.c
sound/soc/amd/ps/ps-pdm-dma.c
sound/soc/amd/raven/acp3x-pcm-dma.c
sound/soc/amd/renoir/acp3x-pdm-dma.c
sound/soc/amd/vangogh/acp5x-mach.c
sound/soc/amd/vangogh/acp5x-pcm-dma.c
sound/soc/amd/yc/acp6x-mach.c
sound/soc/amd/yc/acp6x-pdm-dma.c
sound/soc/amd/yc/acp6x.h
sound/soc/amd/yc/pci-acp6x.c
sound/soc/apple/mca.c
sound/soc/atmel/atmel-classd.c
sound/soc/atmel/atmel-i2s.c
sound/soc/atmel/atmel-pdmic.c
sound/soc/atmel/atmel_wm8904.c
sound/soc/atmel/mchp-i2s-mcc.c
sound/soc/atmel/mchp-pdmc.c
sound/soc/atmel/mchp-spdifrx.c
sound/soc/atmel/mchp-spdiftx.c
sound/soc/atmel/mikroe-proto.c
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/atmel/sam9x5_wm8731.c
sound/soc/atmel/tse850-pcm5142.c
sound/soc/au1x/ac97c.c
sound/soc/au1x/i2sc.c
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc-i2s.c
sound/soc/bcm/bcm63xx-i2s-whistler.c
sound/soc/bcm/cygnus-ssp.c
sound/soc/cirrus/Kconfig
sound/soc/cirrus/edb93xx.c
sound/soc/cirrus/ep93xx-i2s.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/adau1977-spi.c
sound/soc/codecs/cs35l41-lib.c
sound/soc/codecs/cs35l41.c
sound/soc/codecs/cs35l41.h
sound/soc/codecs/cs35l45-i2c.c
sound/soc/codecs/cs35l45-spi.c
sound/soc/codecs/cs35l45-tables.c
sound/soc/codecs/cs35l45.c
sound/soc/codecs/cs35l45.h
sound/soc/codecs/cs35l56-i2c.c [new file with mode: 0644]
sound/soc/codecs/cs35l56-sdw.c [new file with mode: 0644]
sound/soc/codecs/cs35l56-shared.c [new file with mode: 0644]
sound/soc/codecs/cs35l56-spi.c [new file with mode: 0644]
sound/soc/codecs/cs35l56.c [new file with mode: 0644]
sound/soc/codecs/cs35l56.h [new file with mode: 0644]
sound/soc/codecs/cs4271-i2c.c
sound/soc/codecs/cs4271-spi.c
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs42l42-sdw.c
sound/soc/codecs/cs47l15.c
sound/soc/codecs/cs47l24.c
sound/soc/codecs/cs47l35.c
sound/soc/codecs/cs47l85.c
sound/soc/codecs/cs47l90.c
sound/soc/codecs/cs47l92.c
sound/soc/codecs/da7218.c
sound/soc/codecs/da7219-aad.c
sound/soc/codecs/da7219-aad.h
sound/soc/codecs/es8316.c
sound/soc/codecs/inno_rk3036.c
sound/soc/codecs/lpass-macro-common.c
sound/soc/codecs/lpass-macro-common.h
sound/soc/codecs/lpass-rx-macro.c
sound/soc/codecs/lpass-tx-macro.c
sound/soc/codecs/lpass-va-macro.c
sound/soc/codecs/lpass-wsa-macro.c
sound/soc/codecs/max98363.c [new file with mode: 0644]
sound/soc/codecs/max98363.h [new file with mode: 0644]
sound/soc/codecs/max98373-sdw.c
sound/soc/codecs/max9867.c
sound/soc/codecs/msm8916-wcd-analog.c
sound/soc/codecs/msm8916-wcd-digital.c
sound/soc/codecs/mt6358.c
sound/soc/codecs/mt6359.c
sound/soc/codecs/nau8821.c
sound/soc/codecs/nau8821.h
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.h
sound/soc/codecs/pcm179x-spi.c
sound/soc/codecs/rk817_codec.c
sound/soc/codecs/rt1019.c
sound/soc/codecs/rt1308-sdw.c
sound/soc/codecs/rt1308-sdw.h
sound/soc/codecs/rt1316-sdw.c
sound/soc/codecs/rt1316-sdw.h
sound/soc/codecs/rt1318-sdw.c
sound/soc/codecs/rt1318-sdw.h
sound/soc/codecs/rt5682-sdw.c
sound/soc/codecs/rt700.c
sound/soc/codecs/rt700.h
sound/soc/codecs/rt711-sdca.c
sound/soc/codecs/rt711-sdca.h
sound/soc/codecs/rt711.c
sound/soc/codecs/rt711.h
sound/soc/codecs/rt712-sdca-dmic.c [new file with mode: 0644]
sound/soc/codecs/rt712-sdca-dmic.h [new file with mode: 0644]
sound/soc/codecs/rt712-sdca.c
sound/soc/codecs/rt712-sdca.h
sound/soc/codecs/rt715-sdca.c
sound/soc/codecs/rt715-sdca.h
sound/soc/codecs/rt715.c
sound/soc/codecs/rt715.h
sound/soc/codecs/sdw-mockup.c
sound/soc/codecs/sma1303.c
sound/soc/codecs/src4xxx-i2c.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/sta32x.c
sound/soc/codecs/sta350.c
sound/soc/codecs/tas5086.c
sound/soc/codecs/tas571x.c
sound/soc/codecs/tas5720.c
sound/soc/codecs/wcd9335.c
sound/soc/codecs/wcd934x.c
sound/soc/codecs/wcd938x.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm8998.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/zl38060.c
sound/soc/dwc/dwc-i2s.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/eukrea-tlv320.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_aud2htx.c
sound/soc/fsl/fsl_audmix.c
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_easrc.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_mqs.c
sound/soc/fsl/fsl_qmc_audio.c [new file with mode: 0644]
sound/soc/fsl/fsl_rpmsg.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/fsl_xcvr.c
sound/soc/fsl/imx-audmix.c
sound/soc/fsl/imx-audmux.c
sound/soc/fsl/imx-card.c
sound/soc/fsl/imx-es8328.c
sound/soc/fsl/imx-pcm-rpmsg.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-spdif.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/fsl/mpc5200_psc_i2s.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/p1022_ds.c
sound/soc/fsl/p1022_rdk.c
sound/soc/fsl/pcm030-audio-fabric.c
sound/soc/generic/audio-graph-card.c
sound/soc/generic/audio-graph-card2-custom-sample.c
sound/soc/generic/audio-graph-card2.c
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-card.c
sound/soc/generic/test-component.c
sound/soc/img/img-i2s-in.c
sound/soc/img/img-i2s-out.c
sound/soc/img/img-parallel-out.c
sound/soc/img/img-spdif-in.c
sound/soc/img/img-spdif-out.c
sound/soc/img/pistachio-internal-dac.c
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/avs/boards/hdaudio.c
sound/soc/intel/avs/path.c
sound/soc/intel/boards/bytcht_es8316.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/bytcr_rt5651.c
sound/soc/intel/boards/bytcr_wm5102.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/sof_cirrus_common.c
sound/soc/intel/boards/sof_es8336.c
sound/soc/intel/boards/sof_pcm512x.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_max98373.c
sound/soc/intel/boards/sof_wm8804.c
sound/soc/intel/catpt/device.c
sound/soc/intel/common/soc-acpi-intel-rpl-match.c
sound/soc/intel/skylake/skl-ssp-clk.c
sound/soc/kirkwood/kirkwood-i2s.c
sound/soc/mediatek/common/mtk-btcvsd.c
sound/soc/mediatek/common/mtk-soundcard-driver.c
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
sound/soc/mediatek/mt8186/mt8186-dai-adda.c
sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
sound/soc/mediatek/mt8192/mt8192-afe-clk.c
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
sound/soc/mediatek/mt8195/mt8195-dai-adda.c
sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
sound/soc/mediatek/mt8195/mt8195-mt6359.c
sound/soc/meson/aiu.c
sound/soc/meson/axg-card.c
sound/soc/meson/axg-tdm-interface.c
sound/soc/meson/gx-card.c
sound/soc/meson/meson-codec-glue.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/pxa/mmp-sspa.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/qcom/common.c
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
sound/soc/qcom/qdsp6/q6apm.c
sound/soc/qcom/qdsp6/q6routing.c
sound/soc/qcom/sdw.c
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_pdm.c
sound/soc/rockchip/rockchip_rt5645.c
sound/soc/rockchip/rockchip_spdif.c
sound/soc/samsung/aries_wm8994.c
sound/soc/samsung/arndale.c
sound/soc/samsung/bells.c
sound/soc/samsung/i2s.c
sound/soc/samsung/littlemill.c
sound/soc/samsung/lowland.c
sound/soc/samsung/odroid.c
sound/soc/samsung/pcm.c
sound/soc/samsung/snow.c
sound/soc/samsung/spdif.c
sound/soc/samsung/speyside.c
sound/soc/sh/fsi.c
sound/soc/sh/hac.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rz-ssi.c
sound/soc/sh/siu_dai.c
sound/soc/soc-component.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dai.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sof/amd/acp-common.c
sound/soc/sof/amd/acp-pcm.c
sound/soc/sof/amd/acp.c
sound/soc/sof/amd/acp.h
sound/soc/sof/amd/pci-rmb.c
sound/soc/sof/amd/pci-rn.c
sound/soc/sof/amd/rembrandt.c
sound/soc/sof/amd/renoir.c
sound/soc/sof/compress.c
sound/soc/sof/core.c
sound/soc/sof/debug.c
sound/soc/sof/intel/Kconfig
sound/soc/sof/intel/Makefile
sound/soc/sof/intel/apl.c
sound/soc/sof/intel/cnl.c
sound/soc/sof/intel/hda-common-ops.c
sound/soc/sof/intel/hda-ctrl.c
sound/soc/sof/intel/hda-dai-ops.c [new file with mode: 0644]
sound/soc/sof/intel/hda-dai.c
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda-ipc.c
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda-mlink.c
sound/soc/sof/intel/hda-pcm.c
sound/soc/sof/intel/hda-stream.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/icl.c
sound/soc/sof/intel/mtl.c
sound/soc/sof/intel/pci-apl.c
sound/soc/sof/intel/pci-cnl.c
sound/soc/sof/intel/pci-icl.c
sound/soc/sof/intel/pci-mtl.c
sound/soc/sof/intel/pci-skl.c
sound/soc/sof/intel/pci-tgl.c
sound/soc/sof/intel/tgl.c
sound/soc/sof/ipc3-control.c
sound/soc/sof/ipc3-dtrace.c
sound/soc/sof/ipc3-pcm.c
sound/soc/sof/ipc3-topology.c
sound/soc/sof/ipc3.c
sound/soc/sof/ipc4-control.c
sound/soc/sof/ipc4-mtrace.c
sound/soc/sof/ipc4-pcm.c
sound/soc/sof/ipc4-topology.c
sound/soc/sof/ipc4-topology.h
sound/soc/sof/loader.c
sound/soc/sof/pcm.c
sound/soc/sof/pm.c
sound/soc/sof/sof-audio.c
sound/soc/sof/sof-audio.h
sound/soc/sof/sof-client-ipc-flood-test.c
sound/soc/sof/sof-client-probes-ipc3.c
sound/soc/sof/sof-client-probes-ipc4.c
sound/soc/sof/sof-client.c
sound/soc/sof/sof-client.h
sound/soc/sof/sof-priv.h
sound/soc/sof/topology.c
sound/soc/sprd/sprd-mcdt.c
sound/soc/stm/stm32_adfsdm.c
sound/soc/stm/stm32_i2s.c
sound/soc/stm/stm32_sai_sub.c
sound/soc/stm/stm32_spdifrx.c
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun4i-i2s.c
sound/soc/sunxi/sun4i-spdif.c
sound/soc/sunxi/sun50i-dmic.c
sound/soc/sunxi/sun8i-codec.c
sound/soc/tegra/Kconfig
sound/soc/tegra/tegra186_asrc.c
sound/soc/tegra/tegra186_dspk.c
sound/soc/tegra/tegra20_ac97.c
sound/soc/tegra/tegra20_i2s.c
sound/soc/tegra/tegra210_admaif.c
sound/soc/tegra/tegra210_adx.c
sound/soc/tegra/tegra210_ahub.c
sound/soc/tegra/tegra210_amx.c
sound/soc/tegra/tegra210_dmic.c
sound/soc/tegra/tegra210_i2s.c
sound/soc/tegra/tegra210_mixer.c
sound/soc/tegra/tegra210_mvc.c
sound/soc/tegra/tegra210_ope.c
sound/soc/tegra/tegra210_sfc.c
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra_asoc_machine.c
sound/soc/ti/ams-delta.c
sound/soc/ti/davinci-i2s.c
sound/soc/ti/davinci-mcasp.c
sound/soc/ti/omap-hdmi.c
sound/soc/ti/omap-mcbsp.c
sound/soc/uniphier/evea.c
sound/soc/ux500/mop500.c
sound/soc/ux500/ux500_msp_dai.c
sound/soc/xilinx/xlnx_formatter_pcm.c
sound/soc/xilinx/xlnx_spdif.c
sound/soc/xtensa/xtfpga-i2s.c
sound/sparc/cs4231.c
sound/sparc/dbri.c
sound/usb/card.c
sound/usb/endpoint.c
sound/usb/helper.c
sound/usb/usbaudio.h
tools/testing/selftests/alsa/mixer-test.c
tools/testing/selftests/alsa/pcm-test.c

diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml
new file mode 100644 (file)
index 0000000..ec888f4
--- /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/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PowerQUICC CPM QUICC Multichannel Controller (QMC)
+
+maintainers:
+  - Herve Codina <herve.codina@bootlin.com>
+
+description:
+  The QMC (QUICC Multichannel Controller) emulates up to 64 channels within one
+  serial controller using the same TDM physical interface routed from TSA.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - fsl,mpc885-scc-qmc
+          - fsl,mpc866-scc-qmc
+      - const: fsl,cpm1-scc-qmc
+
+  reg:
+    items:
+      - description: SCC (Serial communication controller) register base
+      - description: SCC parameter ram base
+      - description: Dual port ram base
+
+  reg-names:
+    items:
+      - const: scc_regs
+      - const: scc_pram
+      - const: dpram
+
+  interrupts:
+    maxItems: 1
+    description: SCC interrupt line in the CPM interrupt controller
+
+  fsl,tsa-serial:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle to TSA node
+          - enum: [1, 2, 3]
+            description: |
+              TSA serial interface (dt-bindings/soc/cpm1-fsl,tsa.h defines these
+              values)
+               - 1: SCC2
+               - 2: SCC3
+               - 3: SCC4
+    description:
+      Should be a phandle/number pair. The phandle to TSA node and the TSA
+      serial interface to use.
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+patternProperties:
+  '^channel@([0-9]|[1-5][0-9]|6[0-3])$':
+    description:
+      A channel managed by this controller
+    type: object
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 63
+        description:
+          The channel number
+
+      fsl,operational-mode:
+        $ref: /schemas/types.yaml#/definitions/string
+        enum: [transparent, hdlc]
+        default: transparent
+        description: |
+          The channel operational mode
+            - hdlc: The channel handles HDLC frames
+            - transparent: The channel handles raw data without any processing
+
+      fsl,reverse-data:
+        $ref: /schemas/types.yaml#/definitions/flag
+        description:
+          The bit order as seen on the channels is reversed,
+          transmitting/receiving the MSB of each octet first.
+          This flag is used only in 'transparent' mode.
+
+      fsl,tx-ts-mask:
+        $ref: /schemas/types.yaml#/definitions/uint64
+        description:
+          Channel assigned Tx time-slots within the Tx time-slots routed by the
+          TSA to this cell.
+
+      fsl,rx-ts-mask:
+        $ref: /schemas/types.yaml#/definitions/uint64
+        description:
+          Channel assigned Rx time-slots within the Rx time-slots routed by the
+          TSA to this cell.
+
+    required:
+      - reg
+      - fsl,tx-ts-mask
+      - fsl,rx-ts-mask
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - fsl,tsa-serial
+  - '#address-cells'
+  - '#size-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/soc/cpm1-fsl,tsa.h>
+
+    qmc@a60 {
+        compatible = "fsl,mpc885-scc-qmc", "fsl,cpm1-scc-qmc";
+        reg = <0xa60 0x20>,
+              <0x3f00 0xc0>,
+              <0x2000 0x1000>;
+        reg-names = "scc_regs", "scc_pram", "dpram";
+        interrupts = <27>;
+        interrupt-parent = <&CPM_PIC>;
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        fsl,tsa-serial = <&tsa FSL_CPM_TSA_SCC4>;
+
+        channel@16 {
+            /* Ch16 : First 4 even TS from all routed from TSA */
+            reg = <16>;
+            fsl,mode = "transparent";
+            fsl,reverse-data;
+            fsl,tx-ts-mask = <0x00000000 0x000000aa>;
+            fsl,rx-ts-mask = <0x00000000 0x000000aa>;
+        };
+
+        channel@17 {
+            /* Ch17 : First 4 odd TS from all routed from TSA */
+            reg = <17>;
+            fsl,mode = "transparent";
+            fsl,reverse-data;
+            fsl,tx-ts-mask = <0x00000000 0x00000055>;
+            fsl,rx-ts-mask = <0x00000000 0x00000055>;
+        };
+
+        channel@19 {
+            /* Ch19 : 8 TS (TS 8..15) from all routed from TSA */
+            reg = <19>;
+            fsl,mode = "hdlc";
+            fsl,tx-ts-mask = <0x00000000 0x0000ff00>;
+            fsl,rx-ts-mask = <0x00000000 0x0000ff00>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml
new file mode 100644 (file)
index 0000000..7e51c63
--- /dev/null
@@ -0,0 +1,205 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PowerQUICC CPM Time-slot assigner (TSA) controller
+
+maintainers:
+  - Herve Codina <herve.codina@bootlin.com>
+
+description:
+  The TSA is the time-slot assigner that can be found on some PowerQUICC SoC.
+  Its purpose is to route some TDM time-slots to other internal serial
+  controllers.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - fsl,mpc885-tsa
+          - fsl,mpc866-tsa
+      - const: fsl,cpm1-tsa
+
+  reg:
+    items:
+      - description: SI (Serial Interface) register base
+      - description: SI RAM base
+
+  reg-names:
+    items:
+      - const: si_regs
+      - const: si_ram
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+patternProperties:
+  '^tdm@[0-1]$':
+    description:
+      The TDM managed by this controller
+    type: object
+
+    additionalProperties: false
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 1
+        description:
+          The TDM number for this TDM, 0 for TDMa and 1 for TDMb
+
+      fsl,common-rxtx-pins:
+        $ref: /schemas/types.yaml#/definitions/flag
+        description:
+          The hardware can use four dedicated pins for Tx clock, Tx sync, Rx
+          clock and Rx sync or use only two pins, Tx/Rx clock and Tx/Rx sync.
+          Without the 'fsl,common-rxtx-pins' property, the four pins are used.
+          With the 'fsl,common-rxtx-pins' property, two pins are used.
+
+      clocks:
+        minItems: 2
+        items:
+          - description: External clock connected to L1RSYNC pin
+          - description: External clock connected to L1RCLK pin
+          - description: External clock connected to L1TSYNC pin
+          - description: External clock connected to L1TCLK pin
+
+      clock-names:
+        minItems: 2
+        items:
+          - const: l1rsync
+          - const: l1rclk
+          - const: l1tsync
+          - const: l1tclk
+
+      fsl,rx-frame-sync-delay-bits:
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Receive frame sync delay in number of bits.
+          Indicates the delay between the Rx sync and the first bit of the Rx
+          frame. 0 for no bit delay. 1, 2 or 3 for 1, 2 or 3 bits delay.
+
+      fsl,tx-frame-sync-delay-bits:
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Transmit frame sync delay in number of bits.
+          Indicates the delay between the Tx sync and the first bit of the Tx
+          frame. 0 for no bit delay. 1, 2 or 3 for 1, 2 or 3 bits delay.
+
+      fsl,clock-falling-edge:
+        $ref: /schemas/types.yaml#/definitions/flag
+        description:
+          Data is sent on falling edge of the clock (and received on the rising
+          edge). If 'clock-falling-edge' is not present, data is sent on the
+          rising edge (and received on the falling edge).
+
+      fsl,fsync-rising-edge:
+        $ref: /schemas/types.yaml#/definitions/flag
+        description:
+          Frame sync pulses are sampled with the rising edge of the channel
+          clock. If 'fsync-rising-edge' is not present, pulses are sampled with
+          the falling edge.
+
+      fsl,double-speed-clock:
+        $ref: /schemas/types.yaml#/definitions/flag
+        description:
+          The channel clock is twice the data rate.
+
+    patternProperties:
+      '^fsl,[rt]x-ts-routes$':
+        $ref: /schemas/types.yaml#/definitions/uint32-matrix
+        description: |
+          A list of tuple that indicates the Tx or Rx time-slots routes.
+        items:
+          items:
+            - description:
+                The number of time-slots
+              minimum: 1
+              maximum: 64
+            - description: |
+                The source (Tx) or destination (Rx) serial interface
+                (dt-bindings/soc/cpm1-fsl,tsa.h defines these values)
+                 - 0: No destination
+                 - 1: SCC2
+                 - 2: SCC3
+                 - 3: SCC4
+                 - 4: SMC1
+                 - 5: SMC2
+              enum: [0, 1, 2, 3, 4, 5]
+        minItems: 1
+        maxItems: 64
+
+    allOf:
+      # If fsl,common-rxtx-pins is present, only 2 clocks are needed.
+      # Else, the 4 clocks must be present.
+      - if:
+          required:
+            - fsl,common-rxtx-pins
+        then:
+          properties:
+            clocks:
+              maxItems: 2
+            clock-names:
+              maxItems: 2
+        else:
+          properties:
+            clocks:
+              minItems: 4
+            clock-names:
+              minItems: 4
+
+    required:
+      - reg
+      - clocks
+      - clock-names
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - '#address-cells'
+  - '#size-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/soc/cpm1-fsl,tsa.h>
+
+    tsa@ae0 {
+        compatible = "fsl,mpc885-tsa", "fsl,cpm1-tsa";
+        reg = <0xae0 0x10>,
+              <0xc00 0x200>;
+        reg-names = "si_regs", "si_ram";
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tdm@0 {
+            /* TDMa */
+            reg = <0>;
+
+            clocks = <&clk_l1rsynca>, <&clk_l1rclka>;
+            clock-names = "l1rsync", "l1rclk";
+
+            fsl,common-rxtx-pins;
+            fsl,fsync-rising-edge;
+
+            fsl,tx-ts-routes = <2 0>,             /* TS 0..1 */
+                           <24 FSL_CPM_TSA_SCC4>, /* TS 2..25 */
+                           <1 0>,                 /* TS 26 */
+                           <5 FSL_CPM_TSA_SCC3>;  /* TS 27..31 */
+
+            fsl,rx-ts-routes = <2 0>,             /* TS 0..1 */
+                           <24 FSL_CPM_TSA_SCC4>, /* 2..25 */
+                           <1 0>,                 /* TS 26 */
+                           <5 FSL_CPM_TSA_SCC3>;  /* TS 27..31 */
+        };
+    };
index 044bcd370d494e759467ea787d9feec580959c50..ea62e51aba90ba298babda765b738db10df974cb 100644 (file)
@@ -32,7 +32,7 @@ properties:
     maxItems: 1
 
   clock-names:
-    const: "mclk"
+    const: mclk
 
   powerdown-gpios:
     description: GPIO used for hardware power-down.
diff --git a/Documentation/devicetree/bindings/sound/adi,adau17x1.txt b/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
deleted file mode 100644 (file)
index 1447dec..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-Analog Devices ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781
-
-Required properties:
-
- - compatible:         Should contain one of the following:
-                       "adi,adau1361"
-                       "adi,adau1461"
-                       "adi,adau1761"
-                       "adi,adau1961"
-                       "adi,adau1381"
-                       "adi,adau1781"
-
- - reg:                        The i2c address. Value depends on the state of ADDR0
-                       and ADDR1, as wired in hardware.
-
-Optional properties:
- - clock-names:                If provided must be "mclk".
- - clocks:             phandle + clock-specifiers for the clock that provides
-                       the audio master clock for the device.
-
-Examples:
-#include <dt-bindings/sound/adau17x1.h>
-
-       i2c_bus {
-               adau1361@38 {
-                       compatible = "adi,adau1761";
-                       reg = <0x38>;
-
-                       clock-names = "mclk";
-                       clocks = <&audio_clock>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/sound/adi,adau17x1.yaml b/Documentation/devicetree/bindings/sound/adi,adau17x1.yaml
new file mode 100644 (file)
index 0000000..8ef1e7f
--- /dev/null
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,adau17x1.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 Codec
+
+maintainers:
+  - Lars-Peter Clausen <lars@metafoo.de>
+
+properties:
+  compatible:
+    enum:
+      - adi,adau1361
+      - adi,adau1381
+      - adi,adau1461
+      - adi,adau1761
+      - adi,adau1781
+      - adi,adau1961
+
+  reg:
+    maxItems: 1
+    description:
+      The i2c address. Value depends on the state of ADDR0 and ADDR1,
+      as wired in hardware.
+
+  clock-names:
+    const: mclk
+
+  clocks:
+    items:
+      - description: provides the audio master clock for the device.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+      audio-codec@38 {
+        compatible = "adi,adau1761";
+        reg = <0x38>;
+        clock-names = "mclk";
+        clocks = <&audio_clock>;
+      };
+    };
diff --git a/Documentation/devicetree/bindings/sound/adi,max98363.yaml b/Documentation/devicetree/bindings/sound/adi,max98363.yaml
new file mode 100644 (file)
index 0000000..a844b63
--- /dev/null
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,max98363.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX98363 SoundWire Amplifier
+
+maintainers:
+  - Ryan Lee <ryans.lee@analog.com>
+
+description:
+  The MAX98363 is a SoundWire input Class D mono amplifier that
+  supports MIPI SoundWire v1.2-compatible digital interface for
+  audio and control data.
+  SoundWire peripheral device ID of MAX98363 is 0x3*019f836300
+  where * is the peripheral device unique ID decoded from pin.
+  It supports up to 10 peripheral devices(0x0 to 0x9).
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: sdw3019f836300
+
+  reg:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    soundwire-controller@3250000 {
+        #address-cells = <2>;
+        #size-cells = <0>;
+        reg = <0x3250000 0x2000>;
+
+        speaker@0,0 {
+            compatible = "sdw3019f836300";
+            reg = <0 0>;
+            #sound-dai-cells = <0>;
+            sound-name-prefix = "Speaker Left";
+        };
+
+        speaker@0,1 {
+            compatible = "sdw3019f836300";
+            reg = <0 1>;
+            #sound-dai-cells = <0>;
+            sound-name-prefix = "Speaker Right";
+        };
+    };
index fd5aa61b467f813ed28775d3ebffe4cf3b6c7809..bdc10d4204ecc5679c31deff10ff9a3b30be08a3 100644 (file)
@@ -41,21 +41,21 @@ properties:
 
   adi,vmon-slot-no:
     description: slot number of the voltage sense monitor
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 15
     default: 0
 
   adi,imon-slot-no:
     description: slot number of the current sense monitor
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 15
     default: 1
 
   adi,spkfb-slot-no:
     description: slot number of speaker DSP monitor
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 15
     default: 2
@@ -64,7 +64,7 @@ properties:
     description:
       Selects the PCM data input channel that is routed to the speaker
       audio processing bypass path.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 15
     default: 0
diff --git a/Documentation/devicetree/bindings/sound/ak4458.txt b/Documentation/devicetree/bindings/sound/ak4458.txt
deleted file mode 100644 (file)
index 0416c14..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-AK4458 audio DAC
-
-This device supports I2C mode.
-
-Required properties:
-
-- compatible : "asahi-kasei,ak4458" or "asahi-kasei,ak4497"
-- reg : The I2C address of the device for I2C
-
-Optional properties:
-- reset-gpios: A GPIO specifier for the power down & reset pin
-- mute-gpios: A GPIO specifier for the soft mute pin
-- AVDD-supply: Analog power supply
-- DVDD-supply: Digital power supply
-- dsd-path: Select DSD input pins for ak4497
-            0: select #16, #17, #19 pins
-            1: select #3, #4, #5 pins
-
-Example:
-
-&i2c {
-       ak4458: dac@10 {
-               compatible = "asahi-kasei,ak4458";
-               reg = <0x10>;
-               reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>
-               mute-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/ak5558.txt b/Documentation/devicetree/bindings/sound/ak5558.txt
deleted file mode 100644 (file)
index e28708d..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-AK5558 8 channel differential 32-bit delta-sigma ADC
-
-This device supports I2C mode only.
-
-Required properties:
-
-- compatible : "asahi-kasei,ak5558" or "asahi-kasei,ak5552".
-- reg : The I2C address of the device.
-
-Optional properties:
-
-- reset-gpios: A GPIO specifier for the power down & reset pin.
-- AVDD-supply: Analog power supply
-- DVDD-supply: Digital power supply
-
-Example:
-
-&i2c {
-       ak5558: adc@10 {
-               compatible = "asahi-kasei,ak5558";
-               reg = <0x10>;
-               reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/alc5632.txt b/Documentation/devicetree/bindings/sound/alc5632.txt
deleted file mode 100644 (file)
index ffd886d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-ALC5632 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-  - compatible : "realtek,alc5632"
-
-  - reg : the I2C address of the device.
-
-  - gpio-controller : Indicates this device is a GPIO controller.
-
-  - #gpio-cells : Should be two. The first cell is the pin number and the
-    second cell is used to specify optional parameters (currently unused).
-
-Pins on the device (for linking into audio routes):
-
-  * SPK_OUTP
-  * SPK_OUTN
-  * HP_OUT_L
-  * HP_OUT_R
-  * AUX_OUT_P
-  * AUX_OUT_N
-  * LINE_IN_L
-  * LINE_IN_R
-  * PHONE_P
-  * PHONE_N
-  * MIC1_P
-  * MIC1_N
-  * MIC2_P
-  * MIC2_N
-  * MICBIAS1
-  * DMICDAT
-
-Example:
-
-alc5632: alc5632@1e {
-       compatible = "realtek,alc5632";
-       reg = <0x1a>;
-
-       gpio-controller;
-       #gpio-cells = <2>;
-};
diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
new file mode 100644 (file)
index 0000000..4477f84
--- /dev/null
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4458.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AK4458 audio DAC
+
+maintainers:
+  - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+properties:
+  compatible:
+    enum:
+      - asahi-kasei,ak4458
+      - asahi-kasei,ak4497
+
+  reg:
+    maxItems: 1
+
+  avdd-supply:
+    description: Analog power supply
+
+  dvdd-supply:
+    description: Digital power supply
+
+  reset-gpios:
+    maxItems: 1
+
+  mute-gpios:
+    maxItems: 1
+    description:
+      GPIO used to mute all the outputs
+
+  dsd-path:
+    description: Select DSD input pins for ak4497
+    $ref: /schemas/types.yaml#/definitions/uint32
+    oneOf:
+      - const: 0
+        description: "select #16, #17, #19 pins"
+      - const: 1
+        description: "select #3, #4, #5 pins"
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: asahi-kasei,ak4458
+
+    then:
+      properties:
+        dsd-path: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@10 {
+            compatible = "asahi-kasei,ak4458";
+            reg = <0x10>;
+            reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
+            mute-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak5558.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak5558.yaml
new file mode 100644 (file)
index 0000000..d3d494a
--- /dev/null
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/asahi-kasei,ak5558.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AK5558 8 channel differential 32-bit delta-sigma ADC
+
+maintainers:
+  - Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>
+  - Mihai Serban <mihai.serban@nxp.com>
+
+properties:
+  compatible:
+    enum:
+      - asahi-kasei,ak5552
+      - asahi-kasei,ak5558
+
+  reg:
+    maxItems: 1
+
+  avdd-supply:
+    description: A 1.8V supply that powers up the AVDD pin.
+
+  dvdd-supply:
+    description: A 1.2V supply that powers up the DVDD pin.
+
+  reset-gpios:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        ak5558: codec@10 {
+            compatible = "asahi-kasei,ak5558";
+            reg = <0x10>;
+            reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
+        };
+    };
index 6b4e02a0695ad960194edd09dd6d3853313a786c..fa9f9a853365756681d1f1d7e214b1f22a61dddc 100644 (file)
@@ -16,19 +16,19 @@ definitions:
     $ref: /schemas/graph.yaml#/$defs/port-base
     properties:
       convert-rate:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-rate
       convert-channels:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-channels
       convert-sample-format:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-format
       mclk-fs:
-        $ref: "simple-card.yaml#/definitions/mclk-fs"
+        $ref: simple-card.yaml#/definitions/mclk-fs
 
   endpoint-base:
     $ref: /schemas/graph.yaml#/$defs/endpoint-base
     properties:
       mclk-fs:
-        $ref: "simple-card.yaml#/definitions/mclk-fs"
+        $ref: simple-card.yaml#/definitions/mclk-fs
       frame-inversion:
         description: dai-link uses frame clock inversion
         $ref: /schemas/types.yaml#/definitions/flag
@@ -49,11 +49,11 @@ definitions:
         description: Indicates system clock
         $ref: /schemas/types.yaml#/definitions/phandle
       system-clock-frequency:
-        $ref: "simple-card.yaml#/definitions/system-clock-frequency"
+        $ref: simple-card.yaml#/definitions/system-clock-frequency
       system-clock-direction-out:
-        $ref: "simple-card.yaml#/definitions/system-clock-direction-out"
+        $ref: simple-card.yaml#/definitions/system-clock-direction-out
       system-clock-fixed:
-        $ref: "simple-card.yaml#/definitions/system-clock-fixed"
+        $ref: simple-card.yaml#/definitions/system-clock-fixed
 
       dai-format:
         description: audio format.
@@ -69,11 +69,11 @@ definitions:
             - msb
             - lsb
       convert-rate:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-rate
       convert-channels:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-channels
       convert-sample-format:
-        $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+        $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-format
 
       dai-tdm-slot-num:
         description: Number of slots in use.
index d59baedee180d43fc53ecb36053ef5e74a76c179..c87eb91de159324b032f4c00078b2172021d9d80 100644 (file)
@@ -15,7 +15,7 @@ properties:
   label:
     maxItems: 1
   prefix:
-    description: "device name prefix"
+    description: device name prefix
     $ref: /schemas/types.yaml#/definitions/string
   routing:
     description: |
@@ -27,11 +27,11 @@ properties:
     description: User specified audio sound widgets.
     $ref: /schemas/types.yaml#/definitions/non-unique-string-array
   convert-rate:
-    $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
+    $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-rate
   convert-channels:
-    $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+    $ref: /schemas/sound/dai-params.yaml#/$defs/dai-channels
   convert-sample-format:
-    $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+    $ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-format
 
   pa-gpios:
     maxItems: 1
index 18fb471aa891bf0a979afb3ebac6aeefd276af47..14dea1feefc5ae93458cbe06d635d61765c760ae 100644 (file)
@@ -85,11 +85,19 @@ properties:
       boost-cap-microfarad.
       External Boost must have GPIO1 as GPIO output. GPIO1 will be set high to
       enable boost voltage.
+      Shared boost allows two amplifiers to share a single boost circuit by
+      communicating on the MDSYNC bus. The active amplifier controls the boost
+      circuit using combined data from both amplifiers. GPIO1 should be
+      configured for Sync when shared boost is used. Shared boost is not
+      compatible with External boost. Active amplifier requires
+      boost-peak-milliamp, boost-ind-nanohenry and boost-cap-microfarad.
       0 = Internal Boost
       1 = External Boost
+      2 = Shared Boost Active
+      3 = Shared Boost Passive
     $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
-    maximum: 1
+    maximum: 3
 
   cirrus,gpio1-polarity-invert:
     description:
index 88a0ca474c3d7fe5b97cebbd0c8da609c72eab8f..2ab74f99568529eefa94044d13e66e401280ef82 100644 (file)
@@ -45,11 +45,79 @@ properties:
       Audio serial port SDOUT Hi-Z control. Sets the Hi-Z
       configuration for SDOUT pin of amplifier. Logical OR of
       CS35L45_ASP_TX_HIZ_xxx values.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 3
     default: 2
 
+patternProperties:
+  "^cirrus,gpio-ctrl[1-3]$":
+    description:
+      GPIO pins configuration.
+    type: object
+    additionalProperties: false
+    properties:
+      gpio-dir:
+        description:
+          GPIO pin direction. Valid only when 'gpio-ctrl' is 1
+            0 = Output
+            1 = Input
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 1
+      gpio-lvl:
+        description:
+          GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
+            0 = Low
+            1 = High
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-op-cfg:
+        description:
+          GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
+            0 = CMOS
+            1 = Open Drain
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-pol:
+        description:
+          GPIO output polarity select. Valid only when 'gpio-ctrl' is 1
+          and 'gpio-dir' is 0
+            0 = Non-inverted, Active High
+            1 = Inverted, Active Low
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-ctrl:
+        description:
+          Defines the function of the GPIO pin.
+          GPIO1
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2 = Pin acts as MDSYNC, direction controlled by MDSYNC
+            3-7 = Reserved
+          GPIO2
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2 = Pin acts as open drain INT
+            3 = Reserved
+            4 = Pin acts as push-pull output INT. Active low.
+            5 = Pin acts as push-pull output INT. Active high.
+            6,7 = Reserved
+          GPIO3
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2-7 = Reserved
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 7
+        default: 0
 required:
   - compatible
   - reg
@@ -74,5 +142,15 @@ examples:
           reset-gpios = <&gpio 110 0>;
           cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED |
                                         CS35L45_ASP_TX_HIZ_DISABLED)>;
+          cirrus,gpio-ctrl1 {
+             gpio-ctrl = <0x2>;
+          };
+          cirrus,gpio-ctrl2 {
+             gpio-ctrl = <0x2>;
+          };
+          cirrus,gpio-ctrl3 {
+             gpio-ctrl = <0x1>;
+             gpio-dir = <0x1>;
+          };
         };
     };
index 7356084a2ca2b7ff67fee9002b5ded2d564f24a2..af599d8735e26043da368b17ac73bab71460a1e1 100644 (file)
@@ -68,7 +68,7 @@ properties:
       This is "normal tip sense (TS)" in the datasheet.
 
       The CS42L42_TS_INV_* defines are available for this.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 1
 
@@ -87,7 +87,7 @@ properties:
       7 - 1.5s
 
       The CS42L42_TS_DBNCE_* defines are available for this.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 7
 
@@ -106,7 +106,7 @@ properties:
       7 - 1.5s
 
       The CS42L42_TS_DBNCE_* defines are available for this.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 7
 
@@ -120,7 +120,7 @@ properties:
 
       0ms - 200ms,
       Default = 100ms
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 200
 
@@ -133,7 +133,7 @@ properties:
 
       0ms - 20ms,
       Default = 10ms
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 20
 
@@ -169,7 +169,7 @@ properties:
       3 - Slowest
 
       The CS42L42_HSBIAS_RAMP_* defines are available for this.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     minimum: 0
     maximum: 3
 
diff --git a/Documentation/devicetree/bindings/sound/cirrus,ep9301-i2s.yaml b/Documentation/devicetree/bindings/sound/cirrus,ep9301-i2s.yaml
new file mode 100644 (file)
index 0000000..453d493
--- /dev/null
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/cirrus,ep9301-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus EP93xx I2S Controller
+
+description: |
+  The I2S controller is used to stream serial audio data between the external
+  I2S CODECs’, ADCs/DACs, and the ARM Core. The controller supports I2S, Left-
+  and Right-Justified DSP formats.
+
+maintainers:
+  - Alexander Sverdlin <alexander.sverdlin@gmail.com>
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: cirrus,ep9301-i2s
+
+  '#sound-dai-cells':
+    const: 0
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    minItems: 3
+    maxItems: 3
+
+  clock-names:
+    items:
+      - const: mclk
+      - const: sclk
+      - const: lrclk
+
+required:
+  - compatible
+  - '#sound-dai-cells'
+  - reg
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    i2s: i2s@80820000 {
+        compatible = "cirrus,ep9301-i2s";
+        #sound-dai-cells = <0>;
+        reg = <0x80820000 0x100>;
+        interrupt-parent = <&vic1>;
+        interrupts = <28>;
+        clocks = <&syscon 29>,
+                 <&syscon 30>,
+                 <&syscon 31>;
+        clock-names = "mclk", "sclk", "lrclk";
+    };
+
+...
index 9f2b111818ea418565acd1a171860d74ff0c866b..b6079b3c440d54566321b47df8afdef2b738b745 100644 (file)
@@ -28,6 +28,10 @@ properties:
     items:
       - const: mclk
 
+  port:
+    $ref: audio-graph-port.yaml#
+    unevaluatedProperties: false
+
   "#sound-dai-cells":
     const: 0
 
diff --git a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml
new file mode 100644 (file)
index 0000000..ff5cd92
--- /dev/null
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl,qmc-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: QMC audio
+
+maintainers:
+  - Herve Codina <herve.codina@bootlin.com>
+
+description: |
+  The QMC audio is an ASoC component which uses QMC (QUICC Multichannel
+  Controller) channels to transfer the audio data.
+  It provides as many DAI as the number of QMC channel used.
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: fsl,qmc-audio
+
+  '#address-cells':
+    const: 1
+  '#size-cells':
+    const: 0
+  '#sound-dai-cells':
+    const: 1
+
+patternProperties:
+  '^dai@([0-9]|[1-5][0-9]|6[0-3])$':
+    description:
+      A DAI managed by this controller
+    type: object
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 63
+        description:
+          The DAI number
+
+      fsl,qmc-chan:
+        $ref: /schemas/types.yaml#/definitions/phandle-array
+        items:
+          - items:
+              - description: phandle to QMC node
+              - description: Channel number
+        description:
+          Should be a phandle/number pair. The phandle to QMC node and the QMC
+          channel to use for this DAI.
+
+    required:
+      - reg
+      - fsl,qmc-chan
+
+required:
+  - compatible
+  - '#address-cells'
+  - '#size-cells'
+  - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    audio_controller: audio-controller {
+        compatible = "fsl,qmc-audio";
+        #address-cells = <1>;
+        #size-cells = <0>;
+        #sound-dai-cells = <1>;
+        dai@16 {
+            reg = <16>;
+            fsl,qmc-chan = <&qmc 16>;
+        };
+        dai@17 {
+            reg = <17>;
+            fsl,qmc-chan = <&qmc 17>;
+        };
+    };
+
+    sound {
+        compatible = "simple-audio-card";
+        #address-cells = <1>;
+        #size-cells = <0>;
+        simple-audio-card,dai-link@0 {
+            reg = <0>;
+            format = "dsp_b";
+            cpu {
+                sound-dai = <&audio_controller 16>;
+            };
+            codec {
+                sound-dai = <&codec1>;
+                dai-tdm-slot-num = <4>;
+                dai-tdm-slot-width = <8>;
+                /* TS 3, 5, 7, 9 */
+                dai-tdm-slot-tx-mask = <0 0 0 1 0 1 0 1 0 1>;
+                dai-tdm-slot-rx-mask = <0 0 0 1 0 1 0 1 0 1>;
+            };
+        };
+        simple-audio-card,dai-link@1 {
+            reg = <1>;
+            format = "dsp_b";
+            cpu {
+                sound-dai = <&audio_controller 17>;
+            };
+            codec {
+                sound-dai = <&codec2>;
+                dai-tdm-slot-num = <4>;
+                dai-tdm-slot-width = <8>;
+                /* TS 2, 4, 6, 8 */
+                dai-tdm-slot-tx-mask = <0 0 1 0 1 0 1 0 1>;
+                dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/max98371.txt b/Documentation/devicetree/bindings/sound/max98371.txt
deleted file mode 100644 (file)
index 8b2b270..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-max98371 codec
-
-This device supports I2C mode only.
-
-Required properties:
-
-- compatible : "maxim,max98371"
-- reg : The chip select number on the I2C bus
-
-Example:
-
-&i2c {
-       max98371: max98371@31 {
-               compatible = "maxim,max98371";
-               reg = <0x31>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/max9867.txt b/Documentation/devicetree/bindings/sound/max9867.txt
deleted file mode 100644 (file)
index b8bd914..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-max9867 codec
-
-This device supports I2C mode only.
-
-Required properties:
-
-- compatible : "maxim,max9867"
-- reg : The chip select number on the I2C bus
-
-Example:
-
-&i2c {
-       max9867: max9867@18 {
-               compatible = "maxim,max9867";
-               reg = <0x18>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/maxim,max9759.txt b/Documentation/devicetree/bindings/sound/maxim,max9759.txt
deleted file mode 100644 (file)
index 737a996..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-Maxim MAX9759 Speaker Amplifier
-===============================
-
-Required properties:
-- compatible : "maxim,max9759"
-- shutdown-gpios : the gpio connected to the shutdown pin
-- mute-gpios : the gpio connected to the mute pin
-- gain-gpios : the 2 gpios connected to the g1 and g2 pins
-
-Example:
-
-max9759: analog-amplifier {
-       compatible = "maxim,max9759";
-       shutdown-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>;
-       mute-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
-       gain-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>,
-                    <&gpio3 25 GPIO_ACTIVE_LOW>;
-};
diff --git a/Documentation/devicetree/bindings/sound/maxim,max9759.yaml b/Documentation/devicetree/bindings/sound/maxim,max9759.yaml
new file mode 100644 (file)
index 0000000..a76ee6a
--- /dev/null
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max9759.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim MAX9759 Speaker Amplifier
+
+maintainers:
+  - Otabek Nazrullaev <otabeknazrullaev1998@gmail.com>
+
+properties:
+  compatible:
+    const: maxim,max9759
+
+  shutdown-gpios:
+    maxItems: 1
+    description: the gpio connected to the shutdown pin
+
+  mute-gpios:
+    maxItems: 1
+    description: the gpio connected to the mute pin
+
+  gain-gpios:
+    maxItems: 2
+    description: the 2 gpios connected to the g1 and g2 pins
+
+required:
+  - compatible
+  - shutdown-gpios
+  - mute-gpios
+  - gain-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    amplifier {
+      compatible = "maxim,max9759";
+      shutdown-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>;
+      mute-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
+      gain-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>,
+             <&gpio3 25 GPIO_ACTIVE_LOW>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98371.yaml b/Documentation/devicetree/bindings/sound/maxim,max98371.yaml
new file mode 100644 (file)
index 0000000..14fba34
--- /dev/null
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max98371.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim MAX98371 audio codec
+
+maintainers:
+  - anish kumar <yesanishhere@gmail.com>
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: maxim,max98371
+
+  '#sound-dai-cells':
+    const: 0
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@31 {
+            compatible = "maxim,max98371";
+            reg = <0x31>;
+            #sound-dai-cells = <0>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/maxim,max9867.yaml b/Documentation/devicetree/bindings/sound/maxim,max9867.yaml
new file mode 100644 (file)
index 0000000..0b9a84d
--- /dev/null
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max9867.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX9867 CODEC
+
+description: |
+  This device supports I2C only.
+  Pins on the device (for linking into audio routes):
+      * LOUT
+      * ROUT
+      * LINL
+      * LINR
+      * MICL
+      * MICR
+      * DMICL
+      * DMICR
+
+maintainers:
+  - Ladislav Michl <ladis@linux-mips.org>
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - maxim,max9867
+
+  '#sound-dai-cells':
+    const: 0
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@18 {
+            compatible = "maxim,max9867";
+            #sound-dai-cells = <0>;
+            reg = <0x18>;
+            clocks = <&codec_clk>;
+        };
+    };
+...
index 88f82d09644316ca3ff1388e7b53592a6273438e..7fe85b08f9dfa03caf4cf1e81f831b02a28cc05c 100644 (file)
@@ -26,15 +26,15 @@ properties:
     const: audiosys
 
   mediatek,apmixedsys:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek apmixedsys controller
 
   mediatek,infracfg:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek infracfg controller
 
   mediatek,topckgen:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek topckgen controller
 
   clocks:
index d427f7f623db5999cbcb541092a07a6cdcc3c3ef..9853c11a1330b8bc06433543153096cfac9c63f2 100644 (file)
@@ -18,7 +18,7 @@ properties:
       - mediatek,mt8186-mt6366-da7219-max98357-sound
 
   mediatek,platform:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8186 ASoC platform.
 
   headset-codec:
index aa23b0024c461d596f81071a70833daa565c7358..d80083df03eb038db257c8456ebb8fa2f3f59032 100644 (file)
@@ -19,7 +19,7 @@ properties:
       - mediatek,mt8186-mt6366-rt5682s-max98360-sound
 
   mediatek,platform:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8186 ASoC platform.
 
   dmic-gpios:
index 7a25bc9b80602d3671d83c5a31e84de072daf4b3..064ef172bef4c333f6efe3d7efeffcfa6b941358 100644 (file)
@@ -24,15 +24,15 @@ properties:
     const: audiosys
 
   mediatek,apmixedsys:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek apmixedsys controller
 
   mediatek,infracfg:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek infracfg controller
 
   mediatek,topckgen:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek topckgen controller
 
   power-domains:
index c6e614c1c30b94b43f4fcc666042b7349ce3e0a7..7e50f5d65c8fbd235be00af5cbd7a309b7ae15bd 100644 (file)
@@ -21,11 +21,11 @@ properties:
       - mediatek,mt8192_mt6359_rt1015p_rt5682s
 
   mediatek,platform:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8192 ASoC platform.
 
   mediatek,hdmi-codec:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of HDMI codec.
 
   headset-codec:
index 4452a4070eff8a4e15e62fa90ff5665285bab45e..d5adf07d46e0a5ae8ebb14f1face09344f3ae852 100644 (file)
@@ -32,7 +32,7 @@ properties:
       See ../reserved-memory/reserved-memory.txt for details.
 
   mediatek,topckgen:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of the mediatek topckgen controller
 
   power-domains:
index ad3447ff8b2cfed10545c6aeaa06322fa77f6ffa..c1ddbf672ca3fba06086a278bc68271bc0fa1420 100644 (file)
@@ -24,19 +24,19 @@ properties:
     description: User specified audio sound card name
 
   mediatek,platform:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8195 ASoC platform.
 
   mediatek,dptx-codec:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8195 Display Port Tx codec node.
 
   mediatek,hdmi-codec:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8195 HDMI codec node.
 
   mediatek,adsp:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
     description: The phandle of MT8195 ADSP platform.
 
   mediatek,dai-link:
index cb861aca8d40f716659a6b0ad5e3cb61520601da..a9c34526f4cb0b078a6a7a4e1ecaa6fd7adc35dc 100644 (file)
@@ -74,6 +74,9 @@ Optional properties:
   - nuvoton,adcout-drive-strong: make the drive strength of ADCOUT IO PIN strong if set.
       Otherwise, the drive keeps normal strength.
 
+  - nuvoton,adc-delay-ms: Delay (in ms) to make input path stable and avoid pop noise. The
+      default value is 125 and range between 125 to 500 ms.
+
   - clocks: list of phandle and clock specifier pairs according to common clock bindings for the
       clocks described in clock-names
   - clock-names: should include "mclk" for the MCLK master clock
index 7ef774910e5cd2349bb1fa6692747dedb9814a33..96f2f927a6f591f266164dc83219f601e161854e 100644 (file)
@@ -31,10 +31,10 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headset Stereophone"
-        - "Int Spk"
-        - "Headset Mic"
-        - "Digital Mic"
+        - Headset Stereophone
+        - Int Spk
+        - Headset Mic
+        - Digital Mic
 
         # CODEC Pins
         - SPKOUT
index 82801b4f46dd6265cefbdecdd703d5128689203c..7c1e9895ce858b2539affa1c1ca8e34a2cb4eda4 100644 (file)
@@ -80,4 +80,8 @@ properties:
     type: boolean
     description: The Mic Jack represents state of the headset microphone pin
 
+  nvidia,coupled-mic-hp-det:
+    type: boolean
+    description: The Mic detect GPIO is viable only if HP detect GPIO is active
+
 additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max9808x.yaml
new file mode 100644 (file)
index 0000000..fc89dbd
--- /dev/null
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-max9808x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra audio complex with MAX9808x CODEC
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+allOf:
+  - $ref: nvidia,tegra-audio-common.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - pattern: '^[a-z0-9]+,tegra-audio-max98088(-[a-z0-9]+)+$'
+          - const: nvidia,tegra-audio-max98088
+      - items:
+          - pattern: '^[a-z0-9]+,tegra-audio-max98089(-[a-z0-9]+)+$'
+          - const: nvidia,tegra-audio-max98089
+
+  nvidia,audio-routing:
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    description: |
+      A list of the connections between audio components.
+      Each entry is a pair of strings, the first being the connection's sink,
+      the second being the connection's source. Valid names for sources and
+      sinks are the pins (documented in the binding document),
+      and the jacks on the board.
+    minItems: 2
+    items:
+      enum:
+        # Board Connectors
+        - "Int Spk"
+        - "Headphone Jack"
+        - "Earpiece"
+        - "Headset Mic"
+        - "Internal Mic 1"
+        - "Internal Mic 2"
+
+        # CODEC Pins
+        - HPL
+        - HPR
+        - SPKL
+        - SPKR
+        - RECL
+        - RECR
+        - INA1
+        - INA2
+        - INB1
+        - INB2
+        - MIC1
+        - MIC2
+        - MICBIAS
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/tegra30-car.h>
+    #include <dt-bindings/soc/tegra-pmc.h>
+    sound {
+        compatible = "lge,tegra-audio-max98089-p895",
+                     "nvidia,tegra-audio-max98089";
+        nvidia,model = "LG Optimus Vu MAX98089";
+
+        nvidia,audio-routing =
+            "Headphone Jack", "HPL",
+            "Headphone Jack", "HPR",
+            "Int Spk", "SPKL",
+            "Int Spk", "SPKR",
+            "Earpiece", "RECL",
+            "Earpiece", "RECR",
+            "INA1", "Headset Mic",
+            "MIC1", "MICBIAS",
+            "MICBIAS", "Internal Mic 1",
+            "MIC2", "Internal Mic 2";
+
+        nvidia,i2s-controller = <&tegra_i2s0>;
+        nvidia,audio-codec = <&codec>;
+
+        clocks = <&tegra_car TEGRA30_CLK_PLL_A>,
+                 <&tegra_car TEGRA30_CLK_PLL_A_OUT0>,
+                 <&tegra_pmc TEGRA_PMC_CLK_OUT_1>;
+        clock-names = "pll_a", "pll_a_out0", "mclk";
+    };
index ccc2ee77ca3061f8e7a8b3ac8dca2375d2fa1b0a..4d912458b18ba072dbe37569c6baea7c045e1da3 100644 (file)
@@ -38,10 +38,10 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphones"
-        - "Speakers"
-        - "Mic Jack"
-        - "Int Mic"
+        - Headphones
+        - Speakers
+        - Mic Jack
+        - Int Mic
 
         # CODEC Pins
         - MIC1
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5631.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5631.yaml
new file mode 100644 (file)
index 0000000..a044870
--- /dev/null
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-rt5631.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra audio complex with RT5631 CODEC
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+allOf:
+  - $ref: nvidia,tegra-audio-common.yaml#
+
+properties:
+  compatible:
+    items:
+      - pattern: '^[a-z0-9]+,tegra-audio-rt5631(-[a-z0-9]+)+$'
+      - const: nvidia,tegra-audio-rt5631
+
+  nvidia,audio-routing:
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    description: |
+      A list of the connections between audio components.
+      Each entry is a pair of strings, the first being the connection's sink,
+      the second being the connection's source. Valid names for sources and
+      sinks are the pins (documented in the binding document),
+      and the jacks on the board.
+    minItems: 2
+    items:
+      enum:
+        # Board Connectors
+        - "Int Spk"
+        - "Headphone Jack"
+        - "Mic Jack"
+        - "Int Mic"
+
+        # CODEC Pins
+        - MIC1
+        - MIC2
+        - AXIL
+        - AXIR
+        - MONOIN_RXN
+        - MONOIN_RXP
+        - DMIC
+        - MIC Bias1
+        - MIC Bias2
+        - MONO_IN
+        - AUXO1
+        - AUXO2
+        - SPOL
+        - SPOR
+        - HPOL
+        - HPOR
+        - MONO
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/tegra30-car.h>
+    #include <dt-bindings/soc/tegra-pmc.h>
+    sound {
+        compatible = "asus,tegra-audio-rt5631-tf700t",
+                     "nvidia,tegra-audio-rt5631";
+        nvidia,model = "Asus Transformer Infinity TF700T RT5631";
+
+        nvidia,audio-routing =
+                "Headphone Jack", "HPOL",
+                "Headphone Jack", "HPOR",
+                "Int Spk", "SPOL",
+                "Int Spk", "SPOR",
+                "MIC1", "MIC Bias1",
+                "MIC Bias1", "Mic Jack",
+                "DMIC", "Int Mic";
+
+        nvidia,i2s-controller = <&tegra_i2s1>;
+        nvidia,audio-codec = <&rt5631>;
+
+        clocks = <&tegra_car TEGRA30_CLK_PLL_A>,
+                 <&tegra_car TEGRA30_CLK_PLL_A_OUT0>,
+                 <&tegra_pmc TEGRA_PMC_CLK_OUT_1>;
+        clock-names = "pll_a", "pll_a_out0", "mclk";
+    };
index b1deaf271afa81ffd93c5c4ca74d2791e20ba0f8..2638592435b218274a752966734c95bed62e97c5 100644 (file)
@@ -31,9 +31,9 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphones"
-        - "Speakers"
-        - "Mic Jack"
+        - Headphones
+        - Speakers
+        - Mic Jack
 
         # CODEC Pins
         - DMIC1
index a49997d6028bf80b9fdf9d37e6ce52c8ee7653a7..09e1d0b18d276b4c82be8d481b46d8e6f0155bda 100644 (file)
@@ -31,11 +31,11 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphone"
-        - "Speaker"
-        - "Headset Mic"
-        - "Internal Mic 1"
-        - "Internal Mic 2"
+        - Headphone
+        - Speaker
+        - Headset Mic
+        - Internal Mic 1
+        - Internal Mic 2
 
         # CODEC Pins
         - IN1P
@@ -47,14 +47,14 @@ properties:
         - DMIC2
         - DMIC3
         - DMIC4
-        - "DMIC L1"
-        - "DMIC L2"
-        - "DMIC L3"
-        - "DMIC L4"
-        - "DMIC R1"
-        - "DMIC R2"
-        - "DMIC R3"
-        - "DMIC R4"
+        - DMIC L1
+        - DMIC L2
+        - DMIC L3
+        - DMIC L4
+        - DMIC R1
+        - DMIC R2
+        - DMIC R3
+        - DMIC R4
         - LOUT1
         - LOUT2
         - LOUT3
index 943e7c01741c70fc0ae8d762efd88eab5779a20b..e5bc6a6ade24680ab9f5a08040b26b8157a9f67f 100644 (file)
@@ -31,9 +31,9 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphone Jack"
-        - "Line In Jack"
-        - "Mic Jack"
+        - Headphone Jack
+        - Line In Jack
+        - Mic Jack
 
         # CODEC Pins
         - HP_OUT
index a5b431d7d0c2e5f24462fc07c489ec026b170bcc..3323d6a438f5cefa5dc910ab9c30c09b53ffd6b2 100644 (file)
@@ -31,8 +31,8 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphone Jack"
-        - "Mic Jack"
+        - Headphone Jack
+        - Mic Jack
 
         # CODEC Pins
         - LOUT1
@@ -53,7 +53,7 @@ properties:
         - MIC1
         - MIC2N
         - MIC2
-        - "Mic Bias"
+        - Mic Bias
 
 required:
   - nvidia,i2s-controller
index 1b836acab98096fc16c663248c629794d11c65ba..1be25ce4514b7a22c20e4b709f40a8709eb37e67 100644 (file)
@@ -35,10 +35,10 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphone Jack"
-        - "Int Spk"
-        - "Mic Jack"
-        - "Int Mic"
+        - Headphone Jack
+        - Int Spk
+        - Mic Jack
+        - Int Mic
 
         # CODEC Pins
         - IN1L
index a1448283344ba624a82c6eeefff67158d7a75d86..397306b8800d484a08ebe3e7aab27ebbd77fadc0 100644 (file)
@@ -31,9 +31,9 @@ properties:
     items:
       enum:
         # Board Connectors
-        - "Headphone"
-        - "LineIn"
-        - "Mic"
+        - Headphone
+        - LineIn
+        - Mic
 
         # CODEC Pins
         - MONOOUT
@@ -48,7 +48,7 @@ properties:
         - PCBEEP
         - MIC1
         - MIC2
-        - "Mic Bias"
+        - Mic Bias
 
 required:
   - nvidia,ac97-controller
index e6fcf542cf878a93c4cb7cf9f952b5f912406a05..ec4b0ac8ad68c0589f6ffc11127e89421ea41798 100644 (file)
@@ -9,15 +9,13 @@ title: LPASS(Low Power Audio Subsystem) RX Macro audio codec
 maintainers:
   - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 
-allOf:
-  - $ref: dai-common.yaml#
-
 properties:
   compatible:
     enum:
       - qcom,sc7280-lpass-rx-macro
       - qcom,sm8250-lpass-rx-macro
       - qcom,sm8450-lpass-rx-macro
+      - qcom,sm8550-lpass-rx-macro
       - qcom,sc8280xp-lpass-rx-macro
 
   reg:
@@ -30,20 +28,12 @@ properties:
     const: 0
 
   clocks:
+    minItems: 3
     maxItems: 5
 
   clock-names:
-    oneOf:
-      - items:   # for ADSP based platforms
-          - const: mclk
-          - const: npl
-          - const: macro
-          - const: dcodec
-          - const: fsgen
-      - items:   # for ADSP bypass based platforms
-          - const: mclk
-          - const: npl
-          - const: fsgen
+    minItems: 3
+    maxItems: 5
 
   clock-output-names:
     maxItems: 1
@@ -61,6 +51,65 @@ required:
   - reg
   - "#sound-dai-cells"
 
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sc7280-lpass-rx-macro
+    then:
+      properties:
+        clock-names:
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: npl
+                - const: macro
+                - const: dcodec
+                - const: fsgen
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
+                - const: npl
+                - const: fsgen
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sc8280xp-lpass-rx-macro
+            - qcom,sm8250-lpass-rx-macro
+            - qcom,sm8450-lpass-rx-macro
+    then:
+      properties:
+        clocks:
+          minItems: 5
+          maxItems: 5
+        clock-names:
+          items:
+            - const: mclk
+            - const: npl
+            - const: macro
+            - const: dcodec
+            - const: fsgen
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sm8550-lpass-rx-macro
+    then:
+      properties:
+        clocks:
+          minItems: 4
+          maxItems: 4
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
+            - const: fsgen
+
 unevaluatedProperties: false
 
 examples:
index 6c8751497d36fbb41f3c739f1ec17875eb3aa932..4156981fe02b6f68c9eda7222bde9e73400b9844 100644 (file)
@@ -9,15 +9,13 @@ title: LPASS(Low Power Audio Subsystem) TX Macro audio codec
 maintainers:
   - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 
-allOf:
-  - $ref: dai-common.yaml#
-
 properties:
   compatible:
     enum:
       - qcom,sc7280-lpass-tx-macro
       - qcom,sm8250-lpass-tx-macro
       - qcom,sm8450-lpass-tx-macro
+      - qcom,sm8550-lpass-tx-macro
       - qcom,sc8280xp-lpass-tx-macro
 
   reg:
@@ -30,22 +28,12 @@ properties:
     const: 0
 
   clocks:
-    oneOf:
-      - maxItems: 3
-      - maxItems: 5
+    minItems: 3
+    maxItems: 5
 
   clock-names:
-    oneOf:
-      - items:   # for ADSP based platforms
-          - const: mclk
-          - const: npl
-          - const: macro
-          - const: dcodec
-          - const: fsgen
-      - items:   # for ADSP bypass based platforms
-          - const: mclk
-          - const: npl
-          - const: fsgen
+    minItems: 3
+    maxItems: 5
 
   clock-output-names:
     maxItems: 1
@@ -67,6 +55,65 @@ required:
   - reg
   - "#sound-dai-cells"
 
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sc7280-lpass-tx-macro
+    then:
+      properties:
+        clock-names:
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: npl
+                - const: macro
+                - const: dcodec
+                - const: fsgen
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
+                - const: npl
+                - const: fsgen
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sc8280xp-lpass-tx-macro
+            - qcom,sm8250-lpass-tx-macro
+            - qcom,sm8450-lpass-tx-macro
+    then:
+      properties:
+        clocks:
+          minItems: 5
+          maxItems: 5
+        clock-names:
+          items:
+            - const: mclk
+            - const: npl
+            - const: macro
+            - const: dcodec
+            - const: fsgen
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sm8550-lpass-tx-macro
+    then:
+      properties:
+        clocks:
+          minItems: 4
+          maxItems: 4
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
+            - const: fsgen
+
 unevaluatedProperties: false
 
 examples:
index 61cdfc265b0fb66d3a6a9575a23044e883f8d80a..4a56108c444b8ac52a4c3a16fa0ebbe36b4c94b6 100644 (file)
@@ -9,15 +9,13 @@ title: LPASS(Low Power Audio Subsystem) VA Macro audio codec
 maintainers:
   - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 
-allOf:
-  - $ref: dai-common.yaml#
-
 properties:
   compatible:
     enum:
       - qcom,sc7280-lpass-va-macro
       - qcom,sm8250-lpass-va-macro
       - qcom,sm8450-lpass-va-macro
+      - qcom,sm8550-lpass-va-macro
       - qcom,sc8280xp-lpass-va-macro
 
   reg:
@@ -30,16 +28,12 @@ properties:
     const: 0
 
   clocks:
-    maxItems: 3
+    minItems: 1
+    maxItems: 4
 
   clock-names:
-    oneOf:
-      - items:   # for ADSP based platforms
-          - const: mclk
-          - const: macro
-          - const: dcodec
-      - items:   # for ADSP bypass based platforms
-          - const: mclk
+    minItems: 1
+    maxItems: 4
 
   clock-output-names:
     maxItems: 1
@@ -63,6 +57,76 @@ required:
   - compatible
   - reg
   - "#sound-dai-cells"
+  - clock-names
+  - clocks
+
+allOf:
+  - $ref: dai-common.yaml#
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sc7280-lpass-va-macro
+    then:
+      properties:
+        clocks:
+          maxItems: 1
+        clock-names:
+          items:
+            - const: mclk
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8250-lpass-va-macro
+    then:
+      properties:
+        clocks:
+          minItems: 3
+          maxItems: 3
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sc8280xp-lpass-va-macro
+              - qcom,sm8450-lpass-va-macro
+    then:
+      properties:
+        clocks:
+          minItems: 4
+          maxItems: 4
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
+            - const: npl
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sm8550-lpass-va-macro
+    then:
+      properties:
+        clocks:
+          minItems: 3
+          maxItems: 3
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
 
 unevaluatedProperties: false
 
index 66cbb1f5e31a5cdaf53126972b1f32c4122fed67..eea7609d1b33490cdb85a204145348109023d4ef 100644 (file)
@@ -15,6 +15,7 @@ properties:
       - qcom,sc7280-lpass-wsa-macro
       - qcom,sm8250-lpass-wsa-macro
       - qcom,sm8450-lpass-wsa-macro
+      - qcom,sm8550-lpass-wsa-macro
       - qcom,sc8280xp-lpass-wsa-macro
 
   reg:
@@ -27,11 +28,11 @@ properties:
     const: 0
 
   clocks:
-    minItems: 5
+    minItems: 4
     maxItems: 6
 
   clock-names:
-    minItems: 5
+    minItems: 4
     maxItems: 6
 
   clock-output-names:
@@ -62,6 +63,7 @@ allOf:
     then:
       properties:
         clocks:
+          minItems: 5
           maxItems: 5
         clock-names:
           items:
@@ -89,6 +91,23 @@ allOf:
             - const: va
             - const: fsgen
 
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sm8550-lpass-wsa-macro
+    then:
+      properties:
+        clocks:
+          minItems: 4
+          maxItems: 4
+        clock-names:
+          items:
+            - const: mclk
+            - const: macro
+            - const: dcodec
+            - const: fsgen
+
 unevaluatedProperties: false
 
 examples:
index 0110b38f6de97f099dbdca9b74e34ebd82f5a054..ce811942a9f17da45dc02ddcee342165dffc4a6b 100644 (file)
@@ -56,7 +56,7 @@ patternProperties:
           Compress offload dai.
 
     dependencies:
-      is-compress-dai: ["direction"]
+      is-compress-dai: [ direction ]
 
     required:
       - reg
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt b/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt
deleted file mode 100644 (file)
index 1f75fee..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-QCOM WCD9335 Codec
-
-Qualcomm WCD9335 Codec is a standalone Hi-Fi audio codec IC, supports
-Qualcomm Technologies, Inc. (QTI) multimedia solutions, including
-the MSM8996, MSM8976, and MSM8956 chipsets. It has in-built
-Soundwire controller, interrupt mux. It supports both I2S/I2C and
-SLIMbus audio interfaces.
-
-Required properties with SLIMbus Interface:
-
-- compatible:
-       Usage: required
-       Value type: <stringlist>
-       Definition: For SLIMbus interface it should be "slimMID,PID",
-                   textual representation of Manufacturer ID, Product Code,
-                   shall be in lower case hexadecimal with leading zeroes
-                   suppressed.  Refer to slimbus/bus.txt for details.
-                   Should be:
-                   "slim217,1a0" for MSM8996 and APQ8096 SoCs with SLIMbus.
-
-- reg
-       Usage: required
-       Value type: <u32 u32>
-       Definition: Should be ('Device index', 'Instance ID')
-
-- interrupts
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: Interrupts via WCD INTR1 and INTR2 pins
-
-- interrupt-names:
-       Usage: required
-       Value type: <String array>
-       Definition: Interrupt names of WCD INTR1 and INTR2
-       Should be: "intr1", "intr2"
-
-- reset-gpios:
-       Usage: required
-       Value type: <String Array>
-       Definition: Reset gpio line
-
-- slim-ifc-dev:
-       Usage: required
-       Value type: <phandle>
-       Definition: SLIM interface device
-
-- clocks:
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: See clock-bindings.txt section "consumers". List of
-                three clock specifiers for mclk, mclk2 and slimbus clock.
-
-- clock-names:
-       Usage: required
-       Value type: <string>
-       Definition: Must contain "mclk", "mclk2" and "slimbus" strings.
-
-- vdd-buck-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the 1.8V buck supply
-
-- vdd-buck-sido-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the 1.8V SIDO buck supply
-
-- vdd-rx-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the 1.8V rx supply
-
-- vdd-tx-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the 1.8V tx supply
-
-- vdd-vbat-supply:
-       Usage: Optional
-       Value type: <phandle>
-       Definition: Should contain a reference to the vbat supply
-
-- vdd-micbias-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the micbias supply
-
-- vdd-io-supply:
-       Usage: required
-       Value type: <phandle>
-       Definition: Should contain a reference to the 1.8V io supply
-
-- interrupt-controller:
-       Usage: required
-       Definition: Indicating that this is a interrupt controller
-
-- #interrupt-cells:
-       Usage: required
-       Value type: <int>
-       Definition: should be 1
-
-#sound-dai-cells
-       Usage: required
-       Value type: <u32>
-       Definition: Must be 1
-
-audio-codec@1{
-       compatible = "slim217,1a0";
-       reg  = <1 0>;
-       interrupts = <&msmgpio 54 IRQ_TYPE_LEVEL_HIGH>;
-       interrupt-names = "intr2"
-       reset-gpios = <&msmgpio 64 GPIO_ACTIVE_LOW>;
-       slim-ifc-dev  = <&wc9335_ifd>;
-       clock-names = "mclk", "native";
-       clocks = <&rpmcc RPM_SMD_DIV_CLK1>,
-                <&rpmcc RPM_SMD_BB_CLK1>;
-       vdd-buck-supply = <&pm8994_s4>;
-       vdd-rx-supply = <&pm8994_s4>;
-       vdd-buck-sido-supply = <&pm8994_s4>;
-       vdd-tx-supply = <&pm8994_s4>;
-       vdd-io-supply = <&pm8994_s4>;
-       #sound-dai-cells = <1>;
-}
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9335.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd9335.yaml
new file mode 100644 (file)
index 0000000..34f8fe4
--- /dev/null
@@ -0,0 +1,156 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wcd9335.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm WCD9335 Audio Codec
+
+maintainers:
+  - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description:
+  Qualcomm WCD9335 Codec is a standalone Hi-Fi audio codec IC with in-built
+  Soundwire controller and interrupt mux. It supports both I2S/I2C and SLIMbus
+  audio interfaces.
+
+properties:
+  compatible:
+    const: slim217,1a0
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 2
+
+  clock-names:
+    items:
+      - const: mclk
+      - const: slimbus
+
+  interrupts:
+    maxItems: 2
+
+  interrupt-names:
+    items:
+      - const: intr1
+      - const: intr2
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  slim-ifc-dev:
+    description: SLIM IFC device interface
+    $ref: /schemas/types.yaml#/definitions/phandle
+
+  '#sound-dai-cells':
+    const: 1
+
+  vdd-buck-supply:
+    description: 1.8V buck supply
+
+  vdd-buck-sido-supply:
+    description: 1.8V SIDO buck supply
+
+  vdd-io-supply:
+    description: 1.8V I/O supply
+
+  vdd-micbias-supply:
+    description: micbias supply
+
+  vdd-rx-supply:
+    description: 1.8V rx supply
+
+  vdd-tx-supply:
+    description: 1.8V tx supply
+
+  vdd-vbat-supply:
+    description: vbat supply
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      required:
+        - slim-ifc-dev
+    then:
+      required:
+        - clocks
+        - clock-names
+        - interrupts
+        - interrupt-names
+        - interrupt-controller
+        - '#interrupt-cells'
+        - reset-gpios
+        - slim-ifc-dev
+        - '#sound-dai-cells'
+        - vdd-buck-supply
+        - vdd-buck-sido-supply
+        - vdd-io-supply
+        - vdd-rx-supply
+        - vdd-tx-supply
+    else:
+      properties:
+        clocks: false
+        clock-names: false
+        interrupts: false
+        interrupt-names: false
+        interrupt-controller: false
+        '#interrupt-cells': false
+        reset-gpios: false
+        slim-ifc-dev: false
+        '#sound-dai-cells': false
+        vdd-buck-supply: false
+        vdd-buck-sido-supply: false
+        vdd-io-supply: false
+        vdd-micbias-supply: false
+        vdd-rx-supply: false
+        vdd-tx-supply: false
+        vdd-vbat-supply: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,rpmcc.h>
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    tasha_ifd: codec@0,0 {
+        compatible = "slim217,1a0";
+        reg = <0 0>;
+    };
+
+    codec@1,0 {
+        compatible = "slim217,1a0";
+        reg = <1 0>;
+
+        clock-names = "mclk", "slimbus";
+        clocks = <&div1_mclk>, <&rpmcc RPM_SMD_BB_CLK1>;
+
+        interrupt-parent = <&tlmm>;
+        interrupts = <54 IRQ_TYPE_LEVEL_HIGH>,
+                     <53 IRQ_TYPE_LEVEL_HIGH>;
+        interrupt-names = "intr1", "intr2";
+        interrupt-controller;
+        #interrupt-cells = <1>;
+
+        reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>;
+        slim-ifc-dev = <&tasha_ifd>;
+        #sound-dai-cells = <1>;
+
+        vdd-buck-supply = <&vreg_s4a_1p8>;
+        vdd-buck-sido-supply = <&vreg_s4a_1p8>;
+        vdd-tx-supply = <&vreg_s4a_1p8>;
+        vdd-rx-supply = <&vreg_s4a_1p8>;
+        vdd-io-supply = <&vreg_s4a_1p8>;
+    };
index 30506d91fddd67508a3b29091efa881c782c5268..4df59f3b7b018c74ae59a96ca31dab940f8645d0 100644 (file)
@@ -152,6 +152,7 @@ required:
   - reg
 
 allOf:
+  - $ref: dai-common.yaml#
   - if:
       required:
         - slim-ifc-dev
diff --git a/Documentation/devicetree/bindings/sound/realtek,alc5632.yaml b/Documentation/devicetree/bindings/sound/realtek,alc5632.yaml
new file mode 100644 (file)
index 0000000..fb05988
--- /dev/null
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,alc5632.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ALC5632 audio CODEC
+
+description: |
+  Pins on the device (for linking into audio routes):
+      * SPK_OUTP
+      * SPK_OUTN
+      * HP_OUT_L
+      * HP_OUT_R
+      * AUX_OUT_P
+      * AUX_OUT_N
+      * LINE_IN_L
+      * LINE_IN_R
+      * PHONE_P
+      * PHONE_N
+      * MIC1_P
+      * MIC1_N
+      * MIC2_P
+      * MIC2_N
+      * MICBIAS1
+      * DMICDAT
+
+maintainers:
+  - Leon Romanovsky <leon@leon.nu>
+
+properties:
+  compatible:
+    const: realtek,alc5632
+
+  reg:
+    maxItems: 1
+
+  '#gpio-cells':
+    const: 2
+
+  gpio-controller: true
+
+required:
+  - compatible
+  - reg
+  - '#gpio-cells'
+  - gpio-controller
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "realtek,alc5632";
+            reg = <0x1a>;
+            gpio-controller;
+            #gpio-cells = <2>;
+        };
+    };
index 12ccf29338d92e8308d11412eb7633d2ae2ff2f3..8a821dec952699252310367cec76b9cbb96318af 100644 (file)
@@ -101,17 +101,7 @@ properties:
 
   clock-names:
     description: List of necessary clock names.
-    minItems: 1
-    maxItems: 31
-    items:
-      oneOf:
-        - const: ssi-all
-        - pattern: '^ssi\.[0-9]$'
-        - pattern: '^src\.[0-9]$'
-        - pattern: '^mix\.[0-1]$'
-        - pattern: '^ctu\.[0-1]$'
-        - pattern: '^dvc\.[0-1]$'
-        - pattern: '^clk_(a|b|c|i)$'
+    # details are defined below
 
   ports:
     $ref: audio-graph-port.yaml#/definitions/port-base
@@ -155,7 +145,7 @@ properties:
           dmas:
             maxItems: 1
           dma-names:
-            const: "tx"
+            const: tx
         required:
           - dmas
           - dma-names
@@ -288,6 +278,11 @@ required:
 
 allOf:
   - $ref: dai-common.yaml#
+
+  # --------------------
+  # reg/reg-names
+  # --------------------
+  # for Gen1
   - if:
       properties:
         compatible:
@@ -303,7 +298,15 @@ allOf:
               - scu
               - ssi
               - adg
-    else:
+  # for Gen2/Gen3
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - renesas,rcar_sound-gen2
+              - renesas,rcar_sound-gen3
+    then:
       properties:
         reg:
           minItems: 5
@@ -315,35 +318,87 @@ allOf:
               - ssiu
               - ssi
               - audmapp
+  # for Gen4
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: renesas,rcar_sound-gen4
+    then:
+      properties:
+        reg:
+          maxItems: 4
+        reg-names:
+          items:
+            enum:
+              - adg
+              - ssiu
+              - ssi
+              - sdmc
+
+  # --------------------
+  # clock-names
+  # --------------------
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: renesas,rcar_sound-gen4
+    then:
+      properties:
+        clock-names:
+          maxItems: 3
+          items:
+            enum:
+              - ssi.0
+              - ssiu.0
+              - clkin
+    else:
+      properties:
+        clock-names:
+          minItems: 1
+          maxItems: 31
+          items:
+            oneOf:
+              - const: ssi-all
+              - pattern: '^ssi\.[0-9]$'
+              - pattern: '^src\.[0-9]$'
+              - pattern: '^mix\.[0-1]$'
+              - pattern: '^ctu\.[0-1]$'
+              - pattern: '^dvc\.[0-1]$'
+              - pattern: '^clk_(a|b|c|i)$'
 
 unevaluatedProperties: false
 
 examples:
   - |
+    #include <dt-bindings/clock/r8a7790-cpg-mssr.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/r8a7790-sysc.h>
     rcar_sound: sound@ec500000 {
         #sound-dai-cells = <1>;
         compatible = "renesas,rcar_sound-r8a7790", "renesas,rcar_sound-gen2";
         reg = <0xec500000 0x1000>, /* SCU  */
               <0xec5a0000 0x100>,  /* ADG  */
               <0xec540000 0x1000>, /* SSIU */
-              <0xec541000 0x1280>, /* SSI  */
+              <0xec541000 0x280>,  /* SSI  */
               <0xec740000 0x200>;  /* Audio DMAC peri peri*/
         reg-names = "scu", "adg", "ssiu", "ssi", "audmapp";
 
-        clocks = <&mstp10_clks 1005>,                      /* SSI-ALL    */
-                 <&mstp10_clks 1006>, <&mstp10_clks 1007>, /* SSI9, SSI8 */
-                 <&mstp10_clks 1008>, <&mstp10_clks 1009>, /* SSI7, SSI6 */
-                 <&mstp10_clks 1010>, <&mstp10_clks 1011>, /* SSI5, SSI4 */
-                 <&mstp10_clks 1012>, <&mstp10_clks 1013>, /* SSI3, SSI2 */
-                 <&mstp10_clks 1014>, <&mstp10_clks 1015>, /* SSI1, SSI0 */
-                 <&mstp10_clks 1022>, <&mstp10_clks 1023>, /* SRC9, SRC8 */
-                 <&mstp10_clks 1024>, <&mstp10_clks 1025>, /* SRC7, SRC6 */
-                 <&mstp10_clks 1026>, <&mstp10_clks 1027>, /* SRC5, SRC4 */
-                 <&mstp10_clks 1028>, <&mstp10_clks 1029>, /* SRC3, SRC2 */
-                 <&mstp10_clks 1030>, <&mstp10_clks 1031>, /* SRC1, SRC0 */
-                 <&mstp10_clks 1020>, <&mstp10_clks 1021>, /* MIX1, MIX0 */
-                 <&mstp10_clks 1020>, <&mstp10_clks 1021>, /* CTU1, CTU0 */
-                 <&mstp10_clks 1019>, <&mstp10_clks 1018>, /* DVC0, DVC1 */
+        clocks = <&cpg CPG_MOD 1005>,                      /* SSI-ALL    */
+                 <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, /* SSI9, SSI8 */
+                 <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, /* SSI7, SSI6 */
+                 <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, /* SSI5, SSI4 */
+                 <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, /* SSI3, SSI2 */
+                 <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, /* SSI1, SSI0 */
+                 <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, /* SRC9, SRC8 */
+                 <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, /* SRC7, SRC6 */
+                 <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, /* SRC5, SRC4 */
+                 <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, /* SRC3, SRC2 */
+                 <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, /* SRC1, SRC0 */
+                 <&cpg CPG_MOD 1020>, <&cpg CPG_MOD 1021>, /* MIX1, MIX0 */
+                 <&cpg CPG_MOD 1020>, <&cpg CPG_MOD 1021>, /* CTU1, CTU0 */
+                 <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, /* DVC0, DVC1 */
                  <&audio_clk_a>, <&audio_clk_b>,           /* CLKA, CLKB */
                  <&audio_clk_c>, <&audio_clk_i>;           /* CLKC, CLKI */
 
@@ -364,6 +419,17 @@ examples:
                       "clk_a", "clk_b",
                       "clk_c", "clk_i";
 
+        power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
+
+        resets = <&cpg 1005>,
+                 <&cpg 1006>, <&cpg 1007>, <&cpg 1008>, <&cpg 1009>,
+                 <&cpg 1010>, <&cpg 1011>, <&cpg 1012>, <&cpg 1013>,
+                 <&cpg 1014>, <&cpg 1015>;
+        reset-names = "ssi-all",
+                      "ssi.9", "ssi.8", "ssi.7", "ssi.6",
+                      "ssi.5", "ssi.4", "ssi.3", "ssi.2",
+                      "ssi.1", "ssi.0";
+
         rcar_sound,dvc {
                dvc0: dvc-0 {
                     dmas = <&audma0 0xbc>;
@@ -396,7 +462,7 @@ examples:
                 status = "disabled";
             };
             src1: src-1 {
-                interrupts = <0 353 0>;
+                interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
                 dmas = <&audma0 0x87>, <&audma1 0x9c>;
                 dma-names = "rx", "tx";
             };
@@ -417,12 +483,12 @@ examples:
 
         rcar_sound,ssi {
             ssi0: ssi-0 {
-                interrupts = <0 370 1>;
+                interrupts = <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>;
                 dmas = <&audma0 0x01>, <&audma1 0x02>;
                 dma-names = "rx", "tx";
             };
             ssi1: ssi-1 {
-                interrupts = <0 371 1>;
+                interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
                 dmas = <&audma0 0x03>, <&audma1 0x04>;
                 dma-names = "rx", "tx";
             };
@@ -464,7 +530,6 @@ examples:
         };
     };
 
-
     /* assume audio-graph */
     codec {
         port {
index 196881d943967adeac2d1f125f187e191e7ea4ab..3b5ae45eee4adc2319dd4bbfd77763a9cc99ac24 100644 (file)
@@ -25,14 +25,18 @@ properties:
     maxItems: 1
 
   interrupts:
-    maxItems: 4
+    minItems: 2
+    maxItems: 3
 
   interrupt-names:
-    items:
-      - const: int_req
-      - const: dma_rx
-      - const: dma_tx
-      - const: dma_rt
+    oneOf:
+      - items:
+          - const: int_req
+          - const: dma_rx
+          - const: dma_tx
+      - items:
+          - const: int_req
+          - const: dma_rt
 
   clocks:
     maxItems: 4
@@ -106,9 +110,8 @@ examples:
             reg = <0x10049c00 0x400>;
             interrupts = <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>,
                          <GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
-                         <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>,
-                         <GIC_SPI 329 IRQ_TYPE_EDGE_RISING>;
-            interrupt-names = "int_req", "dma_rx", "dma_tx", "dma_rt";
+                         <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>;
+            interrupt-names = "int_req", "dma_rx", "dma_tx";
             clocks = <&cpg CPG_MOD R9A07G044_SSI0_PCLK2>,
                      <&cpg CPG_MOD R9A07G044_SSI0_PCLK_SFR>,
                      <&audio_clk1>,
index 4c95895de75efb716c1e0790b4a4dbb635555de8..7bb6c5dff7865c7d4c107275b4a4d1deb5809b54 100644 (file)
@@ -86,6 +86,13 @@ properties:
         - tx-m
         - rx-m
 
+  port:
+    $ref: audio-graph-port.yaml#
+    unevaluatedProperties: false
+
+  power-domains:
+    maxItems: 1
+
   rockchip,grf:
     $ref: /schemas/types.yaml#/definitions/phandle
     description:
index 1cb4da3006070ca778342e563607af718e813612..fcb01abffa9750336c7d881713625f03aa1ff080 100644 (file)
@@ -34,6 +34,7 @@ properties:
               - rockchip,rk3366-i2s
               - rockchip,rk3368-i2s
               - rockchip,rk3399-i2s
+              - rockchip,rk3588-i2s
               - rockchip,rv1126-i2s
           - const: rockchip,rk3066-i2s
 
@@ -82,6 +83,10 @@ properties:
   resets:
     maxItems: 2
 
+  port:
+    $ref: audio-graph-port.yaml#
+    unevaluatedProperties: false
+
   rockchip,capture-channels:
     $ref: /schemas/types.yaml#/definitions/uint32
     default: 2
index 02059d66b08453a3b204cc28febe032e55feddbb..1353c051488faeb1d4417a206f17e8ebc8f8fbba 100644 (file)
@@ -50,7 +50,7 @@ properties:
     description: The bias voltage to be used in mVolts. The voltage can take
       values from 1.25V to 3V by 250mV steps. If this node is not mentioned
       or the value is unknown, then the value is set to 1.25V.
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     enum: [ 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 ]
 
   lrclk-strength:
@@ -63,7 +63,7 @@ properties:
         1 =            1.66 mA         2.87 mA         4.02  mA
         2 =            3.33 mA         5.74 mA         8.03  mA
         3 =            4.99 mA         8.61 mA         12.05 mA
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     enum: [ 0, 1, 2, 3 ]
 
   sclk-strength:
@@ -76,7 +76,7 @@ properties:
         1 =            1.66 mA         2.87 mA         4.02  mA
         2 =            3.33 mA         5.74 mA         8.03  mA
         3 =            4.99 mA         8.61 mA         12.05 mA
-    $ref: "/schemas/types.yaml#/definitions/uint32"
+    $ref: /schemas/types.yaml#/definitions/uint32
     enum: [ 0, 1, 2, 3 ]
 
   port:
index 806e2fff165f9c646d3a2fc53c37fc7eb9a941ae..b05e05c81cc4d2d035566982ce9a4ebbefcfc229 100644 (file)
@@ -78,7 +78,7 @@ definitions:
     $ref: /schemas/types.yaml#/definitions/uint32
 
   prefix:
-    description: "device name prefix"
+    description: device name prefix
     $ref: /schemas/types.yaml#/definitions/string
 
   label:
index 9cf0efaed88e276f12cc7c61bbfde8d01356fcfd..8600520d7c4754323e297574f19a2debd4db7756 100644 (file)
@@ -42,7 +42,7 @@ properties:
       Specifies a phandle to soc-glue, which is used for changing mode of S/PDIF
       signal pin to output from Hi-Z. This property is optional if you use I2S
       signal pins only.
-    $ref: "/schemas/types.yaml#/definitions/phandle"
+    $ref: /schemas/types.yaml#/definitions/phandle
 
   "#sound-dai-cells":
     const: 1
index 7c8fd37c2f9e9da14740f78ae8801a01d086ec46..1addc75989d56744caabecf37c5b3f43f2d7792c 100644 (file)
@@ -12,6 +12,7 @@ Required properties:
   - "ti,tas5717",
   - "ti,tas5719",
   - "ti,tas5721"
+  - "ti,tas5733"
 - reg: The I2C address of the device
 - #sound-dai-cells: must be equal to 0
 
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8510.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8510.yaml
new file mode 100644 (file)
index 0000000..6d12b0a
--- /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/wlf,wm8510.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8510 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8510
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@1a {
+            compatible = "wlf,wm8510";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8523.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8523.yaml
new file mode 100644 (file)
index 0000000..decc395
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8523.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8523 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8523
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8523";
+            reg = <0x1a>;
+        };
+    };        
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8524.yaml
new file mode 100644 (file)
index 0000000..4d951ec
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8524.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Wolfson WM8524 24-bit 192KHz Stereo DAC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8524
+
+  "#sound-dai-cells":
+    const: 0
+
+  wlf,mute-gpios:
+    maxItems: 1
+    description:
+      a GPIO spec for the MUTE pin.
+
+required:
+  - compatible
+  - wlf,mute-gpios
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    wm8524: codec {
+        compatible = "wlf,wm8524";
+        wlf,mute-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8580.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8580.yaml
new file mode 100644 (file)
index 0000000..2f27852
--- /dev/null
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8580.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8580 and WM8581 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - wlf,wm8580
+      - wlf,wm8581
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8580";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8711.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8711.yaml
new file mode 100644 (file)
index 0000000..ecaac28
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8711.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8711 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8711
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8711";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8728.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8728.yaml
new file mode 100644 (file)
index 0000000..fc89475
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8728.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8728 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8728
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8728";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8737.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8737.yaml
new file mode 100644 (file)
index 0000000..12d8765
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8737.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8737 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8737
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8737";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8753.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8753.yaml
new file mode 100644 (file)
index 0000000..9eebe7d
--- /dev/null
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8753.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8753 audio CODEC
+
+description: |
+  Pins on the device (for linking into audio routes):
+      * LOUT1
+      * LOUT2
+      * ROUT1
+      * ROUT2
+      * MONO1
+      * MONO2
+      * OUT3
+      * OUT4
+      * LINE1
+      * LINE2
+      * RXP
+      * RXN
+      * ACIN
+      * ACOP
+      * MIC1N
+      * MIC1
+      * MIC2N
+      * MIC2
+      * Mic Bias
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: wlf,wm8753
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        codec@1a {
+            compatible = "wlf,wm8753";
+            reg = <0x1a>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
new file mode 100644 (file)
index 0000000..ee8eba7
--- /dev/null
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8960.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Wolfson WM8960 audio codec
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+properties:
+  compatible:
+    const: wlf,wm8960
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: mclk
+
+  '#sound-dai-cells':
+    const: 0
+
+  wlf,capless:
+    type: boolean
+    description:
+      If present, OUT3 pin will be enabled and disabled together with HP_L and
+      HP_R pins in response to jack detect events.
+
+  wlf,gpio-cfg:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 2
+    description: |
+      A list of GPIO configuration register values.
+       - gpio-cfg[0]: ALRCGPIO of R9 (Audio interface)
+       - gpio-cfg[1]: {GPIOPOL:GPIOSEL[2:0]} of R48 (Additional Control 4).
+
+  wlf,hp-cfg:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 3
+    description: |
+      A list of headphone jack detect configuration register values:
+       - hp-cfg[0]: HPSEL[1:0] of R48 (Additional Control 4).
+       - hp-cfg[1]: {HPSWEN:HPSWPOL} of R24 (Additional Control 2).
+       - hp-cfg[2]: {TOCLKSEL:TOEN} of R23 (Additional Control 1).
+
+  wlf,shared-lrclk:
+    type: boolean
+    description:
+      If present, the LRCM bit of R24 (Additional control 2) gets set,
+      indicating that ADCLRC and DACLRC pins will be disabled only when ADC
+      (Left and Right) and DAC (Left and Right) are disabled.
+      When WM8960 works on synchronize mode and DACLRC pin is used to supply
+      frame clock, it will no frame clock for captrue unless enable DAC to
+      enable DACLRC pin. If shared-lrclk is present, no need to enable DAC for
+      captrue.
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        audio-codec@1a {
+            compatible = "wlf,wm8960";
+            reg = <0x1a>;
+            clocks = <&clks 0>;
+            clock-names = "mclk";
+            #sound-dai-cells = <0>;
+            wlf,hp-cfg = <3 2 3>;
+            wlf,gpio-cfg = <1 3>;
+            wlf,shared-lrclk;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml
new file mode 100644 (file)
index 0000000..8f045de
--- /dev/null
@@ -0,0 +1,194 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8994.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Wolfson WM1811/WM8994/WM8958 audio codecs
+
+maintainers:
+  - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+  - patches@opensource.cirrus.com
+
+description: |
+  These devices support both I2C and SPI (configured with pin strapping on the
+  board).
+
+  Pins on the device (for linking into audio routes):
+  IN1LN, IN1LP, IN2LN, IN2LP:VXRN, IN1RN, IN1RP, IN2RN, IN2RP:VXRP, SPKOUTLP,
+  SPKOUTLN, SPKOUTRP, SPKOUTRN, HPOUT1L, HPOUT1R, HPOUT2P, HPOUT2N, LINEOUT1P,
+  LINEOUT1N, LINEOUT2P, LINEOUT2N.
+
+properties:
+  compatible:
+    enum:
+      - wlf,wm1811
+      - wlf,wm8994
+      - wlf,wm8958
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    items:
+      - const: MCLK1
+      - const: MCLK2
+
+  gpio-controller: true
+
+  '#gpio-cells':
+    const: 2
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+    description:
+      The first cell is the IRQ number. The second cell is the flags, encoded
+      as the trigger masks.
+
+  AVDD1-supply: true
+  AVDD2-supply: true
+  CPVDD-supply: true
+  DBVDD-supply: true
+  DBVDD1-supply: true
+  DBVDD2-supply: true
+  DBVDD3-supply: true
+  DCVDD-supply: true
+  LDO1VDD-supply: true
+  LDO2VDD-supply: true
+  SPKVDD1-supply: true
+  SPKVDD2-supply: true
+
+  '#sound-dai-cells':
+    const: 0
+
+  wlf,gpio-cfg:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 11
+    description:
+      A list of GPIO configuration register values. If absent, no configuration
+      of these registers is performed. If any value is over 0xffff then the
+      register will be left as default. If present 11 values must be supplied.
+
+  wlf,micbias-cfg:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 2
+    description:
+      Two MICBIAS register values for WM1811 or WM8958.  If absent the register
+      defaults will be used.
+
+  wlf,ldo1ena-gpios:
+    maxItems: 1
+    description:
+      Control of LDO1ENA input to device.
+
+  wlf,ldo2ena-gpios:
+    maxItems: 1
+    description:
+      Control of LDO2ENA input to device.
+
+  wlf,lineout1-se:
+    type: boolean
+    description:
+      LINEOUT1 is in single ended mode.
+
+  wlf,lineout2-se:
+    type: boolean
+    description:
+      INEOUT2 is in single ended mode.
+
+  wlf,lineout1-feedback:
+    type: boolean
+    description:
+      LINEOUT1 has common mode feedback connected.
+
+  wlf,lineout2-feedback:
+    type: boolean
+    description:
+      LINEOUT2 has common mode feedback connected.
+
+  wlf,ldoena-always-driven:
+    type: boolean
+    description:
+      LDOENA is always driven.
+
+  wlf,spkmode-pu:
+    type: boolean
+    description:
+      Enable the internal pull-up resistor on the SPKMODE pin.
+
+  wlf,csnaddr-pd:
+    type: boolean
+    description:
+      Enable the internal pull-down resistor on the CS/ADDR pin.
+
+required:
+  - compatible
+  - reg
+  - AVDD2-supply
+  - CPVDD-supply
+  - SPKVDD1-supply
+  - SPKVDD2-supply
+
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      properties:
+        compatible:
+          enum:
+            - wlf,wm1811
+            - wlf,wm8958
+    then:
+      properties:
+        DBVDD-supply: false
+        LDO2VDD-supply: false
+      required:
+        - DBVDD1-supply
+        - DBVDD2-supply
+        - DBVDD3-supply
+    else:
+      properties:
+        DBVDD1-supply: false
+        DBVDD2-supply: false
+        DBVDD3-supply: false
+      required:
+        - DBVDD-supply
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        audio-codec@1a {
+            compatible = "wlf,wm1811";
+            reg = <0x1a>;
+            clocks = <&i2s0 0>;
+            clock-names = "MCLK1";
+
+            AVDD2-supply = <&main_dc_reg>;
+            CPVDD-supply = <&main_dc_reg>;
+            DBVDD1-supply = <&main_dc_reg>;
+            DBVDD2-supply = <&main_dc_reg>;
+            DBVDD3-supply = <&main_dc_reg>;
+            LDO1VDD-supply = <&main_dc_reg>;
+            SPKVDD1-supply = <&main_dc_reg>;
+            SPKVDD2-supply = <&main_dc_reg>;
+
+            wlf,ldo1ena-gpios = <&gpb0 0 GPIO_ACTIVE_HIGH>;
+            wlf,ldo2ena-gpios = <&gpb0 1 GPIO_ACTIVE_HIGH>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wm8510.txt b/Documentation/devicetree/bindings/sound/wm8510.txt
deleted file mode 100644 (file)
index e6b6cc0..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-WM8510 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8510"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Example:
-
-wm8510: codec@1a {
-       compatible = "wlf,wm8510";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8523.txt b/Documentation/devicetree/bindings/sound/wm8523.txt
deleted file mode 100644 (file)
index f3a6485..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-WM8523 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-  - compatible : "wlf,wm8523"
-
-  - reg : the I2C address of the device.
-
-Example:
-
-wm8523: codec@1a {
-       compatible = "wlf,wm8523";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8524.txt b/Documentation/devicetree/bindings/sound/wm8524.txt
deleted file mode 100644 (file)
index f6c0c26..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-WM8524 audio CODEC
-
-This device does not use I2C or SPI but a simple Hardware Control Interface.
-
-Required properties:
-
-  - compatible : "wlf,wm8524"
-
-  - wlf,mute-gpios: a GPIO spec for the MUTE pin.
-
-Example:
-
-wm8524: codec {
-       compatible = "wlf,wm8524";
-       wlf,mute-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8580.txt b/Documentation/devicetree/bindings/sound/wm8580.txt
deleted file mode 100644 (file)
index ff3f9f5..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-WM8580 and WM8581 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-  - compatible : "wlf,wm8580", "wlf,wm8581"
-
-  - reg : the I2C address of the device.
-
-Example:
-
-wm8580: codec@1a {
-       compatible = "wlf,wm8580";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8711.txt b/Documentation/devicetree/bindings/sound/wm8711.txt
deleted file mode 100644 (file)
index c30a138..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-WM8711 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8711"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Example:
-
-wm8711: codec@1a {
-       compatible = "wlf,wm8711";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8728.txt b/Documentation/devicetree/bindings/sound/wm8728.txt
deleted file mode 100644 (file)
index a3608b4..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-WM8728 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8728"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Example:
-
-wm8728: codec@1a {
-       compatible = "wlf,wm8728";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8737.txt b/Documentation/devicetree/bindings/sound/wm8737.txt
deleted file mode 100644 (file)
index eda1ec6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-WM8737 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8737"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Example:
-
-wm8737: codec@1a {
-       compatible = "wlf,wm8737";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8753.txt b/Documentation/devicetree/bindings/sound/wm8753.txt
deleted file mode 100644 (file)
index eca9e5a..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-WM8753 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8753"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Pins on the device (for linking into audio routes):
-
-  * LOUT1
-  * LOUT2
-  * ROUT1
-  * ROUT2
-  * MONO1
-  * MONO2
-  * OUT3
-  * OUT4
-  * LINE1
-  * LINE2
-  * RXP
-  * RXN
-  * ACIN
-  * ACOP
-  * MIC1N
-  * MIC1
-  * MIC2N
-  * MIC2
-  * Mic Bias
-
-Example:
-
-wm8753: codec@1a {
-       compatible = "wlf,wm8753";
-       reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8960.txt b/Documentation/devicetree/bindings/sound/wm8960.txt
deleted file mode 100644 (file)
index 85d3b28..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-WM8960 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-  - compatible : "wlf,wm8960"
-
-  - reg : the I2C address of the device.
-
-Optional properties:
-  - wlf,shared-lrclk: This is a boolean property. If present, the LRCM bit of
-       R24 (Additional control 2) gets set, indicating that ADCLRC and DACLRC pins
-       will be disabled only when ADC (Left and Right) and DAC (Left and Right)
-       are disabled.
-       When wm8960 works on synchronize mode and DACLRC pin is used to supply
-       frame clock, it will no frame clock for captrue unless enable DAC to enable
-       DACLRC pin. If shared-lrclk is present, no need to enable DAC for captrue.
-
-  - wlf,capless: This is a boolean property. If present, OUT3 pin will be
-       enabled and disabled together with HP_L and HP_R pins in response to jack
-       detect events.
-
-  - wlf,hp-cfg: A list of headphone jack detect configuration register values.
-               The list must be 3 entries long.
-               hp-cfg[0]: HPSEL[1:0] of R48 (Additional Control 4).
-               hp-cfg[1]: {HPSWEN:HPSWPOL} of R24 (Additional Control 2).
-               hp-cfg[2]: {TOCLKSEL:TOEN} of R23 (Additional Control 1).
-
-  - wlf,gpio-cfg: A list of GPIO configuration register values.
-                 The list must be 2 entries long.
-                 gpio-cfg[0]: ALRCGPIO of R9 (Audio interface)
-                 gpio-cfg[1]: {GPIOPOL:GPIOSEL[2:0]} of R48 (Additional Control 4).
-
-Example:
-
-wm8960: codec@1a {
-       compatible = "wlf,wm8960";
-       reg = <0x1a>;
-
-       wlf,shared-lrclk;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt
deleted file mode 100644 (file)
index 8fa9475..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-WM1811/WM8994/WM8958 audio CODEC
-
-These devices support both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : One of "wlf,wm1811", "wlf,wm8994" or "wlf,wm8958".
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-  - gpio-controller : Indicates this device is a GPIO controller.
-  - #gpio-cells : Must be 2. The first cell is the pin number and the
-    second cell is used to specify optional parameters (currently unused).
-
-  - power supplies for the device, as covered in
-    Documentation/devicetree/bindings/regulator/regulator.txt, depending
-    on compatible:
-    - for wlf,wm1811 and wlf,wm8958:
-      AVDD1-supply, AVDD2-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply,
-      DCVDD-supply, CPVDD-supply, SPKVDD1-supply, SPKVDD2-supply
-    - for wlf,wm8994:
-      AVDD1-supply, AVDD2-supply, DBVDD-supply, DCVDD-supply, CPVDD-supply,
-      SPKVDD1-supply, SPKVDD2-supply
-
-Optional properties:
-
-  - interrupts : The interrupt line the IRQ signal for the device is
-    connected to.  This is optional, if it is not connected then none
-    of the interrupt related properties should be specified.
-  - interrupt-controller : These devices contain interrupt controllers
-    and may provide interrupt services to other devices if they have an
-    interrupt line connected.
-  - #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
-    The first cell is the IRQ number.
-    The second cell is the flags, encoded as the trigger masks from
-    Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
-
-  - clocks : A list of up to two phandle and clock specifier pairs
-  - clock-names : A list of clock names sorted in the same order as clocks.
-                  Valid clock names are "MCLK1" and "MCLK2".
-
-  - wlf,gpio-cfg : A list of GPIO configuration register values. If absent,
-    no configuration of these registers is performed. If any value is
-    over 0xffff then the register will be left as default. If present 11
-    values must be supplied.
-
-  - wlf,micbias-cfg : Two MICBIAS register values for WM1811 or
-    WM8958.  If absent the register defaults will be used.
-
-  - wlf,ldo1ena : GPIO specifier for control of LDO1ENA input to device.
-  - wlf,ldo2ena : GPIO specifier for control of LDO2ENA input to device.
-
-  - wlf,lineout1-se : If present LINEOUT1 is in single ended mode.
-  - wlf,lineout2-se : If present LINEOUT2 is in single ended mode.
-
-  - wlf,lineout1-feedback : If present LINEOUT1 has common mode feedback
-    connected.
-  - wlf,lineout2-feedback : If present LINEOUT2 has common mode feedback
-    connected.
-
-  - wlf,ldoena-always-driven : If present LDOENA is always driven.
-
-  - wlf,spkmode-pu : If present enable the internal pull-up resistor on
-    the SPKMODE pin.
-
-  - wlf,csnaddr-pd : If present enable the internal pull-down resistor on
-    the CS/ADDR pin.
-
-Pins on the device (for linking into audio routes):
-
-  * IN1LN
-  * IN1LP
-  * IN2LN
-  * IN2LP:VXRN
-  * IN1RN
-  * IN1RP
-  * IN2RN
-  * IN2RP:VXRP
-  * SPKOUTLP
-  * SPKOUTLN
-  * SPKOUTRP
-  * SPKOUTRN
-  * HPOUT1L
-  * HPOUT1R
-  * HPOUT2P
-  * HPOUT2N
-  * LINEOUT1P
-  * LINEOUT1N
-  * LINEOUT2P
-  * LINEOUT2N
-
-Example:
-
-wm8994: codec@1a {
-       compatible = "wlf,wm8994";
-       reg = <0x1a>;
-
-       gpio-controller;
-       #gpio-cells = <2>;
-
-       lineout1-se;
-
-       AVDD1-supply = <&regulator>;
-       AVDD2-supply = <&regulator>;
-       CPVDD-supply = <&regulator>;
-       DBVDD-supply = <&regulator>;
-       DCVDD-supply = <&regulator>;
-       SPKVDD1-supply = <&regulator>;
-       SPKVDD2-supply = <&regulator>;
-};
index 5f31fa5e24356dab47cac834b68ef800a5066408..af71c68f1e4e2c771652374f2b53be5b121d8544 100644 (file)
@@ -723,9 +723,10 @@ Module for EMU10K1/EMU10k2 based PCI sound cards.
 
 * Sound Blaster Live!
 * Sound Blaster PCI 512
-* Emu APS (partially supported)
 * Sound Blaster Audigy
-       
+* E-MU APS (partially supported)
+* E-MU DAS
+
 extin
     bitmap of available external inputs for FX8010 (see below)
 extout
index c506f8d16f2e5a7cb9b19c8cee9cfcdcb0660175..aa176451d5b58cfc8f1b44dadb6bdeabf0ecd4a9 100644 (file)
@@ -19,9 +19,9 @@ Digital mixer controls
 These controls are built using the DSP instructions. They offer extended
 functionality. Only the default built-in code in the ALSA driver is described
 here. Note that the controls work as attenuators: the maximum value is the 
-neutral position leaving the signal unchanged. Note that if the  same destination 
-is mentioned in multiple controls, the signal is accumulated and can be wrapped 
-(set to maximal or minimal value without checking of overflow).
+neutral position leaving the signal unchanged. Note that if the same destination
+is mentioned in multiple controls, the signal is accumulated and can be clipped
+(set to maximal or minimal value without checking for overflow).
 
 
 Explanation of used abbreviations:
@@ -32,17 +32,17 @@ ADC
        analog to digital converter
 I2S
        one-way three wire serial bus for digital sound by Philips Semiconductors
-        (this standard is used for connecting standalone DAC and ADC converters)
+       (this standard is used for connecting standalone D/A and A/D converters)
 LFE
-       low frequency effects (subwoofer signal)
+       low frequency effects (used as subwoofer signal)
 AC97
-       a chip containing an analog mixer, DAC and ADC converters
+       a chip containing an analog mixer, D/A and A/D converters
 IEC958
        S/PDIF
 FX-bus
        the EMU10K2 chip has an effect bus containing 64 accumulators.
-        Each of the synthesizer voices can feed its output to these accumulators
-        and the DSP microcontroller can operate with the resulting sum.
+       Each of the synthesizer voices can feed its output to these accumulators
+       and the DSP microcontroller can operate with the resulting sum.
 
 name='PCM Front Playback Volume',index=0
 ----------------------------------------
@@ -218,8 +218,8 @@ LFE outputs.
 name='IEC958 Optical Raw Playback Switch',index=0
 -------------------------------------------------
 If this switch is on, then the samples for the IEC958 (S/PDIF) digital
-output are taken only from the raw FX8010 PCM, otherwise standard front
-PCM samples are taken.
+output are taken only from the raw iec958 ALSA PCM device (which uses
+accumulators 20 and 21 for left and right PCM by default).
 
 
 PCM stream related controls
@@ -237,8 +237,8 @@ as follows:
 
 name='EMU10K1 PCM Send Routing',index 0-31
 ------------------------------------------
-This control specifies the destination - FX-bus accumulators. There 24
-values with this mapping:
+This control specifies the destination - FX-bus accumulators. There are 24
+values in this mapping:
 
 * 0 -  mono, A destination (FX-bus 0-63), default 0
 * 1 -  mono, B destination (FX-bus 0-63), default 1
@@ -306,6 +306,9 @@ MANUALS/PATENTS
 ftp://opensource.creative.com/pub/doc
 -------------------------------------
 
+Note that the site is defunct, but the documents are available
+from various other locations.
+
 LM4545.pdf
        AC97 Codec
 
index 357fcd619d39d31a1ce485ba6e5ba98f6297a5b1..819886634400f22f1cf1336fa1ef578cc5ed2e91 100644 (file)
@@ -15,7 +15,7 @@ The ALSA driver programs this portion of chip by default code
 IEC958 (S/PDIF) raw PCM
 =======================
 
-This PCM device (it's the 4th PCM device (index 3!) and first subdevice
+This PCM device (it's the 3rd PCM device (index 2!) and first subdevice
 (index 0) for a given card) allows to forward 48kHz, stereo, 16-bit
 little endian streams without any modifications to the digital output
 (coaxial or optical). The universal interface allows the creation of up
@@ -33,9 +33,9 @@ Digital mixer controls
 These controls are built using the DSP instructions. They offer extended
 functionality. Only the default built-in code in the ALSA driver is described
 here. Note that the controls work as attenuators: the maximum value is the 
-neutral position leaving the signal unchanged. Note that if the  same destination 
-is mentioned in multiple controls, the signal is accumulated and can be wrapped 
-(set to maximal or minimal value without checking of overflow).
+neutral position leaving the signal unchanged. Note that if the same destination
+is mentioned in multiple controls, the signal is accumulated and can be clipped
+(set to maximal or minimal value without checking for overflow).
 
 
 Explanation of used abbreviations:
@@ -46,11 +46,11 @@ ADC
        analog to digital converter
 I2S
        one-way three wire serial bus for digital sound by Philips Semiconductors
-        (this standard is used for connecting standalone DAC and ADC converters)
+       (this standard is used for connecting standalone D/A and A/D converters)
 LFE
-       low frequency effects (subwoofer signal)
+       low frequency effects (used as subwoofer signal)
 AC97
-       a chip containing an analog mixer, DAC and ADC converters
+       a chip containing an analog mixer, D/A and A/D converters
 IEC958
        S/PDIF
 FX-bus
@@ -313,6 +313,9 @@ MANUALS/PATENTS
 ftp://opensource.creative.com/pub/doc
 -------------------------------------
 
+Note that the site is defunct, but the documents are available
+from various other locations.
+
 LM4545.pdf
        AC97 Codec
 m2049.pdf
index 6e12de9fc34e29eb168e33ac2ec53a3955ad9fc3..baefe4a5d16548a4c4871d3aa68d96e583633734 100644 (file)
@@ -9,3 +9,4 @@ HD-Audio
    controls
    dp-mst
    realtek-pc-beep
+   intel-multi-link
diff --git a/Documentation/sound/hd-audio/intel-multi-link.rst b/Documentation/sound/hd-audio/intel-multi-link.rst
new file mode 100644 (file)
index 0000000..bf0bb78
--- /dev/null
@@ -0,0 +1,312 @@
+.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+.. include:: <isonum.txt>
+
+================================================
+HDAudio multi-link extensions on Intel platforms
+================================================
+
+:Copyright: |copy| 2023 Intel Corporation
+
+This file documents the 'multi-link structure' introduced in 2015 with
+the Skylake processor and recently extended in newer Intel platforms
+
+HDaudio existing link mapping (2015 addition in SkyLake)
+========================================================
+
+External HDAudio codecs are handled with link #0, while iDISP codec
+for HDMI/DisplayPort is handled with link #1.
+
+The only change to the 2015 definitions is the declaration of the
+LCAP.ALT=0x0 - since the ALT bit was previously reserved, this is a
+backwards-compatible change.
+
+LCTL.SPA and LCTL.CPA are automatically set when exiting reset. They
+are only used in existing drivers when the SCF value needs to be
+corrected.
+
+Basic structure for HDaudio codecs
+----------------------------------
+
+::
+
+  +-----------+
+  | ML cap #0 |
+  +-----------+
+  | ML cap #1 |---+
+  +-----------+   |
+                  |
+                  +--> 0x0 +---------------+ LCAP
+                           | ALT=0         |
+                           +---------------+
+                           | S192          |
+                           +---------------+
+                           | S96           |
+                           +---------------+
+                           | S48           |
+                           +---------------+
+                           | S24           |
+                           +---------------+
+                           | S12           |
+                           +---------------+
+                           | S6            |
+                           +---------------+
+
+                       0x4 +---------------+ LCTL
+                           | INTSTS        |
+                           +---------------+
+                           | CPA           |
+                           +---------------+
+                           | SPA           |
+                           +---------------+
+                           | SCF           |
+                           +---------------+
+
+                       0x8 +---------------+ LOSIDV
+                           | L1OSIVD15     |
+                           +---------------+
+                           | L1OSIDV..     |
+                           +---------------+
+                           | L1OSIDV1      |
+                           +---------------+
+
+                       0xC +---------------+ LSDIID
+                           | SDIID14       |
+                           +---------------+
+                           | SDIID...      |
+                           +---------------+
+                           | SDIID0        |
+                           +---------------+
+
+SoundWire HDaudio extended link mapping
+=======================================
+
+A SoundWire extended link is identified when LCAP.ALT=1 and
+LEPTR.ID=0.
+
+DMA control uses the existing LOSIDV register.
+
+Changes include additional descriptions for enumeration that were not
+present in earlier generations.
+
+- multi-link synchronization: capabilities in LCAP.LSS and control in LSYNC
+- number of sublinks (manager IP) in LCAP.LSCOUNT
+- power management moved from SHIM to LCTL.SPA bits
+- hand-over to the DSP for access to multi-link registers, SHIM/IP with LCTL.OFLEN
+- mapping of SoundWire codecs to SDI ID bits
+- move of SHIM and Cadence registers to different offsets, with no
+  change in functionality. The LEPTR.PTR value is an offset from the
+  ML address, with a default value of 0x30000.
+
+Extended structure for SoundWire (assuming 4 Manager IP)
+--------------------------------------------------------
+
+::
+
+  +-----------+
+  | ML cap #0 |
+  +-----------+
+  | ML cap #1 |
+  +-----------+
+  | ML cap #2 |---+
+  +-----------+   |
+                  |
+                  +--> 0x0 +---------------+ LCAP
+                           | ALT=1         |
+                           +---------------+
+                           | INTC          |
+                           +---------------+
+                           | OFLS          |
+                           +---------------+
+                           | LSS           |
+                           +---------------+
+                           | SLCOUNT=4     |-----------+
+                           +---------------+           |
+                                                       |
+                       0x4 +---------------+ LCTL      |
+                           | INTSTS        |           |
+                           +---------------+           |
+                           | CPA (x bits)  |           |
+                           +---------------+           |
+                           | SPA (x bits)  |           |
+                           +---------------+         for each sublink x
+                           | INTEN         |           |
+                           +---------------+           |
+                           | OFLEN         |           |
+                           +---------------+           |
+                                                       |
+                       0x8 +---------------+ LOSIDV    |
+                           | L1OSIVD15     |           |
+                           +---------------+           |
+                           | L1OSIDV..     |           |
+                           +---------------+           |
+                           | L1OSIDV1      |       +---+----------------------------------------------------------+
+                           +---------------+       |                                                              |
+                                                   v                                                              |
+             0xC + 0x2 * x +---------------+ LSDIIDx    +---> 0x30000  +-----------------+  0x00030000            |
+                           | SDIID14       |            |              | SoundWire SHIM  |                        |
+                           +---------------+            |              | generic         |                        |
+                           | SDIID...      |            |              +-----------------+  0x00030100            |
+                           +---------------+            |              | SoundWire IP    |                        |
+                           | SDIID0        |            |              +-----------------+  0x00036000            |
+                           +---------------+            |              | SoundWire SHIM  |                        |
+                                                        |              | vendor-specific |                        |
+                      0x1C +---------------+ LSYNC      |              +-----------------+                        |
+                           | CMDSYNC       |            |                                                         v
+                           +---------------+            |              +-----------------+  0x00030000 + 0x8000 * x
+                           | SYNCGO        |            |              | SoundWire SHIM  |
+                           +---------------+            |              | generic         |
+                           | SYNCPU        |            |              +-----------------+  0x00030100 + 0x8000 * x
+                           +---------------+            |              | SoundWire IP    |
+                           | SYNPRD        |            |              +-----------------+  0x00036000 + 0x8000 * x
+                           +---------------+            |              | SoundWire SHIM  |
+                                                        |              | vendor-specific |
+                      0x20 +---------------+ LEPTR      |              +-----------------+
+                           | ID = 0        |            |
+                           +---------------+            |
+                           | VER           |            |
+                           +---------------+            |
+                           | PTR           |------------+
+                           +---------------+
+
+
+DMIC HDaudio extended link mapping
+==================================
+
+A DMIC extended link is identified when LCAP.ALT=1 and
+LEPTR.ID=0xC1 are set.
+
+DMA control uses the existing LOSIDV register
+
+Changes include additional descriptions for enumeration that were not
+present in earlier generations.
+
+- multi-link synchronization: capabilities in LCAP.LSS and control in LSYNC
+- power management with LCTL.SPA bits
+- hand-over to the DSP for access to multi-link registers, SHIM/IP with LCTL.OFLEN
+
+- move of DMIC registers to different offsets, with no change in
+  functionality. The LEPTR.PTR value is an offset from the ML
+  address, with a default value of 0x10000.
+
+Extended structure for DMIC
+---------------------------
+
+::
+
+  +-----------+
+  | ML cap #0 |
+  +-----------+
+  | ML cap #1 |
+  +-----------+
+  | ML cap #2 |---+
+  +-----------+   |
+                  |
+                  +--> 0x0 +---------------+ LCAP
+                           | ALT=1         |
+                           +---------------+
+                           | INTC          |
+                           +---------------+
+                           | OFLS          |
+                           +---------------+
+                           | SLCOUNT=1     |
+                           +---------------+
+
+                       0x4 +---------------+ LCTL
+                           | INTSTS        |
+                           +---------------+
+                           | CPA           |
+                           +---------------+
+                           | SPA           |
+                           +---------------+
+                           | INTEN         |
+                           +---------------+
+                           | OFLEN         |
+                           +---------------+           +---> 0x10000  +-----------------+  0x00010000
+                                                       |              | DMIC SHIM       |
+                       0x8 +---------------+ LOSIDV    |              | generic         |
+                           | L1OSIVD15     |           |              +-----------------+  0x00010100
+                           +---------------+           |              | DMIC IP         |
+                           | L1OSIDV..     |           |              +-----------------+  0x00016000
+                           +---------------+           |              | DMIC SHIM       |
+                           | L1OSIDV1      |           |              | vendor-specific |
+                           +---------------+           |              +-----------------+
+                                                       |
+                      0x20 +---------------+ LEPTR     |
+                           | ID = 0xC1     |           |
+                           +---------------+           |
+                           | VER           |           |
+                           +---------------+           |
+                           | PTR           |-----------+
+                           +---------------+
+
+
+SSP HDaudio extended link mapping
+=================================
+
+A DMIC extended link is identified when LCAP.ALT=1 and
+LEPTR.ID=0xC0 are set.
+
+DMA control uses the existing LOSIDV register
+
+Changes include additional descriptions for enumeration and control that were not
+present in earlier generations:
+- number of sublinks (SSP IP instances) in LCAP.LSCOUNT
+- power management moved from SHIM to LCTL.SPA bits
+- hand-over to the DSP for access to multi-link registers, SHIM/IP
+with LCTL.OFLEN
+- move of SHIM and SSP IP registers to different offsets, with no
+change in functionality.  The LEPTR.PTR value is an offset from the ML
+address, with a default value of 0x28000.
+
+Extended structure for SSP (assuming 3 instances of the IP)
+-----------------------------------------------------------
+
+::
+
+  +-----------+
+  | ML cap #0 |
+  +-----------+
+  | ML cap #1 |
+  +-----------+
+  | ML cap #2 |---+
+  +-----------+   |
+                  |
+                  +--> 0x0 +---------------+ LCAP
+                           | ALT=1         |
+                           +---------------+
+                           | INTC          |
+                           +---------------+
+                           | OFLS          |
+                           +---------------+
+                           | SLCOUNT=3     |-------------------------for each sublink x -------------------------+
+                           +---------------+                                                                     |
+                                                                                                                 |
+                       0x4 +---------------+ LCTL                                                                |
+                           | INTSTS        |                                                                     |
+                           +---------------+                                                                     |
+                           | CPA (x bits)  |                                                                     |
+                           +---------------+                                                                     |
+                           | SPA (x bits)  |                                                                     |
+                           +---------------+                                                                     |
+                           | INTEN         |                                                                     |
+                           +---------------+                                                                     |
+                           | OFLEN         |                                                                     |
+                           +---------------+           +---> 0x28000  +-----------------+  0x00028000            |
+                                                       |              | SSP SHIM        |                        |
+                       0x8 +---------------+ LOSIDV    |              | generic         |                        |
+                           | L1OSIVD15     |           |              +-----------------+  0x00028100            |
+                           +---------------+           |              | SSP IP          |                        |
+                           | L1OSIDV..     |           |              +-----------------+  0x00028C00            |
+                           +---------------+           |              | SSP SHIM        |                        |
+                           | L1OSIDV1      |           |              | vendor-specific |                        |
+                           +---------------+           |              +-----------------+                        |
+                                                       |                                                         v
+                      0x20 +---------------+ LEPTR     |              +-----------------+  0x00028000 + 0x1000 * x
+                           | ID = 0xC0     |           |              | SSP SHIM        |
+                           +---------------+           |              | generic         |
+                           | VER           |           |              +-----------------+  0x00028100 + 0x1000 * x
+                           +---------------+           |              | SSP IP          |
+                           | PTR           |-----------+              +-----------------+  0x00028C00 + 0x1000 * x
+                           +---------------+                          | SSP SHIM        |
+                                                                      | vendor-specific |
+                                                                      +-----------------+
index 5c9523b7d55cdbb0df0bb291712e74e30e826860..c0f97b5e4249698523d517d277c95e45ab845407 100644 (file)
@@ -19,18 +19,13 @@ explain the general topic of linux kernel coding and doesn't cover
 low-level driver implementation details. It only describes the standard
 way to write a PCI sound driver on ALSA.
 
-This document is still a draft version. Any feedback and corrections,
-please!!
-
 File Tree Structure
 ===================
 
 General
 -------
 
-The file tree structure of ALSA driver is depicted below.
-
-::
+The file tree structure of ALSA driver is depicted below::
 
             sound
                     /core
@@ -68,8 +63,8 @@ kernel config.
 core/oss
 ~~~~~~~~
 
-The codes for PCM and mixer OSS emulation modules are stored in this
-directory. The rawmidi OSS emulation is included in the ALSA rawmidi
+The code for OSS PCM and mixer emulation modules is stored in this
+directory. The OSS rawmidi emulation is included in the ALSA rawmidi
 code since it's quite small. The sequencer code is stored in
 ``core/seq/oss`` directory (see `below <core/seq/oss_>`__).
 
@@ -78,19 +73,19 @@ core/seq
 
 This directory and its sub-directories are for the ALSA sequencer. This
 directory contains the sequencer core and primary sequencer modules such
-like snd-seq-midi, snd-seq-virmidi, etc. They are compiled only when
+as snd-seq-midi, snd-seq-virmidi, etc. They are compiled only when
 ``CONFIG_SND_SEQUENCER`` is set in the kernel config.
 
 core/seq/oss
 ~~~~~~~~~~~~
 
-This contains the OSS sequencer emulation codes.
+This contains the OSS sequencer emulation code.
 
 include directory
 -----------------
 
 This is the place for the public header files of ALSA drivers, which are
-to be exported to user-space, or included by several files at different
+to be exported to user-space, or included by several files in different
 directories. Basically, the private header files should not be placed in
 this directory, but you may still find files there, due to historical
 reasons :)
@@ -100,7 +95,7 @@ drivers directory
 
 This directory contains code shared among different drivers on different
 architectures. They are hence supposed not to be architecture-specific.
-For example, the dummy pcm driver and the serial MIDI driver are found
+For example, the dummy PCM driver and the serial MIDI driver are found
 in this directory. In the sub-directories, there is code for components
 which are independent from bus and cpu architectures.
 
@@ -156,8 +151,8 @@ these architectures.
 usb directory
 -------------
 
-This directory contains the USB-audio driver. In the latest version, the
-USB MIDI driver is integrated in the usb-audio driver.
+This directory contains the USB-audio driver.
+The USB MIDI driver is integrated in the usb-audio driver.
 
 pcmcia directory
 ----------------
@@ -175,9 +170,9 @@ layer including ASoC core, codec and machine drivers.
 oss directory
 -------------
 
-Here contains OSS/Lite codes.
-All codes have been deprecated except for dmasound on m68k as of
-writing this.
+This contains OSS/Lite code.
+At the time of writing, all code has been removed except for dmasound
+on m68k.
 
 
 Basic Flow for PCI Drivers
@@ -341,7 +336,7 @@ to details explained in the following section.
 
       error:
               snd_card_free(card);
-             return err;
+              return err;
       }
 
       /* destructor -- see the "Destructor" sub-section */
@@ -381,7 +376,7 @@ where ``enable[dev]`` is the module option.
 
 Each time the ``probe`` callback is called, check the availability of
 the device. If not available, simply increment the device index and
-returns. dev will be incremented also later (`step 7
+return. dev will be incremented also later (`step 7
 <7) Set the PCI driver data and return zero._>`__).
 
 2) Create a card instance
@@ -402,9 +397,7 @@ Components`_.
 3) Create a main component
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-In this part, the PCI resources are allocated.
-
-::
+In this part, the PCI resources are allocated::
 
   struct mychip *chip;
   ....
@@ -417,13 +410,11 @@ Management`_.
 
 When something goes wrong, the probe function needs to deal with the
 error.  In this example, we have a single error handling path placed
-at the end of the function.
-
-::
+at the end of the function::
 
   error:
           snd_card_free(card);
-         return err;
+          return err;
 
 Since each component can be properly freed, the single
 :c:func:`snd_card_free()` call should suffice in most cases.
@@ -483,13 +474,11 @@ remove callback and power-management callbacks, too.
 Destructor
 ----------
 
-The destructor, remove callback, simply releases the card instance. Then
-the ALSA middle layer will release all the attached components
+The destructor, the remove callback, simply releases the card instance.
+Then the ALSA middle layer will release all the attached components
 automatically.
 
-It would be typically just calling :c:func:`snd_card_free()`:
-
-::
+It would be typically just calling :c:func:`snd_card_free()`::
 
   static void snd_mychip_remove(struct pci_dev *pci)
   {
@@ -504,9 +493,7 @@ Header Files
 ------------
 
 For the above example, at least the following include files are
-necessary.
-
-::
+necessary::
 
   #include <linux/init.h>
   #include <linux/pci.h>
@@ -544,9 +531,7 @@ list on the card record is used to manage the correct release of
 resources at destruction.
 
 As mentioned above, to create a card instance, call
-:c:func:`snd_card_new()`.
-
-::
+:c:func:`snd_card_new()`::
 
   struct snd_card *card;
   int err;
@@ -572,10 +557,8 @@ struct snd_device object. A component
 can be a PCM instance, a control interface, a raw MIDI interface, etc.
 Each such instance has one component entry.
 
-A component can be created via :c:func:`snd_device_new()`
-function.
-
-::
+A component can be created via the :c:func:`snd_device_new()`
+function::
 
   snd_device_new(card, SNDRV_DEV_XXX, chip, &ops);
 
@@ -591,7 +574,7 @@ allocated manually beforehand, and its pointer is passed as the
 argument. This pointer (``chip`` in the above example) is used as the
 identifier for the instance.
 
-Each pre-defined ALSA component such as ac97 and pcm calls
+Each pre-defined ALSA component such as AC97 and PCM calls
 :c:func:`snd_device_new()` inside its constructor. The destructor
 for each component is defined in the callback pointers. Hence, you don't
 need to take care of calling a destructor for such a component.
@@ -605,9 +588,7 @@ Chip-Specific Data
 ------------------
 
 Chip-specific information, e.g. the I/O port address, its resource
-pointer, or the irq number, is stored in the chip-specific record.
-
-::
+pointer, or the irq number, is stored in the chip-specific record::
 
   struct mychip {
           ....
@@ -620,9 +601,7 @@ In general, there are two ways of allocating the chip record.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 As mentioned above, you can pass the extra-data-length to the 5th
-argument of :c:func:`snd_card_new()`, i.e.
-
-::
+argument of :c:func:`snd_card_new()`, e.g.::
 
   err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
                      sizeof(struct mychip), &card);
@@ -642,9 +621,7 @@ released together with the card instance.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 After allocating a card instance via :c:func:`snd_card_new()`
-(with ``0`` on the 4th arg), call :c:func:`kzalloc()`.
-
-::
+(with ``0`` on the 4th arg), call :c:func:`kzalloc()`::
 
   struct snd_card *card;
   struct mychip *chip;
@@ -663,16 +640,12 @@ The chip record should have the field to hold the card pointer at least,
   };
 
 
-Then, set the card pointer in the returned chip instance.
-
-::
+Then, set the card pointer in the returned chip instance::
 
   chip->card = card;
 
 Next, initialize the fields, and register this chip record as a
-low-level device with a specified ``ops``,
-
-::
+low-level device with a specified ``ops``::
 
   static const struct snd_device_ops ops = {
           .dev_free =        snd_mychip_dev_free,
@@ -681,9 +654,7 @@ low-level device with a specified ``ops``,
   snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
 
 :c:func:`snd_mychip_dev_free()` is the device-destructor
-function, which will call the real destructor.
-
-::
+function, which will call the real destructor::
 
   static int snd_mychip_dev_free(struct snd_device *device)
   {
@@ -692,10 +663,10 @@ function, which will call the real destructor.
 
 where :c:func:`snd_mychip_free()` is the real destructor.
 
-The demerit of this method is the obviously more amount of codes.
-The merit is, however, you can trigger the own callback at registering
-and disconnecting the card via setting in snd_device_ops.
-About the registering and disconnecting the card, see the subsections
+The demerit of this method is the obviously larger amount of code.
+The merit is, however, that you can trigger your own callback at
+registering and disconnecting the card via a setting in snd_device_ops.
+About registering and disconnecting the card, see the subsections
 below.
 
 
@@ -724,9 +695,7 @@ Full Code Example
 -----------------
 
 In this section, we'll complete the chip-specific constructor,
-destructor and PCI entries. Example code is shown first, below.
-
-::
+destructor and PCI entries. Example code is shown first, below::
 
       struct mychip {
               struct snd_card *card;
@@ -866,9 +835,7 @@ resources. Also, you need to set the proper PCI DMA mask to limit the
 accessed I/O range. In some cases, you might need to call
 :c:func:`pci_set_master()` function, too.
 
-Suppose the 28bit mask, and the code to be added would be like:
-
-::
+Suppose a 28bit mask, the code to be added would look like::
 
   err = pci_enable_device(pci);
   if (err < 0)
@@ -890,9 +857,7 @@ function (see below).
 
 Now assume that the PCI device has an I/O port with 8 bytes and an
 interrupt. Then struct mychip will have the
-following fields:
-
-::
+following fields::
 
   struct mychip {
           struct snd_card *card;
@@ -905,14 +870,12 @@ following fields:
 For an I/O port (and also a memory region), you need to have the
 resource pointer for the standard resource management. For an irq, you
 have to keep only the irq number (integer). But you need to initialize
-this number as -1 before actual allocation, since irq 0 is valid. The
+this number to -1 before actual allocation, since irq 0 is valid. The
 port address and its resource pointer can be initialized as null by
 :c:func:`kzalloc()` automatically, so you don't have to take care of
 resetting them.
 
-The allocation of an I/O port is done like this:
-
-::
+The allocation of an I/O port is done like this::
 
   err = pci_request_regions(pci, "My Chip");
   if (err < 0) { 
@@ -928,9 +891,7 @@ The returned value, ``chip->res_port``, is allocated via
 must be released via :c:func:`kfree()`, but there is a problem with
 this. This issue will be explained later.
 
-The allocation of an interrupt source is done like this:
-
-::
+The allocation of an interrupt source is done like this::
 
   if (request_irq(pci->irq, snd_mychip_interrupt,
                   IRQF_SHARED, KBUILD_MODNAME, chip)) {
@@ -954,9 +915,7 @@ used for that, but you can use what you like, too.
 
 I won't give details about the interrupt handler at this point, but at
 least its appearance can be explained now. The interrupt handler looks
-usually like the following:
-
-::
+usually as follows::
 
   static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
   {
@@ -966,13 +925,12 @@ usually like the following:
   }
 
 After requesting the IRQ, you can passed it to ``card->sync_irq``
-field:
-::
+field::
 
           card->irq = chip->irq;
 
-This allows PCM core automatically performing
-:c:func:`synchronize_irq()` at the necessary timing like ``hw_free``.
+This allows the PCM core to automatically call
+:c:func:`synchronize_irq()` at the right time, like before ``hw_free``.
 See the later section `sync_stop callback`_ for details.
 
 Now let's write the corresponding destructor for the resources above.
@@ -981,9 +939,7 @@ activated) and release the resources. So far, we have no hardware part,
 so the disabling code is not written here.
 
 To release the resources, the “check-and-release” method is a safer way.
-For the interrupt, do like this:
-
-::
+For the interrupt, do like this::
 
   if (chip->irq >= 0)
           free_irq(chip->irq, chip);
@@ -997,9 +953,7 @@ When you requested I/O ports or memory regions via
 :c:func:`pci_request_regions()` like in this example, release the
 resource(s) using the corresponding function,
 :c:func:`pci_release_region()` or
-:c:func:`pci_release_regions()`.
-
-::
+:c:func:`pci_release_regions()`::
 
   pci_release_regions(chip->pci);
 
@@ -1007,39 +961,32 @@ When you requested manually via :c:func:`request_region()` or
 :c:func:`request_mem_region()`, you can release it via
 :c:func:`release_resource()`. Suppose that you keep the resource
 pointer returned from :c:func:`request_region()` in
-chip->res_port, the release procedure looks like:
-
-::
+chip->res_port, the release procedure looks like::
 
   release_and_free_resource(chip->res_port);
 
 Don't forget to call :c:func:`pci_disable_device()` before the
 end.
 
-And finally, release the chip-specific record.
-
-::
+And finally, release the chip-specific record::
 
   kfree(chip);
 
-We didn't implement the hardware disabling part in the above. If you
+We didn't implement the hardware disabling part above. If you
 need to do this, please note that the destructor may be called even
 before the initialization of the chip is completed. It would be better
 to have a flag to skip hardware disabling if the hardware was not
 initialized yet.
 
 When the chip-data is assigned to the card using
-:c:func:`snd_device_new()` with ``SNDRV_DEV_LOWLELVEL`` , its
-destructor is called at the last. That is, it is assured that all other
+:c:func:`snd_device_new()` with ``SNDRV_DEV_LOWLELVEL``, its
+destructor is called last. That is, it is assured that all other
 components like PCMs and controls have already been released. You don't
 have to stop PCMs, etc. explicitly, but just call low-level hardware
 stopping.
 
 The management of a memory-mapped region is almost as same as the
-management of an I/O port. You'll need three fields like the
-following:
-
-::
+management of an I/O port. You'll need two fields as follows::
 
   struct mychip {
           ....
@@ -1047,9 +994,7 @@ following:
           void __iomem *iobase_virt;
   };
 
-and the allocation would be like below:
-
-::
+and the allocation would look like below::
 
   err = pci_request_regions(pci, "My Chip");
   if (err < 0) {
@@ -1060,9 +1005,7 @@ and the allocation would be like below:
   chip->iobase_virt = ioremap(chip->iobase_phys,
                                       pci_resource_len(pci, 0));
 
-and the corresponding destructor would be:
-
-::
+and the corresponding destructor would be::
 
   static int snd_mychip_free(struct mychip *chip)
   {
@@ -1075,9 +1018,7 @@ and the corresponding destructor would be:
   }
 
 Of course, a modern way with :c:func:`pci_iomap()` will make things a
-bit easier, too.
-
-::
+bit easier, too::
 
   err = pci_request_regions(pci, "My Chip");
   if (err < 0) {
@@ -1097,9 +1038,7 @@ struct pci_device_id table for
 this chipset. It's a table of PCI vendor/device ID number, and some
 masks.
 
-For example,
-
-::
+For example::
 
   static struct pci_device_id snd_mychip_ids[] = {
           { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR,
@@ -1120,9 +1059,7 @@ The last entry of this list is the terminator. You must specify this
 all-zero entry.
 
 Then, prepare the struct pci_driver
-record:
-
-::
+record::
 
   static struct pci_driver driver = {
           .name = KBUILD_MODNAME,
@@ -1133,11 +1070,9 @@ record:
 
 The ``probe`` and ``remove`` functions have already been defined in
 the previous sections. The ``name`` field is the name string of this
-device. Note that you must not use a slash “/” in this string.
-
-And at last, the module entries:
+device. Note that you must not use slashes (“/”) in this string.
 
-::
+And at last, the module entries::
 
   static int __init alsa_card_mychip_init(void)
   {
@@ -1167,22 +1102,22 @@ The PCM middle layer of ALSA is quite powerful and it is only necessary
 for each driver to implement the low-level functions to access its
 hardware.
 
-For accessing to the PCM layer, you need to include ``<sound/pcm.h>``
+To access the PCM layer, you need to include ``<sound/pcm.h>``
 first. In addition, ``<sound/pcm_params.h>`` might be needed if you
-access to some functions related with hw_param.
+access some functions related with hw_param.
 
-Each card device can have up to four pcm instances. A pcm instance
-corresponds to a pcm device file. The limitation of number of instances
-comes only from the available bit size of the Linux's device numbers.
-Once when 64bit device number is used, we'll have more pcm instances
+Each card device can have up to four PCM instances. A PCM instance
+corresponds to a PCM device file. The limitation of number of instances
+comes only from the available bit size of Linux' device numbers.
+Once 64bit device numbers are used, we'll have more PCM instances
 available.
 
-A pcm instance consists of pcm playback and capture streams, and each
-pcm stream consists of one or more pcm substreams. Some soundcards
+A PCM instance consists of PCM playback and capture streams, and each
+PCM stream consists of one or more PCM substreams. Some soundcards
 support multiple playback functions. For example, emu10k1 has a PCM
 playback of 32 stereo substreams. In this case, at each open, a free
 substream is (usually) automatically chosen and opened. Meanwhile, when
-only one substream exists and it was already opened, the successful open
+only one substream exists and it was already opened, a subsequent open
 will either block or error with ``EAGAIN`` according to the file open
 mode. But you don't have to care about such details in your driver. The
 PCM middle layer will take care of such work.
@@ -1191,9 +1126,7 @@ Full Code Example
 -----------------
 
 The example code below does not include any hardware access routines but
-shows only the skeleton, how to build up the PCM interfaces.
-
-::
+shows only the skeleton, how to build up the PCM interfaces::
 
       #include <sound/pcm.h>
       ....
@@ -1399,10 +1332,8 @@ shows only the skeleton, how to build up the PCM interfaces.
 PCM Constructor
 ---------------
 
-A pcm instance is allocated by the :c:func:`snd_pcm_new()`
-function. It would be better to create a constructor for pcm, namely,
-
-::
+A PCM instance is allocated by the :c:func:`snd_pcm_new()`
+function. It would be better to create a constructor for the PCM, namely::
 
   static int snd_mychip_new_pcm(struct mychip *chip)
   {
@@ -1415,16 +1346,16 @@ function. It would be better to create a constructor for pcm, namely,
           pcm->private_data = chip;
           strcpy(pcm->name, "My Chip");
           chip->pcm = pcm;
-         ....
+          ...
           return 0;
   }
 
-The :c:func:`snd_pcm_new()` function takes four arguments. The
-first argument is the card pointer to which this pcm is assigned, and
+The :c:func:`snd_pcm_new()` function takes six arguments. The
+first argument is the card pointer to which this PCM is assigned, and
 the second is the ID string.
 
 The third argument (``index``, 0 in the above) is the index of this new
-pcm. It begins from zero. If you create more than one pcm instances,
+PCM. It begins from zero. If you create more than one PCM instances,
 specify the different numbers in this argument. For example, ``index =
 1`` for the second PCM device.
 
@@ -1437,26 +1368,20 @@ If a chip supports multiple playbacks or captures, you can specify more
 numbers, but they must be handled properly in open/close, etc.
 callbacks. When you need to know which substream you are referring to,
 then it can be obtained from struct snd_pcm_substream data passed to each
-callback as follows:
-
-::
+callback as follows::
 
   struct snd_pcm_substream *substream;
   int index = substream->number;
 
 
-After the pcm is created, you need to set operators for each pcm stream.
-
-::
+After the PCM is created, you need to set operators for each PCM stream::
 
   snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                   &snd_mychip_playback_ops);
   snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
                   &snd_mychip_capture_ops);
 
-The operators are defined typically like this:
-
-::
+The operators are defined typically like this::
 
   static struct snd_pcm_ops snd_mychip_playback_ops = {
           .open =        snd_mychip_pcm_open,
@@ -1472,25 +1397,21 @@ All the callbacks are described in the Operators_ subsection.
 
 After setting the operators, you probably will want to pre-allocate the
 buffer and set up the managed allocation mode.
-For that, simply call the following:
-
-::
+For that, simply call the following::
 
   snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
                                  &chip->pci->dev,
                                  64*1024, 64*1024);
 
-It will allocate a buffer up to 64kB as default. Buffer management
+It will allocate a buffer up to 64kB by default. Buffer management
 details will be described in the later section `Buffer and Memory
 Management`_.
 
-Additionally, you can set some extra information for this pcm in
+Additionally, you can set some extra information for this PCM in
 ``pcm->info_flags``. The available values are defined as
 ``SNDRV_PCM_INFO_XXX`` in ``<sound/asound.h>``, which is used for the
 hardware definition (described later). When your soundchip supports only
-half-duplex, specify like this:
-
-::
+half-duplex, specify it like this::
 
   pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
 
@@ -1498,15 +1419,13 @@ half-duplex, specify like this:
 ... And the Destructor?
 -----------------------
 
-The destructor for a pcm instance is not always necessary. Since the pcm
+The destructor for a PCM instance is not always necessary. Since the PCM
 device will be released by the middle layer code automatically, you
 don't have to call the destructor explicitly.
 
 The destructor would be necessary if you created special records
 internally and needed to release them. In such a case, set the
-destructor function to ``pcm->private_free``:
-
-::
+destructor function to ``pcm->private_free``::
 
       static void mychip_pcm_free(struct snd_pcm *pcm)
       {
@@ -1537,13 +1456,11 @@ Runtime Pointer - The Chest of PCM Information
 When the PCM substream is opened, a PCM runtime instance is allocated
 and assigned to the substream. This pointer is accessible via
 ``substream->runtime``. This runtime pointer holds most information you
-need to control the PCM: the copy of hw_params and sw_params
+need to control the PCM: a copy of hw_params and sw_params
 configurations, the buffer pointers, mmap records, spinlocks, etc.
 
 The definition of runtime instance is found in ``<sound/pcm.h>``. Here
-are the contents of this file:
-
-::
+is the relevant part of this file::
 
   struct _snd_pcm_runtime {
           /* -- Status -- */
@@ -1577,14 +1494,19 @@ are the contents of this file:
           unsigned int period_step;
           unsigned int sleep_min;              /* min ticks to sleep */
           snd_pcm_uframes_t start_threshold;
-          snd_pcm_uframes_t stop_threshold;
-          snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
-                                                  noise is nearest than this */
-          snd_pcm_uframes_t silence_size;      /* Silence filling size */
+          /*
+           * The following two thresholds alleviate playback buffer underruns; when
+           * hw_avail drops below the threshold, the respective action is triggered:
+           */
+          snd_pcm_uframes_t stop_threshold;    /* - stop playback */
+          snd_pcm_uframes_t silence_threshold; /* - pre-fill buffer with silence */
+          snd_pcm_uframes_t silence_size;       /* max size of silence pre-fill; when >= boundary,
+                                                 * fill played area with silence immediately */
           snd_pcm_uframes_t boundary;  /* pointers wrap point */
   
-          snd_pcm_uframes_t silenced_start;
-          snd_pcm_uframes_t silenced_size;
+          /* internal data of auto-silencer */
+          snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
+          snd_pcm_uframes_t silence_filled; /* size filled with silence */
   
           snd_pcm_sync_id_t sync;              /* hardware synchronization ID */
   
@@ -1638,14 +1560,12 @@ Hardware Description
 
 The hardware descriptor (struct snd_pcm_hardware) contains the definitions of
 the fundamental hardware configuration. Above all, you'll need to define this
-in the `PCM open callback`_. Note that the runtime instance holds the copy of
-the descriptor, not the pointer to the existing descriptor. That is,
+in the `PCM open callback`_. Note that the runtime instance holds a copy of
+the descriptor, not a pointer to the existing descriptor. That is,
 in the open callback, you can modify the copied descriptor
 (``runtime->hw``) as you need. For example, if the maximum number of
 channels is 1 only on some chip models, you can still use the same
-hardware descriptor and change the channels_max later:
-
-::
+hardware descriptor and change the channels_max later::
 
           struct snd_pcm_runtime *runtime = substream->runtime;
           ...
@@ -1653,9 +1573,7 @@ hardware descriptor and change the channels_max later:
           if (chip->model == VERY_OLD_ONE)
                   runtime->hw.channels_max = 1;
 
-Typically, you'll have a hardware descriptor as below:
-
-::
+Typically, you'll have a hardware descriptor as below::
 
   static struct snd_pcm_hardware snd_mychip_playback_hw = {
           .info = (SNDRV_PCM_INFO_MMAP |
@@ -1676,51 +1594,51 @@ Typically, you'll have a hardware descriptor as below:
   };
 
 -  The ``info`` field contains the type and capabilities of this
-   pcm. The bit flags are defined in ``<sound/asound.h>`` as
+   PCM. The bit flags are defined in ``<sound/asound.h>`` as
    ``SNDRV_PCM_INFO_XXX``. Here, at least, you have to specify whether
-   the mmap is supported and which interleaved format is
+   mmap is supported and which interleaving formats are
    supported. When the hardware supports mmap, add the
    ``SNDRV_PCM_INFO_MMAP`` flag here. When the hardware supports the
-   interleaved or the non-interleaved formats,
+   interleaved or the non-interleaved formats, the
    ``SNDRV_PCM_INFO_INTERLEAVED`` or ``SNDRV_PCM_INFO_NONINTERLEAVED``
    flag must be set, respectively. If both are supported, you can set
    both, too.
 
    In the above example, ``MMAP_VALID`` and ``BLOCK_TRANSFER`` are
    specified for the OSS mmap mode. Usually both are set. Of course,
-   ``MMAP_VALID`` is set only if the mmap is really supported.
+   ``MMAP_VALID`` is set only if mmap is really supported.
 
    The other possible flags are ``SNDRV_PCM_INFO_PAUSE`` and
-   ``SNDRV_PCM_INFO_RESUME``. The ``PAUSE`` bit means that the pcm
+   ``SNDRV_PCM_INFO_RESUME``. The ``PAUSE`` bit means that the PCM
    supports the “pause” operation, while the ``RESUME`` bit means that
-   the pcm supports the full “suspend/resume” operation. If the
+   the PCM supports the full “suspend/resume” operation. If the
    ``PAUSE`` flag is set, the ``trigger`` callback below must handle
    the corresponding (pause push/release) commands. The suspend/resume
    trigger commands can be defined even without the ``RESUME``
-   flag. See `Power Management`_ section for details.
+   flag. See the `Power Management`_ section for details.
 
    When the PCM substreams can be synchronized (typically,
-   synchronized start/stop of a playback and a capture streams), you
+   synchronized start/stop of a playback and a capture stream), you
    can give ``SNDRV_PCM_INFO_SYNC_START``, too. In this case, you'll
    need to check the linked-list of PCM substreams in the trigger
-   callback. This will be described in the later section.
+   callback. This will be described in a later section.
 
--  ``formats`` field contains the bit-flags of supported formats
+-  The ``formats`` field contains the bit-flags of supported formats
    (``SNDRV_PCM_FMTBIT_XXX``). If the hardware supports more than one
    format, give all or'ed bits. In the example above, the signed 16bit
    little-endian format is specified.
 
--  ``rates`` field contains the bit-flags of supported rates
+-  The ``rates`` field contains the bit-flags of supported rates
    (``SNDRV_PCM_RATE_XXX``). When the chip supports continuous rates,
-   pass ``CONTINUOUS`` bit additionally. The pre-defined rate bits are
-   provided only for typical rates. If your chip supports
+   pass the ``CONTINUOUS`` bit additionally. The pre-defined rate bits
+   are provided only for typical rates. If your chip supports
    unconventional rates, you need to add the ``KNOT`` bit and set up
    the hardware constraint manually (explained later).
 
 -  ``rate_min`` and ``rate_max`` define the minimum and maximum sample
    rate. This should correspond somehow to ``rates`` bits.
 
--  ``channels_min`` and ``channels_max`` define, as you might already
+-  ``channels_min`` and ``channels_max`` define, as you might have already
    expected, the minimum and maximum number of channels.
 
 -  ``buffer_bytes_max`` defines the maximum buffer size in
@@ -1732,15 +1650,16 @@ Typically, you'll have a hardware descriptor as below:
    number of periods in the buffer.
 
    The “period” is a term that corresponds to a fragment in the OSS
-   world. The period defines the size at which a PCM interrupt is
-   generated. This size strongly depends on the hardware. Generally,
-   the smaller period size will give you more interrupts, that is,
-   more controls. In the case of capture, this size defines the input
-   latency. On the other hand, the whole buffer size defines the
-   output latency for the playback direction.
+   world. The period defines the point at which a PCM interrupt is
+   generated. This point strongly depends on the hardware. Generally,
+   a smaller period size will give you more interrupts, which results
+   in being able to fill/drain the buffer more timely. In the case of
+   capture, this size defines the input latency. On the other hand,
+   the whole buffer size defines the output latency for the playback
+   direction.
 
 -  There is also a field ``fifo_size``. This specifies the size of the
-   hardware FIFO, but currently it is neither used in the driver nor
+   hardware FIFO, but currently it is neither used by the drivers nor
    in the alsa-lib. So, you can ignore this field.
 
 PCM Configurations
@@ -1759,34 +1678,32 @@ One thing to be noted is that the configured buffer and period sizes
 are stored in “frames” in the runtime. In the ALSA world, ``1 frame =
 channels \* samples-size``. For conversion between frames and bytes,
 you can use the :c:func:`frames_to_bytes()` and
-:c:func:`bytes_to_frames()` helper functions.
-
-::
+:c:func:`bytes_to_frames()` helper functions::
 
   period_bytes = frames_to_bytes(runtime, runtime->period_size);
 
 Also, many software parameters (sw_params) are stored in frames, too.
-Please check the type of the field. ``snd_pcm_uframes_t`` is for the
-frames as unsigned integer while ``snd_pcm_sframes_t`` is for the
+Please check the type of the field. ``snd_pcm_uframes_t`` is for
+frames as unsigned integer while ``snd_pcm_sframes_t`` is for
 frames as signed integer.
 
 DMA Buffer Information
 ~~~~~~~~~~~~~~~~~~~~~~
 
-The DMA buffer is defined by the following four fields, ``dma_area``,
-``dma_addr``, ``dma_bytes`` and ``dma_private``. The ``dma_area``
+The DMA buffer is defined by the following four fields: ``dma_area``,
+``dma_addr``, ``dma_bytes`` and ``dma_private``. ``dma_area``
 holds the buffer pointer (the logical address). You can call
 :c:func:`memcpy()` from/to this pointer. Meanwhile, ``dma_addr`` holds
 the physical address of the buffer. This field is specified only when
-the buffer is a linear buffer. ``dma_bytes`` holds the size of buffer
-in bytes. ``dma_private`` is used for the ALSA DMA allocator.
+the buffer is a linear buffer. ``dma_bytes`` holds the size of the
+buffer in bytes. ``dma_private`` is used for the ALSA DMA allocator.
 
 If you use either the managed buffer allocation mode or the standard
 API function :c:func:`snd_pcm_lib_malloc_pages()` for allocating the buffer,
 these fields are set by the ALSA middle layer, and you should *not*
 change them by yourself. You can read them but not write them. On the
 other hand, if you want to allocate the buffer by yourself, you'll
-need to manage it in hw_params callback. At least, ``dma_bytes`` is
+need to manage it in the hw_params callback. At least, ``dma_bytes`` is
 mandatory. ``dma_area`` is necessary when the buffer is mmapped. If
 your driver doesn't support mmap, this field is not
 necessary. ``dma_addr`` is also optional. You can use dma_private as
@@ -1796,13 +1713,13 @@ Running Status
 ~~~~~~~~~~~~~~
 
 The running status can be referred via ``runtime->status``. This is
-the pointer to the struct snd_pcm_mmap_status record.
+a pointer to a struct snd_pcm_mmap_status record.
 For example, you can get the current
 DMA hardware pointer via ``runtime->status->hw_ptr``.
 
 The DMA application pointer can be referred via ``runtime->control``,
-which points to the struct snd_pcm_mmap_control record.
-However, accessing directly to this value is not recommended.
+which points to a struct snd_pcm_mmap_control record.
+However, accessing this value directly is not recommended.
 
 Private Data
 ~~~~~~~~~~~~
@@ -1811,11 +1728,10 @@ You can allocate a record for the substream and store it in
 ``runtime->private_data``. Usually, this is done in the `PCM open
 callback`_. Don't mix this with ``pcm->private_data``. The
 ``pcm->private_data`` usually points to the chip instance assigned
-statically at the creation of PCM, while the ``runtime->private_data``
-points to a dynamic data structure created at the PCM open
-callback.
-
-::
+statically at creation time of the PCM device, while
+``runtime->private_data``
+points to a dynamic data structure created in the PCM open
+callback::
 
   static int snd_xxx_open(struct snd_pcm_substream *substream)
   {
@@ -1832,20 +1748,18 @@ The allocated object must be released in the `close callback`_.
 Operators
 ---------
 
-OK, now let me give details about each pcm callback (``ops``). In
+OK, now let me give details about each PCM callback (``ops``). In
 general, every callback must return 0 if successful, or a negative
 error number such as ``-EINVAL``. To choose an appropriate error
 number, it is advised to check what value other parts of the kernel
 return when the same kind of request fails.
 
-The callback function takes at least the argument with
+Each callback function takes at least one argument containing a
 struct snd_pcm_substream pointer. To retrieve the chip
 record from the given substream instance, you can use the following
-macro.
-
-::
+macro::
 
-  int xxx() {
+  int xxx(...) {
           struct mychip *chip = snd_pcm_substream_chip(substream);
           ....
   }
@@ -1864,12 +1778,10 @@ PCM open callback
 
   static int snd_xxx_open(struct snd_pcm_substream *substream);
 
-This is called when a pcm substream is opened.
+This is called when a PCM substream is opened.
 
 At least, here you have to initialize the ``runtime->hw``
-record. Typically, this is done by like this:
-
-::
+record. Typically, this is done like this::
 
   static int snd_xxx_open(struct snd_pcm_substream *substream)
   {
@@ -1883,7 +1795,7 @@ record. Typically, this is done by like this:
 where ``snd_mychip_playback_hw`` is the pre-defined hardware
 description.
 
-You can allocate a private data in this callback, as described in
+You can allocate private data in this callback, as described in the
 `Private Data`_ section.
 
 If the hardware configuration needs more constraints, set the hardware
@@ -1897,12 +1809,10 @@ close callback
   static int snd_xxx_close(struct snd_pcm_substream *substream);
 
 
-Obviously, this is called when a pcm substream is closed.
-
-Any private instance for a pcm substream allocated in the ``open``
-callback will be released here.
+Obviously, this is called when a PCM substream is closed.
 
-::
+Any private instance for a PCM substream allocated in the ``open``
+callback will be released here::
 
   static int snd_xxx_close(struct snd_pcm_substream *substream)
   {
@@ -1914,9 +1824,9 @@ callback will be released here.
 ioctl callback
 ~~~~~~~~~~~~~~
 
-This is used for any special call to pcm ioctls. But usually you can
-leave it as NULL, then PCM core calls the generic ioctl callback
-function :c:func:`snd_pcm_lib_ioctl()`.  If you need to deal with the
+This is used for any special call to PCM ioctls. But usually you can
+leave it NULL, then the PCM core calls the generic ioctl callback
+function :c:func:`snd_pcm_lib_ioctl()`.  If you need to deal with a
 unique setup of channel info or reset procedure, you can pass your own
 callback function here.
 
@@ -1928,22 +1838,20 @@ hw_params callback
   static int snd_xxx_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *hw_params);
 
-This is called when the hardware parameter (``hw_params``) is set up
+This is called when the hardware parameters (``hw_params``) are set up
 by the application, that is, once when the buffer size, the period
-size, the format, etc. are defined for the pcm substream.
+size, the format, etc. are defined for the PCM substream.
 
 Many hardware setups should be done in this callback, including the
 allocation of buffers.
 
-Parameters to be initialized are retrieved by
+Parameters to be initialized are retrieved by the
 :c:func:`params_xxx()` macros.
 
-When you set up the managed buffer allocation mode for the substream,
+When you choose managed buffer allocation mode for the substream,
 a buffer is already allocated before this callback gets
 called. Alternatively, you can call a helper function below for
-allocating the buffer, too.
-
-::
+allocating the buffer::
 
   snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 
@@ -1951,8 +1859,8 @@ allocating the buffer, too.
 DMA buffers have been pre-allocated. See the section `Buffer Types`_
 for more details.
 
-Note that this and ``prepare`` callbacks may be called multiple times
-per initialization. For example, the OSS emulation may call these
+Note that this one and the ``prepare`` callback may be called multiple
+times per initialization. For example, the OSS emulation may call these
 callbacks at each change via its ioctl.
 
 Thus, you need to be careful not to allocate the same buffers many
@@ -1960,10 +1868,10 @@ times, which will lead to memory leaks! Calling the helper function
 above many times is OK. It will release the previous buffer
 automatically when it was already allocated.
 
-Another note is that this callback is non-atomic (schedulable) as
+Another note is that this callback is non-atomic (schedulable) by
 default, i.e. when no ``nonatomic`` flag set. This is important,
 because the ``trigger`` callback is atomic (non-schedulable). That is,
-mutexes or any schedule-related functions are not available in
+mutexes or any schedule-related functions are not available in the
 ``trigger`` callback. Please see the subsection Atomicity_ for
 details.
 
@@ -1979,16 +1887,14 @@ This is called to release the resources allocated via
 
 This function is always called before the close callback is called.
 Also, the callback may be called multiple times, too. Keep track
-whether the resource was already released.
+whether each resource was already released.
 
-When you have set up the managed buffer allocation mode for the PCM
+When you have chosen managed buffer allocation mode for the PCM
 substream, the allocated PCM buffer will be automatically released
 after this callback gets called.  Otherwise you'll have to release the
 buffer manually.  Typically, when the buffer was allocated from the
 pre-allocated pool, you can use the standard API function
-:c:func:`snd_pcm_lib_malloc_pages()` like:
-
-::
+:c:func:`snd_pcm_lib_malloc_pages()` like::
 
   snd_pcm_lib_free_pages(substream);
 
@@ -1999,13 +1905,13 @@ prepare callback
 
   static int snd_xxx_prepare(struct snd_pcm_substream *substream);
 
-This callback is called when the pcm is “prepared”. You can set the
+This callback is called when the PCM is “prepared”. You can set the
 format type, sample rate, etc. here. The difference from ``hw_params``
 is that the ``prepare`` callback will be called each time
 :c:func:`snd_pcm_prepare()` is called, i.e. when recovering after
 underruns, etc.
 
-Note that this callback is now non-atomic. You can use
+Note that this callback is non-atomic. You can use
 schedule-related functions safely in this callback.
 
 In this and the following callbacks, you can refer to the values via
@@ -2026,13 +1932,11 @@ trigger callback
 
   static int snd_xxx_trigger(struct snd_pcm_substream *substream, int cmd);
 
-This is called when the pcm is started, stopped or paused.
-
-Which action is specified in the second argument,
-``SNDRV_PCM_TRIGGER_XXX`` in ``<sound/pcm.h>``. At least, the ``START``
-and ``STOP`` commands must be defined in this callback.
+This is called when the PCM is started, stopped or paused.
 
-::
+The action is specified in the second argument, ``SNDRV_PCM_TRIGGER_XXX``
+defined in ``<sound/pcm.h>``. At least, the ``START``
+and ``STOP`` commands must be defined in this callback::
 
   switch (cmd) {
   case SNDRV_PCM_TRIGGER_START:
@@ -2045,23 +1949,23 @@ and ``STOP`` commands must be defined in this callback.
           return -EINVAL;
   }
 
-When the pcm supports the pause operation (given in the info field of
+When the PCM supports the pause operation (given in the info field of
 the hardware table), the ``PAUSE_PUSH`` and ``PAUSE_RELEASE`` commands
-must be handled here, too. The former is the command to pause the pcm,
-and the latter to restart the pcm again.
+must be handled here, too. The former is the command to pause the PCM,
+and the latter to restart the PCM again.
 
-When the pcm supports the suspend/resume operation, regardless of full
+When the PCM supports the suspend/resume operation, regardless of full
 or partial suspend/resume support, the ``SUSPEND`` and ``RESUME``
 commands must be handled, too. These commands are issued when the
 power-management status is changed. Obviously, the ``SUSPEND`` and
-``RESUME`` commands suspend and resume the pcm substream, and usually,
+``RESUME`` commands suspend and resume the PCM substream, and usually,
 they are identical to the ``STOP`` and ``START`` commands, respectively.
 See the `Power Management`_ section for details.
 
-As mentioned, this callback is atomic as default unless ``nonatomic``
+As mentioned, this callback is atomic by default unless the ``nonatomic``
 flag set, and you cannot call functions which may sleep. The
 ``trigger`` callback should be as minimal as possible, just really
-triggering the DMA. The other stuff should be initialized
+triggering the DMA. The other stuff should be initialized in
 ``hw_params`` and ``prepare`` callbacks properly beforehand.
 
 sync_stop callback
@@ -2072,22 +1976,22 @@ sync_stop callback
   static int snd_xxx_sync_stop(struct snd_pcm_substream *substream);
 
 This callback is optional, and NULL can be passed.  It's called after
-the PCM core stops the stream and changes the stream state
+the PCM core stops the stream, before it changes the stream state via
 ``prepare``, ``hw_params`` or ``hw_free``.
 Since the IRQ handler might be still pending, we need to wait until
 the pending task finishes before moving to the next step; otherwise it
-might lead to a crash due to resource conflicts or access to the freed
+might lead to a crash due to resource conflicts or access to freed
 resources.  A typical behavior is to call a synchronization function
 like :c:func:`synchronize_irq()` here.
 
-For majority of drivers that need only a call of
+For the majority of drivers that need only a call of
 :c:func:`synchronize_irq()`, there is a simpler setup, too.
-While keeping NULL to ``sync_stop`` PCM callback, the driver can set
-``card->sync_irq`` field to store the valid interrupt number after
-requesting an IRQ, instead.   Then PCM core will look call
+While keeping the ``sync_stop`` PCM callback NULL, the driver can set
+the ``card->sync_irq`` field to the returned interrupt number after
+requesting an IRQ, instead.   Then PCM core will call
 :c:func:`synchronize_irq()` with the given IRQ appropriately.
 
-If the IRQ handler is released at the card destructor, you don't need
+If the IRQ handler is released by the card destructor, you don't need
 to clear ``card->sync_irq``, as the card itself is being released.
 So, usually you'll need to add just a single line for assigning
 ``card->sync_irq`` in the driver code unless the driver re-acquires
@@ -2103,30 +2007,30 @@ pointer callback
   static snd_pcm_uframes_t snd_xxx_pointer(struct snd_pcm_substream *substream)
 
 This callback is called when the PCM middle layer inquires the current
-hardware position on the buffer. The position must be returned in
+hardware position in the buffer. The position must be returned in
 frames, ranging from 0 to ``buffer_size - 1``. 
 
-This is called usually from the buffer-update routine in the pcm
+This is usually called from the buffer-update routine in the PCM
 middle layer, which is invoked when :c:func:`snd_pcm_period_elapsed()`
-is called in the interrupt routine. Then the pcm middle layer updates
+is called by the interrupt routine. Then the PCM middle layer updates
 the position and calculates the available space, and wakes up the
 sleeping poll threads, etc.
 
-This callback is also atomic as default.
+This callback is also atomic by default.
 
 copy_user, copy_kernel and fill_silence ops
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 These callbacks are not mandatory, and can be omitted in most cases.
 These callbacks are used when the hardware buffer cannot be in the
-normal memory space. Some chips have their own buffer on the hardware
+normal memory space. Some chips have their own buffer in the hardware
 which is not mappable. In such a case, you have to transfer the data
 manually from the memory buffer to the hardware buffer. Or, if the
 buffer is non-contiguous on both physical and virtual memory spaces,
 these callbacks must be defined, too.
 
 If these two callbacks are defined, copy and set-silence operations
-are done by them. The detailed will be described in the later section
+are done by them. The details will be described in the later section
 `Buffer and Memory Management`_.
 
 ack callback
@@ -2137,7 +2041,11 @@ This callback is also not mandatory. This callback is called when the
 emu10k1-fx and cs46xx need to track the current ``appl_ptr`` for the
 internal buffer, and this callback is useful only for such a purpose.
 
-This callback is atomic as default.
+The callback function may return 0 or a negative error. When the
+return value is ``-EPIPE``, PCM core treats that as a buffer XRUN,
+and changes the state to ``SNDRV_PCM_STATE_XRUN`` automatically.
+
+This callback is atomic by default.
 
 page callback
 ~~~~~~~~~~~~~
@@ -2145,16 +2053,15 @@ page callback
 This callback is optional too. The mmap calls this callback to get the
 page fault address.
 
-Since the recent changes, you need no special callback any longer for
-the standard SG-buffer or vmalloc-buffer. Hence this callback should
-be rarely used.
+You need no special callback for the standard SG-buffer or vmalloc-
+buffer. Hence this callback should be rarely used.
 
-mmap calllback
-~~~~~~~~~~~~~~
+mmap callback
+~~~~~~~~~~~~~
 
 This is another optional callback for controlling mmap behavior.
-Once when defined, PCM core calls this callback when a page is
-memory-mapped instead of dealing via the standard helper.
+When defined, the PCM core calls this callback when a page is
+memory-mapped, instead of using the standard helper.
 If you need special handling (due to some architecture or
 device-specific issues), implement everything here as you like.
 
@@ -2162,13 +2069,14 @@ device-specific issues), implement everything here as you like.
 PCM Interrupt Handler
 ---------------------
 
-The rest of pcm stuff is the PCM interrupt handler. The role of PCM
+The remainder of the PCM stuff is the PCM interrupt handler. The role
+of the PCM
 interrupt handler in the sound driver is to update the buffer position
 and to tell the PCM middle layer when the buffer position goes across
-the prescribed period size. To inform this, call the
+the specified period boundary. To inform about this, call the
 :c:func:`snd_pcm_period_elapsed()` function.
 
-There are several types of sound chips to generate the interrupts.
+There are several ways sound chips can generate interrupts.
 
 Interrupts at the period (fragment) boundary
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2184,14 +2092,12 @@ chip record to hold the current running substream pointer, and set the
 pointer value at ``open`` callback (and reset at ``close`` callback).
 
 If you acquire a spinlock in the interrupt handler, and the lock is used
-in other pcm callbacks, too, then you have to release the lock before
+in other PCM callbacks, too, then you have to release the lock before
 calling :c:func:`snd_pcm_period_elapsed()`, because
-:c:func:`snd_pcm_period_elapsed()` calls other pcm callbacks
+:c:func:`snd_pcm_period_elapsed()` calls other PCM callbacks
 inside.
 
-Typical code would be like:
-
-::
+Typical code would look like::
 
 
       static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
@@ -2211,6 +2117,12 @@ Typical code would be like:
               return IRQ_HANDLED;
       }
 
+Also, when the device can detect a buffer underrun/overrun, the driver
+can notify the XRUN status to the PCM core by calling
+:c:func:`snd_pcm_stop_xrun()`. This function stops the stream and sets
+the PCM state to ``SNDRV_PCM_STATE_XRUN``. Note that it must be called
+outside the PCM stream lock, hence it can't be called from the atomic
+callback.
 
 
 High frequency timer interrupts
@@ -2223,9 +2135,7 @@ position and accumulate the processed sample length at each interrupt.
 When the accumulated size exceeds the period size, call
 :c:func:`snd_pcm_period_elapsed()` and reset the accumulator.
 
-Typical code would be like the following.
-
-::
+Typical code would look as follows::
 
 
       static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
@@ -2270,9 +2180,9 @@ Typical code would be like the following.
 On calling :c:func:`snd_pcm_period_elapsed()`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-In both cases, even if more than one period are elapsed, you don't have
+In both cases, even if more than one period has elapsed, you don't have
 to call :c:func:`snd_pcm_period_elapsed()` many times. Call only
-once. And the pcm layer will check the current hardware pointer and
+once. And the PCM layer will check the current hardware pointer and
 update to the latest status.
 
 Atomicity
@@ -2283,15 +2193,16 @@ kernel programming are race conditions. In the Linux kernel, they are
 usually avoided via spin-locks, mutexes or semaphores. In general, if a
 race condition can happen in an interrupt handler, it has to be managed
 atomically, and you have to use a spinlock to protect the critical
-session. If the critical section is not in interrupt handler code and if
+section. If the critical section is not in interrupt handler code and if
 taking a relatively long time to execute is acceptable, you should use
 mutexes or semaphores instead.
 
-As already seen, some pcm callbacks are atomic and some are not. For
-example, the ``hw_params`` callback is non-atomic, while ``trigger``
+As already seen, some PCM callbacks are atomic and some are not. For
+example, the ``hw_params`` callback is non-atomic, while the ``trigger``
 callback is atomic. This means, the latter is called already in a
-spinlock held by the PCM middle layer. Please take this atomicity into
-account when you choose a locking scheme in the callbacks.
+spinlock held by the PCM middle layer, the PCM stream lock. Please
+take this atomicity into account when you choose a locking scheme in
+the callbacks.
 
 In the atomic callbacks, you cannot use functions which may call
 :c:func:`schedule()` or go to :c:func:`sleep()`. Semaphores and
@@ -2302,29 +2213,34 @@ callback, please use :c:func:`udelay()` or :c:func:`mdelay()`.
 All three atomic callbacks (trigger, pointer, and ack) are called with
 local interrupts disabled.
 
-The recent changes in PCM core code, however, allow all PCM operations
-to be non-atomic. This assumes that the all caller sides are in
+However, it is possible to request all PCM operations to be non-atomic.
+This assumes that all call sites are in
 non-atomic contexts. For example, the function
 :c:func:`snd_pcm_period_elapsed()` is called typically from the
 interrupt handler. But, if you set up the driver to use a threaded
 interrupt handler, this call can be in non-atomic context, too. In such
-a case, you can set ``nonatomic`` filed of struct snd_pcm object
+a case, you can set the ``nonatomic`` field of the struct snd_pcm object
 after creating it. When this flag is set, mutex and rwsem are used internally
 in the PCM core instead of spin and rwlocks, so that you can call all PCM
 functions safely in a non-atomic
 context.
 
+Also, in some cases, you might need to call
+:c:func:`snd_pcm_period_elapsed()` in the atomic context (e.g. the
+period gets elapsed during ``ack`` or other callback). There is a
+variant that can be called inside the PCM stream lock
+:c:func:`snd_pcm_period_elapsed_under_stream_lock()` for that purpose,
+too.
+
 Constraints
 -----------
 
-If your chip supports unconventional sample rates, or only the limited
-samples, you need to set a constraint for the condition.
+Due to physical limitations, hardware is not infinitely configurable.
+These limitations are expressed by setting constraints.
 
-For example, in order to restrict the sample rates in the some supported
+For example, in order to restrict the sample rates to some supported
 values, use :c:func:`snd_pcm_hw_constraint_list()`. You need to
-call this function in the open callback.
-
-::
+call this function in the open callback::
 
       static unsigned int rates[] =
               {4000, 10000, 22050, 44100};
@@ -2346,16 +2262,12 @@ call this function in the open callback.
               ....
       }
 
-
-
 There are many different constraints. Look at ``sound/pcm.h`` for a
 complete list. You can even define your own constraint rules. For
 example, let's suppose my_chip can manage a substream of 1 channel if
 and only if the format is ``S16_LE``, otherwise it supports any format
-specified in struct snd_pcm_hardware> (or in any other
-constraint_list). You can build a rule like this:
-
-::
+specified in struct snd_pcm_hardware (or in any other
+constraint_list). You can build a rule like this::
 
       static int hw_rule_channels_by_format(struct snd_pcm_hw_params *params,
                                             struct snd_pcm_hw_rule *rule)
@@ -2375,9 +2287,7 @@ constraint_list). You can build a rule like this:
       }
 
 
-Then you need to call this function to add your rule:
-
-::
+Then you need to call this function to add your rule::
 
   snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
                       hw_rule_channels_by_format, NULL,
@@ -2386,9 +2296,7 @@ Then you need to call this function to add your rule:
 The rule function is called when an application sets the PCM format, and
 it refines the number of channels accordingly. But an application may
 set the number of channels before setting the format. Thus you also need
-to define the inverse rule:
-
-::
+to define the inverse rule::
 
       static int hw_rule_format_by_channels(struct snd_pcm_hw_params *params,
                                             struct snd_pcm_hw_rule *rule)
@@ -2407,16 +2315,14 @@ to define the inverse rule:
       }
 
 
-... and in the open callback:
-
-::
+... and in the open callback::
 
   snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
                       hw_rule_format_by_channels, NULL,
                       SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 
 One typical usage of the hw constraints is to align the buffer size
-with the period size.  As default, ALSA PCM core doesn't enforce the
+with the period size.  By default, ALSA PCM core doesn't enforce the
 buffer size to be aligned with the period size.  For example, it'd be
 possible to have a combination like 256 period bytes with 999 buffer
 bytes.
@@ -2424,9 +2330,7 @@ bytes.
 Many device chips, however, require the buffer to be a multiple of
 periods.  In such a case, call
 :c:func:`snd_pcm_hw_constraint_integer()` for
-``SNDRV_PCM_HW_PARAM_PERIODS``.
-
-::
+``SNDRV_PCM_HW_PARAM_PERIODS``::
 
   snd_pcm_hw_constraint_integer(substream->runtime,
                                 SNDRV_PCM_HW_PARAM_PERIODS);
@@ -2434,7 +2338,7 @@ periods.  In such a case, call
 This assures that the number of periods is integer, hence the buffer
 size is aligned with the period size.
 
-The hw constraint is a very much powerful mechanism to define the
+The hw constraint is a very powerful mechanism to define the
 preferred PCM configuration, and there are relevant helpers.
 I won't give more details here, rather I would like to say, “Luke, use
 the source.”
@@ -2461,9 +2365,7 @@ Definition of Controls
 
 To create a new control, you need to define the following three
 callbacks: ``info``, ``get`` and ``put``. Then, define a
-struct snd_kcontrol_new record, such as:
-
-::
+struct snd_kcontrol_new record, such as::
 
 
       static struct snd_kcontrol_new my_control = {
@@ -2506,7 +2408,7 @@ The ``private_value`` field contains an arbitrary long integer value
 for this record. When using the generic ``info``, ``get`` and ``put``
 callbacks, you can pass a value through this field. If several small
 numbers are necessary, you can combine them in bitwise. Or, it's
-possible to give a pointer (casted to unsigned long) of some record to
+possible to store a pointer (casted to unsigned long) of some record in
 this field, too. 
 
 The ``tlv`` field can be used to provide metadata about the control;
@@ -2573,7 +2475,7 @@ The access flag is the bitmask which specifies the access type of the
 given control. The default access type is
 ``SNDRV_CTL_ELEM_ACCESS_READWRITE``, which means both read and write are
 allowed to this control. When the access flag is omitted (i.e. = 0), it
-is considered as ``READWRITE`` access as default.
+is considered as ``READWRITE`` access by default.
 
 When the control is read-only, pass ``SNDRV_CTL_ELEM_ACCESS_READ``
 instead. In this case, you don't have to define the ``put`` callback.
@@ -2586,8 +2488,11 @@ If the control value changes frequently (e.g. the VU meter),
 changed without `Change notification`_. Applications should poll such
 a control constantly.
 
-When the control is inactive, set the ``INACTIVE`` flag, too. There are
-``LOCK`` and ``OWNER`` flags to change the write permissions.
+When the control may be updated, but currently has no effect on anything,
+setting the ``INACTIVE`` flag may be appropriate. For example, PCM
+controls should be inactive while no PCM device is open.
+
+There are ``LOCK`` and ``OWNER`` flags to change the write permissions.
 
 Control Callbacks
 -----------------
@@ -2598,9 +2503,7 @@ info callback
 The ``info`` callback is used to get detailed information on this
 control. This must store the values of the given
 struct snd_ctl_elem_info object. For example,
-for a boolean control with a single element:
-
-::
+for a boolean control with a single element::
 
 
       static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
@@ -2619,13 +2522,11 @@ The ``type`` field specifies the type of the control. There are
 ``BOOLEAN``, ``INTEGER``, ``ENUMERATED``, ``BYTES``, ``IEC958`` and
 ``INTEGER64``. The ``count`` field specifies the number of elements in
 this control. For example, a stereo volume would have count = 2. The
-``value`` field is a union, and the values stored are depending on the
+``value`` field is a union, and the values stored depend on the
 type. The boolean and integer types are identical.
 
-The enumerated type is a bit different from others. You'll need to set
-the string for the currently given item index.
-
-::
+The enumerated type is a bit different from the others. You'll need to
+set the string for the selectec item index::
 
   static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_info *uinfo)
@@ -2670,13 +2571,10 @@ stereo channel boolean item.
 get callback
 ~~~~~~~~~~~~
 
-This callback is used to read the current value of the control and to
-return to user-space.
-
-For example,
-
-::
+This callback is used to read the current value of the control, so it
+can be returned to user-space.
 
+For example::
 
       static int snd_myctl_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -2691,15 +2589,11 @@ For example,
 The ``value`` field depends on the type of control as well as on the
 info callback. For example, the sb driver uses this field to store the
 register offset, the bit-shift and the bit-mask. The ``private_value``
-field is set as follows:
-
-::
+field is set as follows::
 
   .private_value = reg | (shift << 16) | (mask << 24)
 
-and is retrieved in callbacks like
-
-::
+and is retrieved in callbacks like::
 
   static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
@@ -2711,19 +2605,16 @@ and is retrieved in callbacks like
   }
 
 In the ``get`` callback, you have to fill all the elements if the
-control has more than one elements, i.e. ``count > 1``. In the example
+control has more than one element, i.e. ``count > 1``. In the example
 above, we filled only one element (``value.integer.value[0]``) since
-it's assumed as ``count = 1``.
+``count = 1`` is assumed.
 
 put callback
 ~~~~~~~~~~~~
 
-This callback is used to write a value from user-space.
-
-For example,
-
-::
+This callback is used to write a value coming from user-space.
 
+For example::
 
       static int snd_myctl_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -2746,12 +2637,12 @@ value is not changed, return 0 instead. If any fatal error happens,
 return a negative error code as usual.
 
 As in the ``get`` callback, when the control has more than one
-elements, all elements must be evaluated in this callback, too.
+element, all elements must be evaluated in this callback, too.
 
 Callbacks are not atomic
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
-All these three callbacks are basically not atomic.
+All these three callbacks are not-atomic.
 
 Control Constructor
 -------------------
@@ -2760,9 +2651,7 @@ When everything is ready, finally we can create a new control. To create
 a control, there are two functions to be called,
 :c:func:`snd_ctl_new1()` and :c:func:`snd_ctl_add()`.
 
-In the simplest way, you can do like this:
-
-::
+In the simplest way, you can do it like this::
 
   err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
   if (err < 0)
@@ -2780,9 +2669,7 @@ Change Notification
 -------------------
 
 If you need to change and update a control in the interrupt routine, you
-can call :c:func:`snd_ctl_notify()`. For example,
-
-::
+can call :c:func:`snd_ctl_notify()`. For example::
 
   snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer);
 
@@ -2796,13 +2683,11 @@ for hardware volume interrupts.
 Metadata
 --------
 
-To provide information about the dB values of a mixer control, use on of
+To provide information about the dB values of a mixer control, use one of
 the ``DECLARE_TLV_xxx`` macros from ``<sound/tlv.h>`` to define a
 variable containing this information, set the ``tlv.p`` field to point to
 this variable, and include the ``SNDRV_CTL_ELEM_ACCESS_TLV_READ`` flag
-in the ``access`` field; like this:
-
-::
+in the ``access`` field; like this::
 
   static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);
 
@@ -2892,9 +2777,7 @@ AC97 Constructor
 ----------------
 
 To create an ac97 instance, first call :c:func:`snd_ac97_bus()`
-with an ``ac97_bus_ops_t`` record with callback functions.
-
-::
+with an ``ac97_bus_ops_t`` record with callback functions::
 
   struct snd_ac97_bus *bus;
   static struct snd_ac97_bus_ops ops = {
@@ -2906,10 +2789,8 @@ with an ``ac97_bus_ops_t`` record with callback functions.
 
 The bus record is shared among all belonging ac97 instances.
 
-And then call :c:func:`snd_ac97_mixer()` with an struct snd_ac97_template
-record together with the bus pointer created above.
-
-::
+And then call :c:func:`snd_ac97_mixer()` with a struct snd_ac97_template
+record together with the bus pointer created above::
 
   struct snd_ac97_template ac97;
   int err;
@@ -2934,9 +2815,7 @@ correspond to the functions for read and write accesses to the
 hardware low-level codes.
 
 The ``read`` callback returns the register value specified in the
-argument.
-
-::
+argument::
 
   static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
                                              unsigned short reg)
@@ -2949,9 +2828,7 @@ argument.
 Here, the chip can be cast from ``ac97->private_data``.
 
 Meanwhile, the ``write`` callback is used to set the register
-value
-
-::
+value::
 
   static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
                        unsigned short reg, unsigned short val)
@@ -2984,32 +2861,24 @@ Both :c:func:`snd_ac97_write()` and
 the given register (``AC97_XXX``). The difference between them is that
 :c:func:`snd_ac97_update()` doesn't write a value if the given
 value has been already set, while :c:func:`snd_ac97_write()`
-always rewrites the value.
-
-::
+always rewrites the value::
 
   snd_ac97_write(ac97, AC97_MASTER, 0x8080);
   snd_ac97_update(ac97, AC97_MASTER, 0x8080);
 
 :c:func:`snd_ac97_read()` is used to read the value of the given
-register. For example,
-
-::
+register. For example::
 
   value = snd_ac97_read(ac97, AC97_MASTER);
 
 :c:func:`snd_ac97_update_bits()` is used to update some bits in
-the given register.
-
-::
+the given register::
 
   snd_ac97_update_bits(ac97, reg, mask, value);
 
 Also, there is a function to change the sample rate (of a given register
 such as ``AC97_PCM_FRONT_DAC_RATE``) when VRA or DRA is supported by the
-codec: :c:func:`snd_ac97_set_rate()`.
-
-::
+codec: :c:func:`snd_ac97_set_rate()`::
 
   snd_ac97_set_rate(ac97, AC97_PCM_FRONT_DAC_RATE, 44100);
 
@@ -3064,9 +2933,7 @@ mpu401 stuff. For example, emu10k1 has its own mpu401 routines.
 MIDI Constructor
 ----------------
 
-To create a rawmidi object, call :c:func:`snd_mpu401_uart_new()`.
-
-::
+To create a rawmidi object, call :c:func:`snd_mpu401_uart_new()`::
 
   struct snd_rawmidi *rmidi;
   snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, info_flags,
@@ -3111,16 +2978,12 @@ corresponds to the data port. If not, you may change the ``cport``
 field of struct snd_mpu401 manually afterward.
 However, struct snd_mpu401 pointer is
 not returned explicitly by :c:func:`snd_mpu401_uart_new()`. You
-need to cast ``rmidi->private_data`` to struct snd_mpu401 explicitly,
-
-::
+need to cast ``rmidi->private_data`` to struct snd_mpu401 explicitly::
 
   struct snd_mpu401 *mpu;
   mpu = rmidi->private_data;
 
-and reset the ``cport`` as you like:
-
-::
+and reset the ``cport`` as you like::
 
   mpu->cport = my_own_control_port;
 
@@ -3144,9 +3007,7 @@ occurred.
 
 In this case, you need to pass the private_data of the returned rawmidi
 object from :c:func:`snd_mpu401_uart_new()` as the second
-argument of :c:func:`snd_mpu401_uart_interrupt()`.
-
-::
+argument of :c:func:`snd_mpu401_uart_interrupt()`::
 
   snd_mpu401_uart_interrupt(irq, rmidi->private_data, regs);
 
@@ -3170,9 +3031,7 @@ RawMIDI Constructor
 -------------------
 
 To create a rawmidi device, call the :c:func:`snd_rawmidi_new()`
-function:
-
-::
+function::
 
   struct snd_rawmidi *rmidi;
   err = snd_rawmidi_new(chip->card, "MyMIDI", 0, outs, ins, &rmidi);
@@ -3202,16 +3061,12 @@ output and input at the same time.
 
 After the rawmidi device is created, you need to set the operators
 (callbacks) for each substream. There are helper functions to set the
-operators for all the substreams of a device:
-
-::
+operators for all the substreams of a device::
 
   snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mymidi_output_ops);
   snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mymidi_input_ops);
 
-The operators are usually defined like this:
-
-::
+The operators are usually defined like this::
 
   static struct snd_rawmidi_ops snd_mymidi_output_ops = {
           .open =    snd_mymidi_output_open,
@@ -3222,9 +3077,7 @@ The operators are usually defined like this:
 These callbacks are explained in the `RawMIDI Callbacks`_ section.
 
 If there are more than one substream, you should give a unique name to
-each of them:
-
-::
+each of them::
 
   struct snd_rawmidi_substream *substream;
   list_for_each_entry(substream,
@@ -3242,9 +3095,7 @@ device can be accessed as ``substream->rmidi->private_data``.
 
 If there is more than one port, your callbacks can determine the port
 index from the struct snd_rawmidi_substream data passed to each
-callback:
-
-::
+callback::
 
   struct snd_rawmidi_substream *substream;
   int index = substream->number;
@@ -3289,9 +3140,7 @@ of bytes that have been read; this will be less than the number of bytes
 requested when there are no more data in the buffer. After the data have
 been transmitted successfully, call
 :c:func:`snd_rawmidi_transmit_ack()` to remove the data from the
-substream buffer:
-
-::
+substream buffer::
 
   unsigned char data;
   while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
@@ -3303,9 +3152,7 @@ substream buffer:
 
 If you know beforehand that the hardware will accept data, you can use
 the :c:func:`snd_rawmidi_transmit()` function which reads some
-data and removes them from the buffer at once:
-
-::
+data and removes them from the buffer at once::
 
   while (snd_mychip_transmit_possible()) {
           unsigned char data;
@@ -3340,9 +3187,7 @@ The ``trigger`` callback must not sleep; the actual reading of data
 from the device is usually done in an interrupt handler.
 
 When data reception is enabled, your interrupt handler should call
-:c:func:`snd_rawmidi_receive()` for all received data:
-
-::
+:c:func:`snd_rawmidi_receive()` for all received data::
 
   void snd_mychip_midi_interrupt(...)
   {
@@ -3388,9 +3233,7 @@ whereas in OSS compatible mode, FM registers can be accessed with the
 OSS direct-FM compatible API in ``/dev/dmfmX`` device.
 
 To create the OPL3 component, you have two functions to call. The first
-one is a constructor for the ``opl3_t`` instance.
-
-::
+one is a constructor for the ``opl3_t`` instance::
 
   struct snd_opl3 *opl3;
   snd_opl3_create(card, lport, rport, OPL3_HW_OPL3_XXX,
@@ -3408,9 +3251,7 @@ the opl3 module will allocate the specified ports by itself.
 
 When the accessing the hardware requires special method instead of the
 standard I/O access, you can create opl3 instance separately with
-:c:func:`snd_opl3_new()`.
-
-::
+:c:func:`snd_opl3_new()`::
 
   struct snd_opl3 *opl3;
   snd_opl3_new(card, OPL3_HW_OPL3_XXX, &opl3);
@@ -3427,9 +3268,7 @@ proper state. Note that :c:func:`snd_opl3_create()` always calls
 it internally.
 
 If the opl3 instance is created successfully, then create a hwdep device
-for this opl3.
-
-::
+for this opl3::
 
   struct snd_hwdep *opl3hwdep;
   snd_opl3_hwdep_new(opl3, 0, 1, &opl3hwdep);
@@ -3451,9 +3290,7 @@ the micro code. In such a case, you can create a hwdep
 ``isa/sb/sb16_csp.c``.
 
 The creation of the ``hwdep`` instance is done via
-:c:func:`snd_hwdep_new()`.
-
-::
+:c:func:`snd_hwdep_new()`::
 
   struct snd_hwdep *hw;
   snd_hwdep_new(card, "My HWDEP", 0, &hw);
@@ -3461,18 +3298,14 @@ The creation of the ``hwdep`` instance is done via
 where the third argument is the index number.
 
 You can then pass any pointer value to the ``private_data``. If you
-assign a private data, you should define the destructor, too. The
-destructor function is set in the ``private_free`` field.
-
-::
+assign private data, you should define a destructor, too. The
+destructor function is set in the ``private_free`` field::
 
   struct mydata *p = kmalloc(sizeof(*p), GFP_KERNEL);
   hw->private_data = p;
   hw->private_free = mydata_free;
 
-and the implementation of the destructor would be:
-
-::
+and the implementation of the destructor would be::
 
   static void mydata_free(struct snd_hwdep *hw)
   {
@@ -3482,9 +3315,7 @@ and the implementation of the destructor would be:
 
 The arbitrary file operations can be defined for this instance. The file
 operators are defined in the ``ops`` table. For example, assume that
-this chip needs an ioctl.
-
-::
+this chip needs an ioctl::
 
   hw->ops.open = mydata_open;
   hw->ops.ioctl = mydata_ioctl;
@@ -3534,31 +3365,30 @@ Buffer Types
 
 ALSA provides several different buffer allocation functions depending on
 the bus and the architecture. All these have a consistent API. The
-allocation of physically-contiguous pages is done via
+allocation of physically-contiguous pages is done via the
 :c:func:`snd_malloc_xxx_pages()` function, where xxx is the bus
 type.
 
-The allocation of pages with fallback is
-:c:func:`snd_malloc_xxx_pages_fallback()`. This function tries
-to allocate the specified pages but if the pages are not available, it
-tries to reduce the page sizes until enough space is found.
+The allocation of pages with fallback is done via
+:c:func:`snd_dma_alloc_pages_fallback()`. This function tries
+to allocate the specified number of pages, but if not enough pages are
+available, it tries to reduce the request size until enough space
+is found, down to one page.
 
-The release the pages, call :c:func:`snd_free_xxx_pages()`
+To release the pages, call the :c:func:`snd_dma_free_pages()`
 function.
 
 Usually, ALSA drivers try to allocate and reserve a large contiguous
-physical space at the time the module is loaded for the later use. This
+physical space at the time the module is loaded for later use. This
 is called “pre-allocation”. As already written, you can call the
-following function at pcm instance construction time (in the case of PCI
-bus).
-
-::
+following function at PCM instance construction time (in the case of PCI
+bus)::
 
   snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                         &pci->dev, size, max);
 
-where ``size`` is the byte size to be pre-allocated and the ``max`` is
-the maximum size to be changed via the ``prealloc`` proc file. The
+where ``size`` is the byte size to be pre-allocated and ``max`` is
+the maximum size settable via the ``prealloc`` proc file. The
 allocator will try to get an area as large as possible within the
 given size.
 
@@ -3567,10 +3397,10 @@ dependent on the bus. For normal devices, pass the device pointer
 (typically identical as ``card->dev``) to the third argument with
 ``SNDRV_DMA_TYPE_DEV`` type.
 
-For the continuous buffer unrelated to the
+A continuous buffer unrelated to the
 bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type.
 You can pass NULL to the device pointer in that case, which is the
-default mode implying to allocate with ``GFP_KERNEL`` flag.
+default mode implying to allocate with the ``GFP_KERNEL`` flag.
 If you need a restricted (lower) address, set up the coherent DMA mask
 bits for the device, and pass the device pointer, like the normal
 device memory allocations.  For this type, it's still allowed to pass
@@ -3580,37 +3410,33 @@ For the scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with the
 device pointer (see the `Non-Contiguous Buffers`_ section).
 
 Once the buffer is pre-allocated, you can use the allocator in the
-``hw_params`` callback:
-
-::
+``hw_params`` callback::
 
   snd_pcm_lib_malloc_pages(substream, size);
 
 Note that you have to pre-allocate to use this function.
 
-Most of drivers use, though, rather the newly introduced "managed
-buffer allocation mode" instead of the manual allocation or release.
+But most drivers use the "managed buffer allocation mode" instead
+of manual allocation and release.
 This is done by calling :c:func:`snd_pcm_set_managed_buffer_all()`
-instead of :c:func:`snd_pcm_lib_preallocate_pages_for_all()`.
-
-::
+instead of :c:func:`snd_pcm_lib_preallocate_pages_for_all()`::
 
   snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
                                  &pci->dev, size, max);
 
-where passed arguments are identical in both functions.
+where the passed arguments are identical for both functions.
 The difference in the managed mode is that PCM core will call
 :c:func:`snd_pcm_lib_malloc_pages()` internally already before calling
 the PCM ``hw_params`` callback, and call :c:func:`snd_pcm_lib_free_pages()`
 after the PCM ``hw_free`` callback automatically.  So the driver
 doesn't have to call these functions explicitly in its callback any
-longer.  This made many driver code having NULL ``hw_params`` and
+longer.  This allows many drivers to have NULL ``hw_params`` and
 ``hw_free`` entries.
 
 External Hardware Buffers
 -------------------------
 
-Some chips have their own hardware buffers and the DMA transfer from the
+Some chips have their own hardware buffers and DMA transfer from the
 host memory is not available. In such a case, you need to either 1)
 copy/set the audio data directly to the external hardware buffer, or 2)
 make an intermediate buffer and copy/set the data from it to the
@@ -3618,8 +3444,8 @@ external hardware buffer in interrupts (or in tasklets, preferably).
 
 The first case works fine if the external hardware buffer is large
 enough. This method doesn't need any extra buffers and thus is more
-effective. You need to define the ``copy_user`` and ``copy_kernel``
-callbacks for the data transfer, in addition to ``fill_silence``
+efficient. You need to define the ``copy_user`` and ``copy_kernel``
+callbacks for the data transfer, in addition to the ``fill_silence``
 callback for playback. However, there is a drawback: it cannot be
 mmapped. The examples are GUS's GF1 PCM or emu8000's wavetable PCM.
 
@@ -3633,16 +3459,14 @@ buffer instead of the host memory. In this case, mmap is available only
 on certain architectures like the Intel one. In non-mmap mode, the data
 cannot be transferred as in the normal way. Thus you need to define the
 ``copy_user``, ``copy_kernel`` and ``fill_silence`` callbacks as well,
-as in the cases above. The examples are found in ``rme32.c`` and
+as in the cases above. Examples are found in ``rme32.c`` and
 ``rme96.c``.
 
 The implementation of the ``copy_user``, ``copy_kernel`` and
 ``silence`` callbacks depends upon whether the hardware supports
 interleaved or non-interleaved samples. The ``copy_user`` callback is
-defined like below, a bit differently depending whether the direction
-is playback or capture:
-
-::
+defined like below, a bit differently depending on whether the direction
+is playback or capture::
 
   static int playback_copy_user(struct snd_pcm_substream *substream,
                int channel, unsigned long pos,
@@ -3652,8 +3476,7 @@ is playback or capture:
                void __user *dst, unsigned long count);
 
 In the case of interleaved samples, the second argument (``channel``) is
-not used. The third argument (``pos``) points the current position
-offset in bytes.
+not used. The third argument (``pos``) specifies the position in bytes.
 
 The meaning of the fourth argument is different between playback and
 capture. For playback, it holds the source data pointer, and for
@@ -3664,49 +3487,42 @@ The last argument is the number of bytes to be copied.
 What you have to do in this callback is again different between playback
 and capture directions. In the playback case, you copy the given amount
 of data (``count``) at the specified pointer (``src``) to the specified
-offset (``pos``) on the hardware buffer. When coded like memcpy-like
-way, the copy would be like:
-
-::
+offset (``pos``) in the hardware buffer. When coded like memcpy-like
+way, the copy would look like::
 
   my_memcpy_from_user(my_buffer + pos, src, count);
 
 For the capture direction, you copy the given amount of data (``count``)
-at the specified offset (``pos``) on the hardware buffer to the
-specified pointer (``dst``).
-
-::
+at the specified offset (``pos``) in the hardware buffer to the
+specified pointer (``dst``)::
 
   my_memcpy_to_user(dst, my_buffer + pos, count);
 
-Here the functions are named as ``from_user`` and ``to_user`` because
+Here the functions are named ``from_user`` and ``to_user`` because
 it's the user-space buffer that is passed to these callbacks.  That
-is, the callback is supposed to copy from/to the user-space data
+is, the callback is supposed to copy data from/to the user-space
 directly to/from the hardware buffer.
 
 Careful readers might notice that these callbacks receive the
 arguments in bytes, not in frames like other callbacks.  It's because
-it would make coding easier like the examples above, and also it makes
-easier to unify both the interleaved and non-interleaved cases, as
-explained in the following.
+this makes coding easier like in the examples above, and also it makes
+it easier to unify both the interleaved and non-interleaved cases, as
+explained below.
 
 In the case of non-interleaved samples, the implementation will be a bit
-more complicated.  The callback is called for each channel, passed by
-the second argument, so totally it's called for N-channels times per
-transfer.
-
-The meaning of other arguments are almost same as the interleaved
-case.  The callback is supposed to copy the data from/to the given
-user-space buffer, but only for the given channel.  For the detailed
-implementations, please check ``isa/gus/gus_pcm.c`` or
-"pci/rme9652/rme9652.c" as examples.
+more complicated.  The callback is called for each channel, passed in
+the second argument, so in total it's called N times per transfer.
 
-The above callbacks are the copy from/to the user-space buffer.  There
-are some cases where we want copy from/to the kernel-space buffer
-instead.  In such a case, ``copy_kernel`` callback is called.  It'd
-look like:
+The meaning of the other arguments are almost the same as in the
+interleaved case.  The callback is supposed to copy the data from/to
+the given user-space buffer, but only for the given channel. For
+details, please check ``isa/gus/gus_pcm.c`` or ``pci/rme9652/rme9652.c``
+as examples.
 
-::
+The above callbacks are the copies from/to the user-space buffer.  There
+are some cases where we want to copy from/to the kernel-space buffer
+instead.  In such a case, the ``copy_kernel`` callback is called.  It'd
+look like::
 
   static int playback_copy_kernel(struct snd_pcm_substream *substream,
                int channel, unsigned long pos,
@@ -3716,19 +3532,15 @@ look like:
                void *dst, unsigned long count);
 
 As found easily, the only difference is that the buffer pointer is
-without ``__user`` prefix; that is, a kernel-buffer pointer is passed
+without ``__user`` prefix; that is, a kernel-buffer pointer is passed
 in the fourth argument.  Correspondingly, the implementation would be
-a version without the user-copy, such as:
-
-::
+a version without the user-copy, such as::
 
   my_memcpy(my_buffer + pos, src, count);
 
 Usually for the playback, another callback ``fill_silence`` is
 defined.  It's implemented in a similar way as the copy callbacks
-above:
-
-::
+above::
 
   static int silence(struct snd_pcm_substream *substream, int channel,
                      unsigned long pos, unsigned long count);
@@ -3736,54 +3548,47 @@ above:
 The meanings of arguments are the same as in the ``copy_user`` and
 ``copy_kernel`` callbacks, although there is no buffer pointer
 argument. In the case of interleaved samples, the channel argument has
-no meaning, as well as on ``copy_*`` callbacks.
+no meaning, as for the ``copy_*`` callbacks.
 
-The role of ``fill_silence`` callback is to set the given amount
-(``count``) of silence data at the specified offset (``pos``) on the
+The role of the ``fill_silence`` callback is to set the given amount
+(``count``) of silence data at the specified offset (``pos``) in the
 hardware buffer. Suppose that the data format is signed (that is, the
 silent-data is 0), and the implementation using a memset-like function
-would be like: 
-
-::
+would look like::
 
   my_memset(my_buffer + pos, 0, count);
 
 In the case of non-interleaved samples, again, the implementation
-becomes a bit more complicated, as it's called N-times per transfer
+becomes a bit more complicated, as it's called N times per transfer
 for each channel. See, for example, ``isa/gus/gus_pcm.c``.
 
 Non-Contiguous Buffers
 ----------------------
 
-If your hardware supports the page table as in emu10k1 or the buffer
-descriptors as in via82xx, you can use the scatter-gather (SG) DMA. ALSA
+If your hardware supports a page table as in emu10k1 or buffer
+descriptors as in via82xx, you can use scatter-gather (SG) DMA. ALSA
 provides an interface for handling SG-buffers. The API is provided in
 ``<sound/pcm.h>``.
 
 For creating the SG-buffer handler, call
 :c:func:`snd_pcm_set_managed_buffer()` or
 :c:func:`snd_pcm_set_managed_buffer_all()` with
-``SNDRV_DMA_TYPE_DEV_SG`` in the PCM constructor like other PCI
-pre-allocator. You need to pass ``&pci->dev``, where pci is
-the struct pci_dev pointer of the chip as
-well.
-
-::
+``SNDRV_DMA_TYPE_DEV_SG`` in the PCM constructor like for other PCI
+pre-allocations. You need to pass ``&pci->dev``, where pci is
+the struct pci_dev pointer of the chip as well::
 
   snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
                                  &pci->dev, size, max);
 
 The ``struct snd_sg_buf`` instance is created as
-``substream->dma_private`` in turn. You can cast the pointer like:
-
-::
+``substream->dma_private`` in turn. You can cast the pointer like::
 
   struct snd_sg_buf *sgbuf = (struct snd_sg_buf *)substream->dma_private;
 
-Then in :c:func:`snd_pcm_lib_malloc_pages()` call, the common SG-buffer
+Then in the :c:func:`snd_pcm_lib_malloc_pages()` call, the common SG-buffer
 handler will allocate the non-contiguous kernel pages of the given size
-and map them onto the virtually contiguous memory. The virtual pointer
-is addressed in runtime->dma_area. The physical address
+and map them as virtually contiguous memory. The virtual pointer
+is addressed via runtime->dma_area. The physical address
 (``runtime->dma_addr``) is set to zero, because the buffer is
 physically non-contiguous. The physical address table is set up in
 ``sgbuf->table``. You can get the physical address at a certain offset
@@ -3796,22 +3601,20 @@ Vmalloc'ed Buffers
 ------------------
 
 It's possible to use a buffer allocated via :c:func:`vmalloc()`, for
-example, for an intermediate buffer. In the recent version of kernel,
-you can simply allocate it via standard
-:c:func:`snd_pcm_lib_malloc_pages()` and co after setting up the
-buffer preallocation with ``SNDRV_DMA_TYPE_VMALLOC`` type.
-
-::
+example, for an intermediate buffer.
+You can simply allocate it via the standard
+:c:func:`snd_pcm_lib_malloc_pages()` and co. after setting up the
+buffer preallocation with ``SNDRV_DMA_TYPE_VMALLOC`` type::
 
   snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
                                  NULL, 0, 0);
 
-The NULL is passed to the device pointer argument, which indicates
-that the default pages (GFP_KERNEL and GFP_HIGHMEM) will be
+NULL is passed as the device pointer argument, which indicates
+that default pages (GFP_KERNEL and GFP_HIGHMEM) will be
 allocated.
 
-Also, note that zero is passed to both the size and the max size
-arguments here.  Since each vmalloc call should succeed at any time,
+Also, note that zero is passed as both the size and the max size
+argument here.  Since each vmalloc call should succeed at any time,
 we don't need to pre-allocate the buffers like other continuous
 pages.
 
@@ -3823,9 +3626,7 @@ useful for debugging. I recommend you set up proc files if you write a
 driver and want to get a running status or register dumps. The API is
 found in ``<sound/info.h>``.
 
-To create a proc file, call :c:func:`snd_card_proc_new()`.
-
-::
+To create a proc file, call :c:func:`snd_card_proc_new()`::
 
   struct snd_info_entry *entry;
   int err = snd_card_proc_new(card, "my-file", &entry);
@@ -3841,28 +3642,22 @@ automatically in the card registration and release functions.
 When the creation is successful, the function stores a new instance in
 the pointer given in the third argument. It is initialized as a text
 proc file for read only. To use this proc file as a read-only text file
-as it is, set the read callback with a private data via
-:c:func:`snd_info_set_text_ops()`.
-
-::
+as-is, set the read callback with private data via
+:c:func:`snd_info_set_text_ops()`::
 
   snd_info_set_text_ops(entry, chip, my_proc_read);
 
 where the second argument (``chip``) is the private data to be used in
-the callbacks. The third parameter specifies the read buffer size and
+the callback. The third parameter specifies the read buffer size and
 the fourth (``my_proc_read``) is the callback function, which is
-defined like
-
-::
+defined like::
 
   static void my_proc_read(struct snd_info_entry *entry,
                            struct snd_info_buffer *buffer);
 
 In the read callback, use :c:func:`snd_iprintf()` for output
 strings, which works just like normal :c:func:`printf()`. For
-example,
-
-::
+example::
 
   static void my_proc_read(struct snd_info_entry *entry,
                            struct snd_info_buffer *buffer)
@@ -3873,28 +3668,22 @@ example,
           snd_iprintf(buffer, "Port = %ld\n", chip->port);
   }
 
-The file permissions can be changed afterwards. As default, it's set as
+The file permissions can be changed afterwards. By default, they are
 read only for all users. If you want to add write permission for the
-user (root as default), do as follows:
-
-::
+user (root by default), do as follows::
 
  entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
 
-and set the write buffer size and the callback
-
-::
+and set the write buffer size and the callback::
 
   entry->c.text.write = my_proc_write;
 
-For the write callback, you can use :c:func:`snd_info_get_line()`
+In the write callback, you can use :c:func:`snd_info_get_line()`
 to get a text line, and :c:func:`snd_info_get_str()` to retrieve
 a string from the line. Some examples are found in
 ``core/oss/mixer_oss.c``, core/oss/and ``pcm_oss.c``.
 
-For a raw-data proc-file, set the attributes as follows:
-
-::
+For a raw-data proc-file, set the attributes as follows::
 
   static const struct snd_info_entry_ops my_file_io_ops = {
           .read = my_file_io_read,
@@ -3906,14 +3695,13 @@ For a raw-data proc-file, set the attributes as follows:
   entry->size = 4096;
   entry->mode = S_IFREG | S_IRUGO;
 
-For the raw data, ``size`` field must be set properly. This specifies
+For raw data, ``size`` field must be set properly. This specifies
 the maximum size of the proc file access.
 
 The read/write callbacks of raw mode are more direct than the text mode.
 You need to use a low-level I/O functions such as
-:c:func:`copy_from_user()` and :c:func:`copy_to_user()` to transfer the data.
-
-::
+:c:func:`copy_from_user()` and :c:func:`copy_to_user()` to transfer the
+data::
 
   static ssize_t my_file_io_read(struct snd_info_entry *entry,
                               void *file_private_data,
@@ -3938,12 +3726,11 @@ Power Management
 If the chip is supposed to work with suspend/resume functions, you need
 to add power-management code to the driver. The additional code for
 power-management should be ifdef-ed with ``CONFIG_PM``, or annotated
-with __maybe_unused attribute; otherwise the compiler will complain
-you.
+with __maybe_unused attribute; otherwise the compiler will complain.
 
 If the driver *fully* supports suspend/resume that is, the device can be
 properly resumed to its state when suspend was called, you can set the
-``SNDRV_PCM_INFO_RESUME`` flag in the pcm info field. Usually, this is
+``SNDRV_PCM_INFO_RESUME`` flag in the PCM info field. Usually, this is
 possible when the registers of the chip can be safely saved and restored
 to RAM. If this is set, the trigger callback is called with
 ``SNDRV_PCM_TRIGGER_RESUME`` after the resume callback completes.
@@ -3953,7 +3740,7 @@ is still possible, it's still worthy to implement suspend/resume
 callbacks. In such a case, applications would reset the status by
 calling :c:func:`snd_pcm_prepare()` and restart the stream
 appropriately. Hence, you can define suspend/resume callbacks below but
-don't set ``SNDRV_PCM_INFO_RESUME`` info flag to the PCM.
+don't set the ``SNDRV_PCM_INFO_RESUME`` info flag to the PCM.
 
 Note that the trigger with SUSPEND can always be called when
 :c:func:`snd_pcm_suspend_all()` is called, regardless of the
@@ -3963,12 +3750,9 @@ behavior of :c:func:`snd_pcm_resume()`. (Thus, in theory,
 callback when no ``SNDRV_PCM_INFO_RESUME`` flag is set. But, it's better
 to keep it for compatibility reasons.)
 
-In the earlier version of ALSA drivers, a common power-management layer
-was provided, but it has been removed. The driver needs to define the
+The driver needs to define the
 suspend/resume hooks according to the bus the device is connected to. In
-the case of PCI drivers, the callbacks look like below:
-
-::
+the case of PCI drivers, the callbacks look like below::
 
   static int __maybe_unused snd_my_suspend(struct device *dev)
   {
@@ -3981,7 +3765,7 @@ the case of PCI drivers, the callbacks look like below:
           return 0;
   }
 
-The scheme of the real suspend job is as follows.
+The scheme of the real suspend job is as follows:
 
 1. Retrieve the card and the chip data.
 
@@ -3995,9 +3779,7 @@ The scheme of the real suspend job is as follows.
 
 5. Stop the hardware if necessary.
 
-A typical code would be like:
-
-::
+Typical code would look like::
 
   static int __maybe_unused mychip_suspend(struct device *dev)
   {
@@ -4016,7 +3798,7 @@ A typical code would be like:
   }
 
 
-The scheme of the real resume job is as follows.
+The scheme of the real resume job is as follows:
 
 1. Retrieve the card and the chip data.
 
@@ -4024,16 +3806,14 @@ The scheme of the real resume job is as follows.
 
 3. Restore the saved registers if necessary.
 
-4. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`.
+4. Resume the mixer, e.g. by calling :c:func:`snd_ac97_resume()`.
 
 5. Restart the hardware (if any).
 
 6. Call :c:func:`snd_power_change_state()` with
    ``SNDRV_CTL_POWER_D0`` to notify the processes.
 
-A typical code would be like:
-
-::
+Typical code would look like::
 
   static int __maybe_unused mychip_resume(struct pci_dev *pci)
   {
@@ -4060,9 +3840,7 @@ been already suspended via its own PM ops calling
 OK, we have all callbacks now. Let's set them up. In the initialization
 of the card, make sure that you can get the chip data from the card
 instance, typically via ``private_data`` field, in case you created the
-chip data individually.
-
-::
+chip data individually::
 
   static int snd_mychip_probe(struct pci_dev *pci,
                               const struct pci_device_id *pci_id)
@@ -4082,9 +3860,7 @@ chip data individually.
   }
 
 When you created the chip data with :c:func:`snd_card_new()`, it's
-anyway accessible via ``private_data`` field.
-
-::
+anyway accessible via ``private_data`` field::
 
   static int snd_mychip_probe(struct pci_dev *pci,
                               const struct pci_device_id *pci_id)
@@ -4101,14 +3877,12 @@ anyway accessible via ``private_data`` field.
           ....
   }
 
-If you need space to save the registers, allocate the buffer for it
+If you need space to save the registers, allocate the buffer for it
 here, too, since it would be fatal if you cannot allocate a memory in
 the suspend phase. The allocated buffer should be released in the
 corresponding destructor.
 
-And next, set suspend/resume callbacks to the pci_driver.
-
-::
+And next, set suspend/resume callbacks to the pci_driver::
 
   static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume);
 
@@ -4128,9 +3902,7 @@ have the ``index``, ``id`` and ``enable`` options.
 
 If the module supports multiple cards (usually up to 8 = ``SNDRV_CARDS``
 cards), they should be arrays. The default initial values are defined
-already as constants for easier programming:
-
-::
+already as constants for easier programming::
 
   static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
   static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -4144,9 +3916,7 @@ The module parameters must be declared with the standard
 ``module_param()``, ``module_param_array()`` and
 :c:func:`MODULE_PARM_DESC()` macros.
 
-The typical coding would be like below:
-
-::
+Typical code would look as below::
 
   #define CARD_NAME "My Chip"
 
@@ -4159,9 +3929,7 @@ The typical coding would be like below:
 
 Also, don't forget to define the module description and the license.
 Especially, the recent modprobe requires to define the
-module license as GPL, etc., otherwise the system is shown as “tainted”.
-
-::
+module license as GPL, etc., otherwise the system is shown as “tainted”::
 
   MODULE_DESCRIPTION("Sound driver for My Chip");
   MODULE_LICENSE("GPL");
@@ -4224,32 +3992,36 @@ Driver with A Single Source File
 
 1. Modify sound/pci/Makefile
 
-   Suppose you have a file xyz.c. Add the following two lines
-
-::
+   Suppose you have a file xyz.c. Add the following two lines::
 
   snd-xyz-objs := xyz.o
   obj-$(CONFIG_SND_XYZ) += snd-xyz.o
 
 2. Create the Kconfig entry
 
-   Add the new entry of Kconfig for your xyz driver. config SND_XYZ
-   tristate "Foobar XYZ" depends on SND select SND_PCM help Say Y here
-   to include support for Foobar XYZ soundcard. To compile this driver
-   as a module, choose M here: the module will be called snd-xyz. the
-   line, select SND_PCM, specifies that the driver xyz supports PCM. In
-   addition to SND_PCM, the following components are supported for
-   select command: SND_RAWMIDI, SND_TIMER, SND_HWDEP,
-   SND_MPU401_UART, SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB,
-   SND_AC97_CODEC. Add the select command for each supported
-   component.
+   Add the new entry of Kconfig for your xyz driver::
+
+  config SND_XYZ
+    tristate "Foobar XYZ"
+    depends on SND
+    select SND_PCM
+    help
+      Say Y here to include support for Foobar XYZ soundcard.
+      To compile this driver as a module, choose M here:
+      the module will be called snd-xyz.
+
+The line ``select SND_PCM`` specifies that the driver xyz supports PCM.
+In addition to SND_PCM, the following components are supported for
+select command: SND_RAWMIDI, SND_TIMER, SND_HWDEP, SND_MPU401_UART,
+SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB, SND_AC97_CODEC.
+Add the select command for each supported component.
 
-   Note that some selections imply the lowlevel selections. For example,
-   PCM includes TIMER, MPU401_UART includes RAWMIDI, AC97_CODEC
-   includes PCM, and OPL3_LIB includes HWDEP. You don't need to give
-   the lowlevel selections again.
+Note that some selections imply the lowlevel selections. For example,
+PCM includes TIMER, MPU401_UART includes RAWMIDI, AC97_CODEC
+includes PCM, and OPL3_LIB includes HWDEP. You don't need to give
+the lowlevel selections again.
 
-   For the details of Kconfig script, refer to the kbuild documentation.
+For the details of Kconfig script, refer to the kbuild documentation.
 
 Drivers with Several Source Files
 ---------------------------------
@@ -4258,16 +4030,12 @@ Suppose that the driver snd-xyz have several source files. They are
 located in the new subdirectory, sound/pci/xyz.
 
 1. Add a new directory (``sound/pci/xyz``) in ``sound/pci/Makefile``
-   as below
-
-::
+   as below::
 
   obj-$(CONFIG_SND) += sound/pci/xyz/
 
 
-2. Under the directory ``sound/pci/xyz``, create a Makefile
-
-::
+2. Under the directory ``sound/pci/xyz``, create a Makefile::
 
          snd-xyz-objs := xyz.o abc.o def.o
          obj-$(CONFIG_SND_XYZ) += snd-xyz.o
index a9b604a796fcabc84c1e8dbfd789773e8c4b1566..b5169b19449a9c95028fa635af034adc17a3a34a 100644 (file)
@@ -2075,6 +2075,7 @@ M:        Alexander Sverdlin <alexander.sverdlin@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     Documentation/devicetree/bindings/iio/adc/cirrus,ep9301-adc.yaml
+F:     Documentation/devicetree/bindings/sound/cirrus,ep9301-*
 F:     arch/arm/boot/compressed/misc-ep93xx.h
 F:     arch/arm/mach-ep93xx/
 F:     drivers/iio/adc/ep93xx_adc.c
@@ -4922,6 +4923,7 @@ L:        patches@opensource.cirrus.com
 S:     Maintained
 F:     Documentation/devicetree/bindings/sound/cirrus,cs*
 F:     include/dt-bindings/sound/cs*
+F:     include/sound/cs*
 F:     sound/pci/hda/cs*
 F:     sound/pci/hda/hda_cs_dsp_ctl.*
 F:     sound/soc/codecs/cs*
@@ -8282,6 +8284,23 @@ S:       Maintained
 F:     drivers/soc/fsl/qe/
 F:     include/soc/fsl/qe/
 
+FREESCALE QUICC ENGINE QMC DRIVER
+M:     Herve Codina <herve.codina@bootlin.com>
+L:     linuxppc-dev@lists.ozlabs.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml
+F:     drivers/soc/fsl/qe/qmc.c
+F:     include/soc/fsl/qe/qmc.h
+
+FREESCALE QUICC ENGINE TSA DRIVER
+M:     Herve Codina <herve.codina@bootlin.com>
+L:     linuxppc-dev@lists.ozlabs.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml
+F:     drivers/soc/fsl/qe/tsa.c
+F:     drivers/soc/fsl/qe/tsa.h
+F:     include/dt-bindings/soc/cpm1-fsl,tsa.h
+
 FREESCALE QUICC ENGINE UCC ETHERNET DRIVER
 M:     Li Yang <leoyang.li@nxp.com>
 L:     netdev@vger.kernel.org
@@ -8333,6 +8352,14 @@ F:       sound/soc/fsl/fsl*
 F:     sound/soc/fsl/imx*
 F:     sound/soc/fsl/mpc8610_hpcd.c
 
+FREESCALE SOC SOUND QMC DRIVER
+M:     Herve Codina <herve.codina@bootlin.com>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+L:     linuxppc-dev@lists.ozlabs.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml
+F:     sound/soc/fsl/fsl_qmc_audio.c
+
 FREESCALE USB PERIPHERAL DRIVERS
 M:     Li Yang <leoyang.li@nxp.com>
 L:     linux-usb@vger.kernel.org
@@ -13651,9 +13678,13 @@ F:     Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
 F:     drivers/spi/spi-at91-usart.c
 
 MICROCHIP AUDIO ASOC DRIVERS
-M:     Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+M:     Claudiu Beznea <claudiu.beznea@microchip.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Supported
+F:     Documentation/devicetree/bindings/sound/atmel*
+F:     Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt
+F:     Documentation/devicetree/bindings/sound/microchip,sama7g5-*
+F:     Documentation/devicetree/bindings/sound/mikroe,mikroe-proto.txt
 F:     sound/soc/atmel
 
 MICROCHIP CSI2DC DRIVER
@@ -13826,9 +13857,10 @@ S:     Supported
 F:     drivers/spi/spi-atmel.*
 
 MICROCHIP SSC DRIVER
-M:     Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+M:     Claudiu Beznea <claudiu.beznea@microchip.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Supported
+F:     Documentation/devicetree/bindings/misc/atmel-ssc.txt
 F:     drivers/misc/atmel-ssc.c
 F:     include/linux/atmel-ssc.h
 
index 56ca14f77543bbc5a5a6644fc3c48ca1d7100843..34ab29966c8bf01984b5a7c62513ba61de47f24e 100644 (file)
@@ -94,7 +94,7 @@ int cpm_command(u32 command, u8 opcode)
        int i, ret;
        unsigned long flags;
 
-       if (command & 0xffffff0f)
+       if (command & 0xffffff03)
                return -EINVAL;
 
        spin_lock_irqsave(&cmd_lock, flags);
index f558b390fbfe42e3e8a5c5376f5b3b6ad37e655e..e4ccfb6a8fa52fefa852b009e161afb3ba23c264 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 
@@ -301,6 +302,7 @@ struct cs_dsp_ops {
 static const struct cs_dsp_ops cs_dsp_adsp1_ops;
 static const struct cs_dsp_ops cs_dsp_adsp2_ops[];
 static const struct cs_dsp_ops cs_dsp_halo_ops;
+static const struct cs_dsp_ops cs_dsp_halo_ao_ops;
 
 struct cs_dsp_buf {
        struct list_head list;
@@ -456,6 +458,33 @@ static const struct {
        },
 };
 
+static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg,
+                                unsigned int off);
+
+static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored)
+{
+       struct cs_dsp *dsp = s->private;
+       struct cs_dsp_coeff_ctl *ctl;
+       unsigned int reg;
+
+       list_for_each_entry(ctl, &dsp->ctl_list, list) {
+               cs_dsp_coeff_base_reg(ctl, &reg, 0);
+               seq_printf(s, "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",
+                          ctl->subname_len, ctl->subname, ctl->len,
+                          cs_dsp_mem_region_name(ctl->alg_region.type),
+                          ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type,
+                          ctl->flags & WMFW_CTL_FLAG_VOLATILE ? 'V' : '-',
+                          ctl->flags & WMFW_CTL_FLAG_SYS ? 'S' : '-',
+                          ctl->flags & WMFW_CTL_FLAG_READABLE ? 'R' : '-',
+                          ctl->flags & WMFW_CTL_FLAG_WRITEABLE ? 'W' : '-',
+                          ctl->enabled ? "enabled" : "disabled",
+                          ctl->set ? "dirty" : "clean");
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(cs_dsp_debugfs_read_controls);
+
 /**
  * cs_dsp_init_debugfs() - Create and populate DSP representation in debugfs
  * @dsp: pointer to DSP structure
@@ -478,6 +507,9 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root)
                debugfs_create_file(cs_dsp_debugfs_fops[i].name, 0444, root,
                                    dsp, &cs_dsp_debugfs_fops[i].fops);
 
+       debugfs_create_file("controls", 0444, root, dsp,
+                           &cs_dsp_debugfs_read_controls_fops);
+
        dsp->debugfs_root = root;
 }
 EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP);
@@ -1300,6 +1332,9 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
        int regions = 0;
        int ret, offset, type;
 
+       if (!firmware)
+               return 0;
+
        ret = -EINVAL;
 
        pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
@@ -2821,7 +2856,10 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP);
  */
 int cs_dsp_halo_init(struct cs_dsp *dsp)
 {
-       dsp->ops = &cs_dsp_halo_ops;
+       if (dsp->no_core_startstop)
+               dsp->ops = &cs_dsp_halo_ao_ops;
+       else
+               dsp->ops = &cs_dsp_halo_ops;
 
        return cs_dsp_common_init(dsp);
 }
@@ -3187,6 +3225,14 @@ static const struct cs_dsp_ops cs_dsp_halo_ops = {
        .stop_core = cs_dsp_halo_stop_core,
 };
 
+static const struct cs_dsp_ops cs_dsp_halo_ao_ops = {
+       .parse_sizes = cs_dsp_adsp2_parse_sizes,
+       .validate_version = cs_dsp_halo_validate_version,
+       .setup_algs = cs_dsp_halo_setup_algs,
+       .region_to_reg = cs_dsp_halo_region_to_reg,
+       .show_fw_status = cs_dsp_halo_show_fw_status,
+};
+
 /**
  * cs_dsp_chunk_write() - Format data to a DSP memory chunk
  * @ch: Pointer to the chunk structure
index 357c5800b112fd05fc6a94987b93c4f8073f1d65..7268c2fbcbc1d80b7b72b45ae4668584016f92c8 100644 (file)
@@ -33,6 +33,29 @@ config UCC
        bool
        default y if UCC_FAST || UCC_SLOW
 
+config CPM_TSA
+       tristate "CPM TSA support"
+       depends on OF && HAS_IOMEM
+       depends on CPM1 || COMPILE_TEST
+       help
+         Freescale CPM Time Slot Assigner (TSA)
+         controller.
+
+         This option enables support for this
+         controller
+
+config CPM_QMC
+       tristate "CPM QMC support"
+       depends on OF && HAS_IOMEM
+       depends on CPM1 || (FSL_SOC && COMPILE_TEST)
+       depends on CPM_TSA
+       help
+         Freescale CPM QUICC Multichannel Controller
+         (QMC)
+
+         This option enables support for this
+         controller
+
 config QE_TDM
        bool
        default y if FSL_UCC_HDLC
index 55a555304f3a540da6abb31607aca6addb853f5f..ec8506e1311361c2f4c31e5151a7eb21c99f6202 100644 (file)
@@ -4,6 +4,8 @@
 #
 obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_common.o qe_ic.o qe_io.o
 obj-$(CONFIG_CPM)      += qe_common.o
+obj-$(CONFIG_CPM_TSA)  += tsa.o
+obj-$(CONFIG_CPM_QMC)  += qmc.o
 obj-$(CONFIG_UCC)      += ucc.o
 obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
 obj-$(CONFIG_UCC_FAST) += ucc_fast.o
diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c
new file mode 100644 (file)
index 0000000..b3c292c
--- /dev/null
@@ -0,0 +1,1537 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QMC driver
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <soc/fsl/qe/qmc.h>
+#include <linux/dma-mapping.h>
+#include <linux/hdlc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <soc/fsl/cpm.h>
+#include <sysdev/fsl_soc.h>
+#include "tsa.h"
+
+/* SCC general mode register high (32 bits) */
+#define SCC_GSMRL      0x00
+#define SCC_GSMRL_ENR          (1 << 5)
+#define SCC_GSMRL_ENT          (1 << 4)
+#define SCC_GSMRL_MODE_QMC     (0x0A << 0)
+
+/* SCC general mode register low (32 bits) */
+#define SCC_GSMRH      0x04
+#define   SCC_GSMRH_CTSS       (1 << 7)
+#define   SCC_GSMRH_CDS                (1 << 8)
+#define   SCC_GSMRH_CTSP       (1 << 9)
+#define   SCC_GSMRH_CDP                (1 << 10)
+
+/* SCC event register (16 bits) */
+#define SCC_SCCE       0x10
+#define   SCC_SCCE_IQOV                (1 << 3)
+#define   SCC_SCCE_GINT                (1 << 2)
+#define   SCC_SCCE_GUN         (1 << 1)
+#define   SCC_SCCE_GOV         (1 << 0)
+
+/* SCC mask register (16 bits) */
+#define SCC_SCCM       0x14
+/* Multichannel base pointer (32 bits) */
+#define QMC_GBL_MCBASE         0x00
+/* Multichannel controller state (16 bits) */
+#define QMC_GBL_QMCSTATE       0x04
+/* Maximum receive buffer length (16 bits) */
+#define QMC_GBL_MRBLR          0x06
+/* Tx time-slot assignment table pointer (16 bits) */
+#define QMC_GBL_TX_S_PTR       0x08
+/* Rx pointer (16 bits) */
+#define QMC_GBL_RXPTR          0x0A
+/* Global receive frame threshold (16 bits) */
+#define QMC_GBL_GRFTHR         0x0C
+/* Global receive frame count (16 bits) */
+#define QMC_GBL_GRFCNT         0x0E
+/* Multichannel interrupt base address (32 bits) */
+#define QMC_GBL_INTBASE                0x10
+/* Multichannel interrupt pointer (32 bits) */
+#define QMC_GBL_INTPTR         0x14
+/* Rx time-slot assignment table pointer (16 bits) */
+#define QMC_GBL_RX_S_PTR       0x18
+/* Tx pointer (16 bits) */
+#define QMC_GBL_TXPTR          0x1A
+/* CRC constant (32 bits) */
+#define QMC_GBL_C_MASK32       0x1C
+/* Time slot assignment table Rx (32 x 16 bits) */
+#define QMC_GBL_TSATRX         0x20
+/* Time slot assignment table Tx (32 x 16 bits) */
+#define QMC_GBL_TSATTX         0x60
+/* CRC constant (16 bits) */
+#define QMC_GBL_C_MASK16       0xA0
+
+/* TSA entry (16bit entry in TSATRX and TSATTX) */
+#define QMC_TSA_VALID          (1 << 15)
+#define QMC_TSA_WRAP           (1 << 14)
+#define QMC_TSA_MASK           (0x303F)
+#define QMC_TSA_CHANNEL(x)     ((x) << 6)
+
+/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */
+#define QMC_SPE_TBASE  0x00
+
+/* Channel mode register (16 bits) */
+#define QMC_SPE_CHAMR  0x02
+#define   QMC_SPE_CHAMR_MODE_HDLC      (1 << 15)
+#define   QMC_SPE_CHAMR_MODE_TRANSP    ((0 << 15) | (1 << 13))
+#define   QMC_SPE_CHAMR_ENT            (1 << 12)
+#define   QMC_SPE_CHAMR_POL            (1 << 8)
+#define   QMC_SPE_CHAMR_HDLC_IDLM      (1 << 13)
+#define   QMC_SPE_CHAMR_HDLC_CRC       (1 << 7)
+#define   QMC_SPE_CHAMR_HDLC_NOF       (0x0f << 0)
+#define   QMC_SPE_CHAMR_TRANSP_RD      (1 << 14)
+#define   QMC_SPE_CHAMR_TRANSP_SYNC    (1 << 10)
+
+/* Tx internal state (32 bits) */
+#define QMC_SPE_TSTATE 0x04
+/* Tx buffer descriptor pointer (16 bits) */
+#define QMC_SPE_TBPTR  0x0C
+/* Zero-insertion state (32 bits) */
+#define QMC_SPE_ZISTATE        0x14
+/* Channel’s interrupt mask flags (16 bits) */
+#define QMC_SPE_INTMSK 0x1C
+/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */
+#define QMC_SPE_RBASE  0x20
+/* HDLC: Maximum frame length register (16 bits) */
+#define QMC_SPE_MFLR   0x22
+/* TRANSPARENT: Transparent maximum receive length (16 bits) */
+#define QMC_SPE_TMRBLR 0x22
+/* Rx internal state (32 bits) */
+#define QMC_SPE_RSTATE 0x24
+/* Rx buffer descriptor pointer (16 bits) */
+#define QMC_SPE_RBPTR  0x2C
+/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */
+#define QMC_SPE_RPACK  0x30
+/* Zero deletion state (32 bits) */
+#define QMC_SPE_ZDSTATE        0x34
+
+/* Transparent synchronization (16 bits) */
+#define QMC_SPE_TRNSYNC 0x3C
+#define   QMC_SPE_TRNSYNC_RX(x)        ((x) << 8)
+#define   QMC_SPE_TRNSYNC_TX(x)        ((x) << 0)
+
+/* Interrupt related registers bits */
+#define QMC_INT_V              (1 << 15)
+#define QMC_INT_W              (1 << 14)
+#define QMC_INT_NID            (1 << 13)
+#define QMC_INT_IDL            (1 << 12)
+#define QMC_INT_GET_CHANNEL(x) (((x) & 0x0FC0) >> 6)
+#define QMC_INT_MRF            (1 << 5)
+#define QMC_INT_UN             (1 << 4)
+#define QMC_INT_RXF            (1 << 3)
+#define QMC_INT_BSY            (1 << 2)
+#define QMC_INT_TXB            (1 << 1)
+#define QMC_INT_RXB            (1 << 0)
+
+/* BD related registers bits */
+#define QMC_BD_RX_E    (1 << 15)
+#define QMC_BD_RX_W    (1 << 13)
+#define QMC_BD_RX_I    (1 << 12)
+#define QMC_BD_RX_L    (1 << 11)
+#define QMC_BD_RX_F    (1 << 10)
+#define QMC_BD_RX_CM   (1 << 9)
+#define QMC_BD_RX_UB   (1 << 7)
+#define QMC_BD_RX_LG   (1 << 5)
+#define QMC_BD_RX_NO   (1 << 4)
+#define QMC_BD_RX_AB   (1 << 3)
+#define QMC_BD_RX_CR   (1 << 2)
+
+#define QMC_BD_TX_R    (1 << 15)
+#define QMC_BD_TX_W    (1 << 13)
+#define QMC_BD_TX_I    (1 << 12)
+#define QMC_BD_TX_L    (1 << 11)
+#define QMC_BD_TX_TC   (1 << 10)
+#define QMC_BD_TX_CM   (1 << 9)
+#define QMC_BD_TX_UB   (1 << 7)
+#define QMC_BD_TX_PAD  (0x0f << 0)
+
+/* Numbers of BDs and interrupt items */
+#define QMC_NB_TXBDS   8
+#define QMC_NB_RXBDS   8
+#define QMC_NB_INTS    128
+
+struct qmc_xfer_desc {
+       union {
+               void (*tx_complete)(void *context);
+               void (*rx_complete)(void *context, size_t length);
+       };
+       void *context;
+};
+
+struct qmc_chan {
+       struct list_head list;
+       unsigned int id;
+       struct qmc *qmc;
+       void *__iomem s_param;
+       enum qmc_mode mode;
+       u64     tx_ts_mask;
+       u64     rx_ts_mask;
+       bool is_reverse_data;
+
+       spinlock_t      tx_lock;
+       cbd_t __iomem *txbds;
+       cbd_t __iomem *txbd_free;
+       cbd_t __iomem *txbd_done;
+       struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS];
+       u64     nb_tx_underrun;
+       bool    is_tx_stopped;
+
+       spinlock_t      rx_lock;
+       cbd_t __iomem *rxbds;
+       cbd_t __iomem *rxbd_free;
+       cbd_t __iomem *rxbd_done;
+       struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS];
+       u64     nb_rx_busy;
+       int     rx_pending;
+       bool    is_rx_halted;
+       bool    is_rx_stopped;
+};
+
+struct qmc {
+       struct device *dev;
+       struct tsa_serial *tsa_serial;
+       void *__iomem scc_regs;
+       void *__iomem scc_pram;
+       void *__iomem dpram;
+       u16 scc_pram_offset;
+       cbd_t __iomem *bd_table;
+       dma_addr_t bd_dma_addr;
+       size_t bd_size;
+       u16 __iomem *int_table;
+       u16 __iomem *int_curr;
+       dma_addr_t int_dma_addr;
+       size_t int_size;
+       struct list_head chan_head;
+       struct qmc_chan *chans[64];
+};
+
+static inline void qmc_write16(void *__iomem addr, u16 val)
+{
+       iowrite16be(val, addr);
+}
+
+static inline u16 qmc_read16(void *__iomem addr)
+{
+       return ioread16be(addr);
+}
+
+static inline void qmc_setbits16(void *__iomem addr, u16 set)
+{
+       qmc_write16(addr, qmc_read16(addr) | set);
+}
+
+static inline void qmc_clrbits16(void *__iomem addr, u16 clr)
+{
+       qmc_write16(addr, qmc_read16(addr) & ~clr);
+}
+
+static inline void qmc_write32(void *__iomem addr, u32 val)
+{
+       iowrite32be(val, addr);
+}
+
+static inline u32 qmc_read32(void *__iomem addr)
+{
+       return ioread32be(addr);
+}
+
+static inline void qmc_setbits32(void *__iomem addr, u32 set)
+{
+       qmc_write32(addr, qmc_read32(addr) | set);
+}
+
+
+int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info)
+{
+       struct tsa_serial_info tsa_info;
+       int ret;
+
+       /* Retrieve info from the TSA related serial */
+       ret = tsa_serial_get_info(chan->qmc->tsa_serial, &tsa_info);
+       if (ret)
+               return ret;
+
+       info->mode = chan->mode;
+       info->rx_fs_rate = tsa_info.rx_fs_rate;
+       info->rx_bit_rate = tsa_info.rx_bit_rate;
+       info->nb_tx_ts = hweight64(chan->tx_ts_mask);
+       info->tx_fs_rate = tsa_info.tx_fs_rate;
+       info->tx_bit_rate = tsa_info.tx_bit_rate;
+       info->nb_rx_ts = hweight64(chan->rx_ts_mask);
+
+       return 0;
+}
+EXPORT_SYMBOL(qmc_chan_get_info);
+
+int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param)
+{
+       if (param->mode != chan->mode)
+               return -EINVAL;
+
+       switch (param->mode) {
+       case QMC_HDLC:
+               if ((param->hdlc.max_rx_buf_size % 4) ||
+                   (param->hdlc.max_rx_buf_size < 8))
+                       return -EINVAL;
+
+               qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR,
+                           param->hdlc.max_rx_buf_size - 8);
+               qmc_write16(chan->s_param + QMC_SPE_MFLR,
+                           param->hdlc.max_rx_frame_size);
+               if (param->hdlc.is_crc32) {
+                       qmc_setbits16(chan->s_param + QMC_SPE_CHAMR,
+                                     QMC_SPE_CHAMR_HDLC_CRC);
+               } else {
+                       qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR,
+                                     QMC_SPE_CHAMR_HDLC_CRC);
+               }
+               break;
+
+       case QMC_TRANSPARENT:
+               qmc_write16(chan->s_param + QMC_SPE_TMRBLR,
+                           param->transp.max_rx_buf_size);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(qmc_chan_set_param);
+
+int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
+                         void (*complete)(void *context), void *context)
+{
+       struct qmc_xfer_desc *xfer_desc;
+       unsigned long flags;
+       cbd_t *__iomem bd;
+       u16 ctrl;
+       int ret;
+
+       /*
+        * R bit  UB bit
+        *   0       0  : The BD is free
+        *   1       1  : The BD is in used, waiting for transfer
+        *   0       1  : The BD is in used, waiting for completion
+        *   1       0  : Should not append
+        */
+
+       spin_lock_irqsave(&chan->tx_lock, flags);
+       bd = chan->txbd_free;
+
+       ctrl = qmc_read16(&bd->cbd_sc);
+       if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) {
+               /* We are full ... */
+               ret = -EBUSY;
+               goto end;
+       }
+
+       qmc_write16(&bd->cbd_datlen, length);
+       qmc_write32(&bd->cbd_bufaddr, addr);
+
+       xfer_desc = &chan->tx_desc[bd - chan->txbds];
+       xfer_desc->tx_complete = complete;
+       xfer_desc->context = context;
+
+       /* Activate the descriptor */
+       ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB);
+       wmb(); /* Be sure to flush the descriptor before control update */
+       qmc_write16(&bd->cbd_sc, ctrl);
+
+       if (!chan->is_tx_stopped)
+               qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);
+
+       if (ctrl & QMC_BD_TX_W)
+               chan->txbd_free = chan->txbds;
+       else
+               chan->txbd_free++;
+
+       ret = 0;
+
+end:
+       spin_unlock_irqrestore(&chan->tx_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(qmc_chan_write_submit);
+
+static void qmc_chan_write_done(struct qmc_chan *chan)
+{
+       struct qmc_xfer_desc *xfer_desc;
+       void (*complete)(void *context);
+       unsigned long flags;
+       void *context;
+       cbd_t *__iomem bd;
+       u16 ctrl;
+
+       /*
+        * R bit  UB bit
+        *   0       0  : The BD is free
+        *   1       1  : The BD is in used, waiting for transfer
+        *   0       1  : The BD is in used, waiting for completion
+        *   1       0  : Should not append
+        */
+
+       spin_lock_irqsave(&chan->tx_lock, flags);
+       bd = chan->txbd_done;
+
+       ctrl = qmc_read16(&bd->cbd_sc);
+       while (!(ctrl & QMC_BD_TX_R)) {
+               if (!(ctrl & QMC_BD_TX_UB))
+                       goto end;
+
+               xfer_desc = &chan->tx_desc[bd - chan->txbds];
+               complete = xfer_desc->tx_complete;
+               context = xfer_desc->context;
+               xfer_desc->tx_complete = NULL;
+               xfer_desc->context = NULL;
+
+               qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB);
+
+               if (ctrl & QMC_BD_TX_W)
+                       chan->txbd_done = chan->txbds;
+               else
+                       chan->txbd_done++;
+
+               if (complete) {
+                       spin_unlock_irqrestore(&chan->tx_lock, flags);
+                       complete(context);
+                       spin_lock_irqsave(&chan->tx_lock, flags);
+               }
+
+               bd = chan->txbd_done;
+               ctrl = qmc_read16(&bd->cbd_sc);
+       }
+
+end:
+       spin_unlock_irqrestore(&chan->tx_lock, flags);
+}
+
+int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
+                        void (*complete)(void *context, size_t length), void *context)
+{
+       struct qmc_xfer_desc *xfer_desc;
+       unsigned long flags;
+       cbd_t *__iomem bd;
+       u16 ctrl;
+       int ret;
+
+       /*
+        * E bit  UB bit
+        *   0       0  : The BD is free
+        *   1       1  : The BD is in used, waiting for transfer
+        *   0       1  : The BD is in used, waiting for completion
+        *   1       0  : Should not append
+        */
+
+       spin_lock_irqsave(&chan->rx_lock, flags);
+       bd = chan->rxbd_free;
+
+       ctrl = qmc_read16(&bd->cbd_sc);
+       if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) {
+               /* We are full ... */
+               ret = -EBUSY;
+               goto end;
+       }
+
+       qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */
+       qmc_write32(&bd->cbd_bufaddr, addr);
+
+       xfer_desc = &chan->rx_desc[bd - chan->rxbds];
+       xfer_desc->rx_complete = complete;
+       xfer_desc->context = context;
+
+       /* Activate the descriptor */
+       ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB);
+       wmb(); /* Be sure to flush data before descriptor activation */
+       qmc_write16(&bd->cbd_sc, ctrl);
+
+       /* Restart receiver if needed */
+       if (chan->is_rx_halted && !chan->is_rx_stopped) {
+               /* Restart receiver */
+               if (chan->mode == QMC_TRANSPARENT)
+                       qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
+               else
+                       qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
+               qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
+               chan->is_rx_halted = false;
+       }
+       chan->rx_pending++;
+
+       if (ctrl & QMC_BD_RX_W)
+               chan->rxbd_free = chan->rxbds;
+       else
+               chan->rxbd_free++;
+
+       ret = 0;
+end:
+       spin_unlock_irqrestore(&chan->rx_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(qmc_chan_read_submit);
+
+static void qmc_chan_read_done(struct qmc_chan *chan)
+{
+       void (*complete)(void *context, size_t size);
+       struct qmc_xfer_desc *xfer_desc;
+       unsigned long flags;
+       cbd_t *__iomem bd;
+       void *context;
+       u16 datalen;
+       u16 ctrl;
+
+       /*
+        * E bit  UB bit
+        *   0       0  : The BD is free
+        *   1       1  : The BD is in used, waiting for transfer
+        *   0       1  : The BD is in used, waiting for completion
+        *   1       0  : Should not append
+        */
+
+       spin_lock_irqsave(&chan->rx_lock, flags);
+       bd = chan->rxbd_done;
+
+       ctrl = qmc_read16(&bd->cbd_sc);
+       while (!(ctrl & QMC_BD_RX_E)) {
+               if (!(ctrl & QMC_BD_RX_UB))
+                       goto end;
+
+               xfer_desc = &chan->rx_desc[bd - chan->rxbds];
+               complete = xfer_desc->rx_complete;
+               context = xfer_desc->context;
+               xfer_desc->rx_complete = NULL;
+               xfer_desc->context = NULL;
+
+               datalen = qmc_read16(&bd->cbd_datlen);
+               qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB);
+
+               if (ctrl & QMC_BD_RX_W)
+                       chan->rxbd_done = chan->rxbds;
+               else
+                       chan->rxbd_done++;
+
+               chan->rx_pending--;
+
+               if (complete) {
+                       spin_unlock_irqrestore(&chan->rx_lock, flags);
+                       complete(context, datalen);
+                       spin_lock_irqsave(&chan->rx_lock, flags);
+               }
+
+               bd = chan->rxbd_done;
+               ctrl = qmc_read16(&bd->cbd_sc);
+       }
+
+end:
+       spin_unlock_irqrestore(&chan->rx_lock, flags);
+}
+
+static int qmc_chan_command(struct qmc_chan *chan, u8 qmc_opcode)
+{
+       return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E);
+}
+
+static int qmc_chan_stop_rx(struct qmc_chan *chan)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&chan->rx_lock, flags);
+
+       /* Send STOP RECEIVE command */
+       ret = qmc_chan_command(chan, 0x0);
+       if (ret) {
+               dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n",
+                       chan->id, ret);
+               goto end;
+       }
+
+       chan->is_rx_stopped = true;
+
+end:
+       spin_unlock_irqrestore(&chan->rx_lock, flags);
+       return ret;
+}
+
+static int qmc_chan_stop_tx(struct qmc_chan *chan)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&chan->tx_lock, flags);
+
+       /* Send STOP TRANSMIT command */
+       ret = qmc_chan_command(chan, 0x1);
+       if (ret) {
+               dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n",
+                       chan->id, ret);
+               goto end;
+       }
+
+       chan->is_tx_stopped = true;
+
+end:
+       spin_unlock_irqrestore(&chan->tx_lock, flags);
+       return ret;
+}
+
+int qmc_chan_stop(struct qmc_chan *chan, int direction)
+{
+       int ret;
+
+       if (direction & QMC_CHAN_READ) {
+               ret = qmc_chan_stop_rx(chan);
+               if (ret)
+                       return ret;
+       }
+
+       if (direction & QMC_CHAN_WRITE) {
+               ret = qmc_chan_stop_tx(chan);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(qmc_chan_stop);
+
+static void qmc_chan_start_rx(struct qmc_chan *chan)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&chan->rx_lock, flags);
+
+       /* Restart the receiver */
+       if (chan->mode == QMC_TRANSPARENT)
+               qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
+       else
+               qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
+       qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
+       chan->is_rx_halted = false;
+
+       chan->is_rx_stopped = false;
+
+       spin_unlock_irqrestore(&chan->rx_lock, flags);
+}
+
+static void qmc_chan_start_tx(struct qmc_chan *chan)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&chan->tx_lock, flags);
+
+       /*
+        * Enable channel transmitter as it could be disabled if
+        * qmc_chan_reset() was called.
+        */
+       qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);
+
+       /* Set the POL bit in the channel mode register */
+       qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);
+
+       chan->is_tx_stopped = false;
+
+       spin_unlock_irqrestore(&chan->tx_lock, flags);
+}
+
+int qmc_chan_start(struct qmc_chan *chan, int direction)
+{
+       if (direction & QMC_CHAN_READ)
+               qmc_chan_start_rx(chan);
+
+       if (direction & QMC_CHAN_WRITE)
+               qmc_chan_start_tx(chan);
+
+       return 0;
+}
+EXPORT_SYMBOL(qmc_chan_start);
+
+static void qmc_chan_reset_rx(struct qmc_chan *chan)
+{
+       struct qmc_xfer_desc *xfer_desc;
+       unsigned long flags;
+       cbd_t *__iomem bd;
+       u16 ctrl;
+
+       spin_lock_irqsave(&chan->rx_lock, flags);
+       bd = chan->rxbds;
+       do {
+               ctrl = qmc_read16(&bd->cbd_sc);
+               qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E));
+
+               xfer_desc = &chan->rx_desc[bd - chan->rxbds];
+               xfer_desc->rx_complete = NULL;
+               xfer_desc->context = NULL;
+
+               bd++;
+       } while (!(ctrl & QMC_BD_RX_W));
+
+       chan->rxbd_free = chan->rxbds;
+       chan->rxbd_done = chan->rxbds;
+       qmc_write16(chan->s_param + QMC_SPE_RBPTR,
+                   qmc_read16(chan->s_param + QMC_SPE_RBASE));
+
+       chan->rx_pending = 0;
+       chan->is_rx_stopped = false;
+
+       spin_unlock_irqrestore(&chan->rx_lock, flags);
+}
+
+static void qmc_chan_reset_tx(struct qmc_chan *chan)
+{
+       struct qmc_xfer_desc *xfer_desc;
+       unsigned long flags;
+       cbd_t *__iomem bd;
+       u16 ctrl;
+
+       spin_lock_irqsave(&chan->tx_lock, flags);
+
+       /* Disable transmitter. It will be re-enable on qmc_chan_start() */
+       qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);
+
+       bd = chan->txbds;
+       do {
+               ctrl = qmc_read16(&bd->cbd_sc);
+               qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R));
+
+               xfer_desc = &chan->tx_desc[bd - chan->txbds];
+               xfer_desc->tx_complete = NULL;
+               xfer_desc->context = NULL;
+
+               bd++;
+       } while (!(ctrl & QMC_BD_TX_W));
+
+       chan->txbd_free = chan->txbds;
+       chan->txbd_done = chan->txbds;
+       qmc_write16(chan->s_param + QMC_SPE_TBPTR,
+                   qmc_read16(chan->s_param + QMC_SPE_TBASE));
+
+       /* Reset TSTATE and ZISTATE to their initial value */
+       qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000);
+       qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100);
+
+       spin_unlock_irqrestore(&chan->tx_lock, flags);
+}
+
+int qmc_chan_reset(struct qmc_chan *chan, int direction)
+{
+       if (direction & QMC_CHAN_READ)
+               qmc_chan_reset_rx(chan);
+
+       if (direction & QMC_CHAN_WRITE)
+               qmc_chan_reset_tx(chan);
+
+       return 0;
+}
+EXPORT_SYMBOL(qmc_chan_reset);
+
+static int qmc_check_chans(struct qmc *qmc)
+{
+       struct tsa_serial_info info;
+       bool is_one_table = false;
+       struct qmc_chan *chan;
+       u64 tx_ts_mask = 0;
+       u64 rx_ts_mask = 0;
+       u64 tx_ts_assigned_mask;
+       u64 rx_ts_assigned_mask;
+       int ret;
+
+       /* Retrieve info from the TSA related serial */
+       ret = tsa_serial_get_info(qmc->tsa_serial, &info);
+       if (ret)
+               return ret;
+
+       if ((info.nb_tx_ts > 64) || (info.nb_rx_ts > 64)) {
+               dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned not supported\n");
+               return -EINVAL;
+       }
+
+       /*
+        * If more than 32 TS are assigned to this serial, one common table is
+        * used for Tx and Rx and so masks must be equal for all channels.
+        */
+       if ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) {
+               if (info.nb_tx_ts != info.nb_rx_ts) {
+                       dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n");
+                       return -EINVAL;
+               }
+               is_one_table = true;
+       }
+
+       tx_ts_assigned_mask = info.nb_tx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_tx_ts) - 1;
+       rx_ts_assigned_mask = info.nb_rx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_rx_ts) - 1;
+
+       list_for_each_entry(chan, &qmc->chan_head, list) {
+               if (chan->tx_ts_mask > tx_ts_assigned_mask) {
+                       dev_err(qmc->dev, "chan %u uses TSA unassigned Tx TS\n", chan->id);
+                       return -EINVAL;
+               }
+               if (tx_ts_mask & chan->tx_ts_mask) {
+                       dev_err(qmc->dev, "chan %u uses an already used Tx TS\n", chan->id);
+                       return -EINVAL;
+               }
+
+               if (chan->rx_ts_mask > rx_ts_assigned_mask) {
+                       dev_err(qmc->dev, "chan %u uses TSA unassigned Rx TS\n", chan->id);
+                       return -EINVAL;
+               }
+               if (rx_ts_mask & chan->rx_ts_mask) {
+                       dev_err(qmc->dev, "chan %u uses an already used Rx TS\n", chan->id);
+                       return -EINVAL;
+               }
+
+               if (is_one_table && (chan->tx_ts_mask != chan->rx_ts_mask)) {
+                       dev_err(qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id);
+                       return -EINVAL;
+               }
+
+               tx_ts_mask |= chan->tx_ts_mask;
+               rx_ts_mask |= chan->rx_ts_mask;
+       }
+
+       return 0;
+}
+
+static unsigned int qmc_nb_chans(struct qmc *qmc)
+{
+       unsigned int count = 0;
+       struct qmc_chan *chan;
+
+       list_for_each_entry(chan, &qmc->chan_head, list)
+               count++;
+
+       return count;
+}
+
+static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
+{
+       struct device_node *chan_np;
+       struct qmc_chan *chan;
+       const char *mode;
+       u32 chan_id;
+       u64 ts_mask;
+       int ret;
+
+       for_each_available_child_of_node(np, chan_np) {
+               ret = of_property_read_u32(chan_np, "reg", &chan_id);
+               if (ret) {
+                       dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
+                       of_node_put(chan_np);
+                       return ret;
+               }
+               if (chan_id > 63) {
+                       dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
+                       of_node_put(chan_np);
+                       return -EINVAL;
+               }
+
+               chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
+               if (!chan) {
+                       of_node_put(chan_np);
+                       return -ENOMEM;
+               }
+
+               chan->id = chan_id;
+               spin_lock_init(&chan->rx_lock);
+               spin_lock_init(&chan->tx_lock);
+
+               ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask);
+               if (ret) {
+                       dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
+                               chan_np);
+                       of_node_put(chan_np);
+                       return ret;
+               }
+               chan->tx_ts_mask = ts_mask;
+
+               ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask);
+               if (ret) {
+                       dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
+                               chan_np);
+                       of_node_put(chan_np);
+                       return ret;
+               }
+               chan->rx_ts_mask = ts_mask;
+
+               mode = "transparent";
+               ret = of_property_read_string(chan_np, "fsl,operational-mode", &mode);
+               if (ret && ret != -EINVAL) {
+                       dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
+                               chan_np);
+                       of_node_put(chan_np);
+                       return ret;
+               }
+               if (!strcmp(mode, "transparent")) {
+                       chan->mode = QMC_TRANSPARENT;
+               } else if (!strcmp(mode, "hdlc")) {
+                       chan->mode = QMC_HDLC;
+               } else {
+                       dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
+                               chan_np, mode);
+                       of_node_put(chan_np);
+                       return -EINVAL;
+               }
+
+               chan->is_reverse_data = of_property_read_bool(chan_np,
+                                                             "fsl,reverse-data");
+
+               list_add_tail(&chan->list, &qmc->chan_head);
+               qmc->chans[chan->id] = chan;
+       }
+
+       return qmc_check_chans(qmc);
+}
+
+static int qmc_setup_tsa_64rxtx(struct qmc *qmc, const struct tsa_serial_info *info)
+{
+       struct qmc_chan *chan;
+       unsigned int i;
+       u16 val;
+
+       /*
+        * Use a common Tx/Rx 64 entries table.
+        * Everything was previously checked, Tx and Rx related stuffs are
+        * identical -> Used Rx related stuff to build the table
+        */
+
+       /* Invalidate all entries */
+       for (i = 0; i < 64; i++)
+               qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);
+
+       /* Set entries based on Rx stuff*/
+       list_for_each_entry(chan, &qmc->chan_head, list) {
+               for (i = 0; i < info->nb_rx_ts; i++) {
+                       if (!(chan->rx_ts_mask & (((u64)1) << i)))
+                               continue;
+
+                       val = QMC_TSA_VALID | QMC_TSA_MASK |
+                             QMC_TSA_CHANNEL(chan->id);
+                       qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val);
+               }
+       }
+
+       /* Set Wrap bit on last entry */
+       qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
+                     QMC_TSA_WRAP);
+
+       /* Init pointers to the table */
+       val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
+       qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
+       qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
+       qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
+       qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);
+
+       return 0;
+}
+
+static int qmc_setup_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_serial_info *info)
+{
+       struct qmc_chan *chan;
+       unsigned int i;
+       u16 val;
+
+       /*
+        * Use a Tx 32 entries table and a Rx 32 entries table.
+        * Everything was previously checked.
+        */
+
+       /* Invalidate all entries */
+       for (i = 0; i < 32; i++) {
+               qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);
+               qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000);
+       }
+
+       /* Set entries based on Rx and Tx stuff*/
+       list_for_each_entry(chan, &qmc->chan_head, list) {
+               /* Rx part */
+               for (i = 0; i < info->nb_rx_ts; i++) {
+                       if (!(chan->rx_ts_mask & (((u64)1) << i)))
+                               continue;
+
+                       val = QMC_TSA_VALID | QMC_TSA_MASK |
+                             QMC_TSA_CHANNEL(chan->id);
+                       qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val);
+               }
+               /* Tx part */
+               for (i = 0; i < info->nb_tx_ts; i++) {
+                       if (!(chan->tx_ts_mask & (((u64)1) << i)))
+                               continue;
+
+                       val = QMC_TSA_VALID | QMC_TSA_MASK |
+                             QMC_TSA_CHANNEL(chan->id);
+                       qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), val);
+               }
+       }
+
+       /* Set Wrap bit on last entries */
+       qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
+                     QMC_TSA_WRAP);
+       qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2),
+                     QMC_TSA_WRAP);
+
+       /* Init Rx pointers ...*/
+       val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
+       qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
+       qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
+
+       /* ... and Tx pointers */
+       val = qmc->scc_pram_offset + QMC_GBL_TSATTX;
+       qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
+       qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);
+
+       return 0;
+}
+
+static int qmc_setup_tsa(struct qmc *qmc)
+{
+       struct tsa_serial_info info;
+       int ret;
+
+       /* Retrieve info from the TSA related serial */
+       ret = tsa_serial_get_info(qmc->tsa_serial, &info);
+       if (ret)
+               return ret;
+
+       /*
+        * Setup one common 64 entries table or two 32 entries (one for Tx and
+        * one for Tx) according to assigned TS numbers.
+        */
+       return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ?
+               qmc_setup_tsa_64rxtx(qmc, &info) :
+               qmc_setup_tsa_32rx_32tx(qmc, &info);
+}
+
+static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan)
+{
+       struct tsa_serial_info info;
+       u16 first_rx, last_tx;
+       u16 trnsync;
+       int ret;
+
+       /* Retrieve info from the TSA related serial */
+       ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info);
+       if (ret)
+               return ret;
+
+       /* Find the first Rx TS allocated to the channel */
+       first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0;
+
+       /* Find the last Tx TS allocated to the channel */
+       last_tx = fls64(chan->tx_ts_mask);
+
+       trnsync = 0;
+       if (info.nb_rx_ts)
+               trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2);
+       if (info.nb_tx_ts)
+               trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2);
+
+       qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync);
+
+       dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n",
+               chan->id, trnsync,
+               first_rx, info.nb_rx_ts, chan->rx_ts_mask,
+               last_tx, info.nb_tx_ts, chan->tx_ts_mask);
+
+       return 0;
+}
+
+static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan)
+{
+       unsigned int i;
+       cbd_t __iomem *bd;
+       int ret;
+       u16 val;
+
+       chan->qmc = qmc;
+
+       /* Set channel specific parameter base address */
+       chan->s_param = qmc->dpram + (chan->id * 64);
+       /* 16 bd per channel (8 rx and 8 tx) */
+       chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS));
+       chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS;
+
+       chan->txbd_free = chan->txbds;
+       chan->txbd_done = chan->txbds;
+       chan->rxbd_free = chan->rxbds;
+       chan->rxbd_done = chan->rxbds;
+
+       /* TBASE and TBPTR*/
+       val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t);
+       qmc_write16(chan->s_param + QMC_SPE_TBASE, val);
+       qmc_write16(chan->s_param + QMC_SPE_TBPTR, val);
+
+       /* RBASE and RBPTR*/
+       val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t);
+       qmc_write16(chan->s_param + QMC_SPE_RBASE, val);
+       qmc_write16(chan->s_param + QMC_SPE_RBPTR, val);
+       qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000);
+       qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
+       qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100);
+       if (chan->mode == QMC_TRANSPARENT) {
+               qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
+               qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60);
+               val = QMC_SPE_CHAMR_MODE_TRANSP | QMC_SPE_CHAMR_TRANSP_SYNC;
+               if (chan->is_reverse_data)
+                       val |= QMC_SPE_CHAMR_TRANSP_RD;
+               qmc_write16(chan->s_param + QMC_SPE_CHAMR, val);
+               ret = qmc_setup_chan_trnsync(qmc, chan);
+               if (ret)
+                       return ret;
+       } else {
+               qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
+               qmc_write16(chan->s_param + QMC_SPE_MFLR, 60);
+               qmc_write16(chan->s_param + QMC_SPE_CHAMR,
+                       QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM);
+       }
+
+       /* Do not enable interrupts now. They will be enabled later */
+       qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000);
+
+       /* Init Rx BDs and set Wrap bit on last descriptor */
+       BUILD_BUG_ON(QMC_NB_RXBDS == 0);
+       val = QMC_BD_RX_I;
+       for (i = 0; i < QMC_NB_RXBDS; i++) {
+               bd = chan->rxbds + i;
+               qmc_write16(&bd->cbd_sc, val);
+       }
+       bd = chan->rxbds + QMC_NB_RXBDS - 1;
+       qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W);
+
+       /* Init Tx BDs and set Wrap bit on last descriptor */
+       BUILD_BUG_ON(QMC_NB_TXBDS == 0);
+       val = QMC_BD_TX_I;
+       if (chan->mode == QMC_HDLC)
+               val |= QMC_BD_TX_L | QMC_BD_TX_TC;
+       for (i = 0; i < QMC_NB_TXBDS; i++) {
+               bd = chan->txbds + i;
+               qmc_write16(&bd->cbd_sc, val);
+       }
+       bd = chan->txbds + QMC_NB_TXBDS - 1;
+       qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W);
+
+       return 0;
+}
+
+static int qmc_setup_chans(struct qmc *qmc)
+{
+       struct qmc_chan *chan;
+       int ret;
+
+       list_for_each_entry(chan, &qmc->chan_head, list) {
+               ret = qmc_setup_chan(qmc, chan);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int qmc_finalize_chans(struct qmc *qmc)
+{
+       struct qmc_chan *chan;
+       int ret;
+
+       list_for_each_entry(chan, &qmc->chan_head, list) {
+               /* Unmask channel interrupts */
+               if (chan->mode == QMC_HDLC) {
+                       qmc_write16(chan->s_param + QMC_SPE_INTMSK,
+                                   QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF |
+                                   QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY |
+                                   QMC_INT_TXB | QMC_INT_RXB);
+               } else {
+                       qmc_write16(chan->s_param + QMC_SPE_INTMSK,
+                                   QMC_INT_UN | QMC_INT_BSY |
+                                   QMC_INT_TXB | QMC_INT_RXB);
+               }
+
+               /* Forced stop the channel */
+               ret = qmc_chan_stop(chan, QMC_CHAN_ALL);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int qmc_setup_ints(struct qmc *qmc)
+{
+       unsigned int i;
+       u16 __iomem *last;
+
+       /* Raz all entries */
+       for (i = 0; i < (qmc->int_size / sizeof(u16)); i++)
+               qmc_write16(qmc->int_table + i, 0x0000);
+
+       /* Set Wrap bit on last entry */
+       if (qmc->int_size >= sizeof(u16)) {
+               last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1;
+               qmc_write16(last, QMC_INT_W);
+       }
+
+       return 0;
+}
+
+static void qmc_irq_gint(struct qmc *qmc)
+{
+       struct qmc_chan *chan;
+       unsigned int chan_id;
+       unsigned long flags;
+       u16 int_entry;
+
+       int_entry = qmc_read16(qmc->int_curr);
+       while (int_entry & QMC_INT_V) {
+               /* Clear all but the Wrap bit */
+               qmc_write16(qmc->int_curr, int_entry & QMC_INT_W);
+
+               chan_id = QMC_INT_GET_CHANNEL(int_entry);
+               chan = qmc->chans[chan_id];
+               if (!chan) {
+                       dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id);
+                       goto int_next;
+               }
+
+               if (int_entry & QMC_INT_TXB)
+                       qmc_chan_write_done(chan);
+
+               if (int_entry & QMC_INT_UN) {
+                       dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id,
+                                int_entry);
+                       chan->nb_tx_underrun++;
+               }
+
+               if (int_entry & QMC_INT_BSY) {
+                       dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id,
+                                int_entry);
+                       chan->nb_rx_busy++;
+                       /* Restart the receiver if needed */
+                       spin_lock_irqsave(&chan->rx_lock, flags);
+                       if (chan->rx_pending && !chan->is_rx_stopped) {
+                               if (chan->mode == QMC_TRANSPARENT)
+                                       qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
+                               else
+                                       qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
+                               qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
+                               chan->is_rx_halted = false;
+                       } else {
+                               chan->is_rx_halted = true;
+                       }
+                       spin_unlock_irqrestore(&chan->rx_lock, flags);
+               }
+
+               if (int_entry & QMC_INT_RXB)
+                       qmc_chan_read_done(chan);
+
+int_next:
+               if (int_entry & QMC_INT_W)
+                       qmc->int_curr = qmc->int_table;
+               else
+                       qmc->int_curr++;
+               int_entry = qmc_read16(qmc->int_curr);
+       }
+}
+
+static irqreturn_t qmc_irq_handler(int irq, void *priv)
+{
+       struct qmc *qmc = (struct qmc *)priv;
+       u16 scce;
+
+       scce = qmc_read16(qmc->scc_regs + SCC_SCCE);
+       qmc_write16(qmc->scc_regs + SCC_SCCE, scce);
+
+       if (unlikely(scce & SCC_SCCE_IQOV))
+               dev_info(qmc->dev, "IRQ queue overflow\n");
+
+       if (unlikely(scce & SCC_SCCE_GUN))
+               dev_err(qmc->dev, "Global transmitter underrun\n");
+
+       if (unlikely(scce & SCC_SCCE_GOV))
+               dev_err(qmc->dev, "Global receiver overrun\n");
+
+       /* normal interrupt */
+       if (likely(scce & SCC_SCCE_GINT))
+               qmc_irq_gint(qmc);
+
+       return IRQ_HANDLED;
+}
+
+static int qmc_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       unsigned int nb_chans;
+       struct resource *res;
+       struct qmc *qmc;
+       int irq;
+       int ret;
+
+       qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL);
+       if (!qmc)
+               return -ENOMEM;
+
+       qmc->dev = &pdev->dev;
+       INIT_LIST_HEAD(&qmc->chan_head);
+
+       qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs");
+       if (IS_ERR(qmc->scc_regs))
+               return PTR_ERR(qmc->scc_regs);
+
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram");
+       if (!res)
+               return -EINVAL;
+       qmc->scc_pram_offset = res->start - get_immrbase();
+       qmc->scc_pram = devm_ioremap_resource(qmc->dev, res);
+       if (IS_ERR(qmc->scc_pram))
+               return PTR_ERR(qmc->scc_pram);
+
+       qmc->dpram  = devm_platform_ioremap_resource_byname(pdev, "dpram");
+       if (IS_ERR(qmc->dpram))
+               return PTR_ERR(qmc->dpram);
+
+       qmc->tsa_serial = devm_tsa_serial_get_byphandle(qmc->dev, np, "fsl,tsa-serial");
+       if (IS_ERR(qmc->tsa_serial)) {
+               return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa_serial),
+                                    "Failed to get TSA serial\n");
+       }
+
+       /* Connect the serial (SCC) to TSA */
+       ret = tsa_serial_connect(qmc->tsa_serial);
+       if (ret) {
+               dev_err(qmc->dev, "Failed to connect TSA serial\n");
+               return ret;
+       }
+
+       /* Parse channels informationss */
+       ret = qmc_of_parse_chans(qmc, np);
+       if (ret)
+               goto err_tsa_serial_disconnect;
+
+       nb_chans = qmc_nb_chans(qmc);
+
+       /* Init GMSR_H and GMSR_L registers */
+       qmc_write32(qmc->scc_regs + SCC_GSMRH,
+                   SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP);
+
+       /* enable QMC mode */
+       qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_MODE_QMC);
+
+       /*
+        * Allocate the buffer descriptor table
+        * 8 rx and 8 tx descriptors per channel
+        */
+       qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t);
+       qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size,
+               &qmc->bd_dma_addr, GFP_KERNEL);
+       if (!qmc->bd_table) {
+               dev_err(qmc->dev, "Failed to allocate bd table\n");
+               ret = -ENOMEM;
+               goto err_tsa_serial_disconnect;
+       }
+       memset(qmc->bd_table, 0, qmc->bd_size);
+
+       qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr);
+
+       /* Allocate the interrupt table */
+       qmc->int_size = QMC_NB_INTS * sizeof(u16);
+       qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size,
+               &qmc->int_dma_addr, GFP_KERNEL);
+       if (!qmc->int_table) {
+               dev_err(qmc->dev, "Failed to allocate interrupt table\n");
+               ret = -ENOMEM;
+               goto err_tsa_serial_disconnect;
+       }
+       memset(qmc->int_table, 0, qmc->int_size);
+
+       qmc->int_curr = qmc->int_table;
+       qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr);
+       qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr);
+
+       /* Set MRBLR (valid for HDLC only) max MRU + max CRC */
+       qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4);
+
+       qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1);
+       qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1);
+
+       qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3);
+       qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8);
+
+       ret = qmc_setup_tsa(qmc);
+       if (ret)
+               goto err_tsa_serial_disconnect;
+
+       qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000);
+
+       ret = qmc_setup_chans(qmc);
+       if (ret)
+               goto err_tsa_serial_disconnect;
+
+       /* Init interrupts table */
+       ret = qmc_setup_ints(qmc);
+       if (ret)
+               goto err_tsa_serial_disconnect;
+
+       /* Disable and clear interrupts,  set the irq handler */
+       qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000);
+       qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F);
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               goto err_tsa_serial_disconnect;
+       ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc);
+       if (ret < 0)
+               goto err_tsa_serial_disconnect;
+
+       /* Enable interrupts */
+       qmc_write16(qmc->scc_regs + SCC_SCCM,
+               SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV);
+
+       ret = qmc_finalize_chans(qmc);
+       if (ret < 0)
+               goto err_disable_intr;
+
+       /* Enable transmiter and receiver */
+       qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+       platform_set_drvdata(pdev, qmc);
+
+       return 0;
+
+err_disable_intr:
+       qmc_write16(qmc->scc_regs + SCC_SCCM, 0);
+
+err_tsa_serial_disconnect:
+       tsa_serial_disconnect(qmc->tsa_serial);
+       return ret;
+}
+
+static int qmc_remove(struct platform_device *pdev)
+{
+       struct qmc *qmc = platform_get_drvdata(pdev);
+
+       /* Disable transmiter and receiver */
+       qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0);
+
+       /* Disable interrupts */
+       qmc_write16(qmc->scc_regs + SCC_SCCM, 0);
+
+       /* Disconnect the serial from TSA */
+       tsa_serial_disconnect(qmc->tsa_serial);
+
+       return 0;
+}
+
+static const struct of_device_id qmc_id_table[] = {
+       { .compatible = "fsl,cpm1-scc-qmc" },
+       {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, qmc_id_table);
+
+static struct platform_driver qmc_driver = {
+       .driver = {
+               .name = "fsl-qmc",
+               .of_match_table = of_match_ptr(qmc_id_table),
+       },
+       .probe = qmc_probe,
+       .remove = qmc_remove,
+};
+module_platform_driver(qmc_driver);
+
+struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name)
+{
+       struct of_phandle_args out_args;
+       struct platform_device *pdev;
+       struct qmc_chan *qmc_chan;
+       struct qmc *qmc;
+       int ret;
+
+       ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0,
+                                              &out_args);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       if (!of_match_node(qmc_driver.driver.of_match_table, out_args.np)) {
+               of_node_put(out_args.np);
+               return ERR_PTR(-EINVAL);
+       }
+
+       pdev = of_find_device_by_node(out_args.np);
+       of_node_put(out_args.np);
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       qmc = platform_get_drvdata(pdev);
+       if (!qmc) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       if (out_args.args_count != 1) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (out_args.args[0] >= ARRAY_SIZE(qmc->chans)) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       qmc_chan = qmc->chans[out_args.args[0]];
+       if (!qmc_chan) {
+               platform_device_put(pdev);
+               return ERR_PTR(-ENOENT);
+       }
+
+       return qmc_chan;
+}
+EXPORT_SYMBOL(qmc_chan_get_byphandle);
+
+void qmc_chan_put(struct qmc_chan *chan)
+{
+       put_device(chan->qmc->dev);
+}
+EXPORT_SYMBOL(qmc_chan_put);
+
+static void devm_qmc_chan_release(struct device *dev, void *res)
+{
+       struct qmc_chan **qmc_chan = res;
+
+       qmc_chan_put(*qmc_chan);
+}
+
+struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
+                                            struct device_node *np,
+                                            const char *phandle_name)
+{
+       struct qmc_chan *qmc_chan;
+       struct qmc_chan **dr;
+
+       dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return ERR_PTR(-ENOMEM);
+
+       qmc_chan = qmc_chan_get_byphandle(np, phandle_name);
+       if (!IS_ERR(qmc_chan)) {
+               *dr = qmc_chan;
+               devres_add(dev, dr);
+       } else {
+               devres_free(dr);
+       }
+
+       return qmc_chan;
+}
+EXPORT_SYMBOL(devm_qmc_chan_get_byphandle);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("CPM QMC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/fsl/qe/tsa.c b/drivers/soc/fsl/qe/tsa.c
new file mode 100644 (file)
index 0000000..3646153
--- /dev/null
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TSA driver
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include "tsa.h"
+#include <dt-bindings/soc/cpm1-fsl,tsa.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+
+/* TSA SI RAM routing tables entry */
+#define TSA_SIRAM_ENTRY_LAST           (1 << 16)
+#define TSA_SIRAM_ENTRY_BYTE           (1 << 17)
+#define TSA_SIRAM_ENTRY_CNT(x)         (((x) & 0x0f) << 18)
+#define TSA_SIRAM_ENTRY_CSEL_MASK      (0x7 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_NU                (0x0 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_SCC2      (0x2 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_SCC3      (0x3 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_SCC4      (0x4 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_SMC1      (0x5 << 22)
+#define TSA_SIRAM_ENTRY_CSEL_SMC2      (0x6 << 22)
+
+/* SI mode register (32 bits) */
+#define TSA_SIMODE     0x00
+#define   TSA_SIMODE_SMC2                      0x80000000
+#define   TSA_SIMODE_SMC1                      0x00008000
+#define   TSA_SIMODE_TDMA(x)                   ((x) << 0)
+#define   TSA_SIMODE_TDMB(x)                   ((x) << 16)
+#define     TSA_SIMODE_TDM_MASK                        0x0fff
+#define     TSA_SIMODE_TDM_SDM_MASK            0x0c00
+#define       TSA_SIMODE_TDM_SDM_NORM          0x0000
+#define       TSA_SIMODE_TDM_SDM_ECHO          0x0400
+#define       TSA_SIMODE_TDM_SDM_INTL_LOOP     0x0800
+#define       TSA_SIMODE_TDM_SDM_LOOP_CTRL     0x0c00
+#define     TSA_SIMODE_TDM_RFSD(x)             ((x) << 8)
+#define     TSA_SIMODE_TDM_DSC                 0x0080
+#define     TSA_SIMODE_TDM_CRT                 0x0040
+#define     TSA_SIMODE_TDM_STZ                 0x0020
+#define     TSA_SIMODE_TDM_CE                  0x0010
+#define     TSA_SIMODE_TDM_FE                  0x0008
+#define     TSA_SIMODE_TDM_GM                  0x0004
+#define     TSA_SIMODE_TDM_TFSD(x)             ((x) << 0)
+
+/* SI global mode register (8 bits) */
+#define TSA_SIGMR      0x04
+#define TSA_SIGMR_ENB                  (1<<3)
+#define TSA_SIGMR_ENA                  (1<<2)
+#define TSA_SIGMR_RDM_MASK             0x03
+#define   TSA_SIGMR_RDM_STATIC_TDMA    0x00
+#define   TSA_SIGMR_RDM_DYN_TDMA       0x01
+#define   TSA_SIGMR_RDM_STATIC_TDMAB   0x02
+#define   TSA_SIGMR_RDM_DYN_TDMAB      0x03
+
+/* SI status register (8 bits) */
+#define TSA_SISTR      0x06
+
+/* SI command register (8 bits) */
+#define TSA_SICMR      0x07
+
+/* SI clock route register (32 bits) */
+#define TSA_SICR       0x0C
+#define   TSA_SICR_SCC2(x)             ((x) << 8)
+#define   TSA_SICR_SCC3(x)             ((x) << 16)
+#define   TSA_SICR_SCC4(x)             ((x) << 24)
+#define     TSA_SICR_SCC_MASK          0x0ff
+#define     TSA_SICR_SCC_GRX           (1 << 7)
+#define     TSA_SICR_SCC_SCX_TSA       (1 << 6)
+#define     TSA_SICR_SCC_RXCS_MASK     (0x7 << 3)
+#define       TSA_SICR_SCC_RXCS_BRG1   (0x0 << 3)
+#define       TSA_SICR_SCC_RXCS_BRG2   (0x1 << 3)
+#define       TSA_SICR_SCC_RXCS_BRG3   (0x2 << 3)
+#define       TSA_SICR_SCC_RXCS_BRG4   (0x3 << 3)
+#define       TSA_SICR_SCC_RXCS_CLK15  (0x4 << 3)
+#define       TSA_SICR_SCC_RXCS_CLK26  (0x5 << 3)
+#define       TSA_SICR_SCC_RXCS_CLK37  (0x6 << 3)
+#define       TSA_SICR_SCC_RXCS_CLK48  (0x7 << 3)
+#define     TSA_SICR_SCC_TXCS_MASK     (0x7 << 0)
+#define       TSA_SICR_SCC_TXCS_BRG1   (0x0 << 0)
+#define       TSA_SICR_SCC_TXCS_BRG2   (0x1 << 0)
+#define       TSA_SICR_SCC_TXCS_BRG3   (0x2 << 0)
+#define       TSA_SICR_SCC_TXCS_BRG4   (0x3 << 0)
+#define       TSA_SICR_SCC_TXCS_CLK15  (0x4 << 0)
+#define       TSA_SICR_SCC_TXCS_CLK26  (0x5 << 0)
+#define       TSA_SICR_SCC_TXCS_CLK37  (0x6 << 0)
+#define       TSA_SICR_SCC_TXCS_CLK48  (0x7 << 0)
+
+/* Serial interface RAM pointer register (32 bits) */
+#define TSA_SIRP       0x10
+
+struct tsa_entries_area {
+       void *__iomem entries_start;
+       void *__iomem entries_next;
+       void *__iomem last_entry;
+};
+
+struct tsa_tdm {
+       bool is_enable;
+       struct clk *l1rclk_clk;
+       struct clk *l1rsync_clk;
+       struct clk *l1tclk_clk;
+       struct clk *l1tsync_clk;
+       u32 simode_tdm;
+};
+
+#define TSA_TDMA       0
+#define TSA_TDMB       1
+
+struct tsa {
+       struct device *dev;
+       void *__iomem si_regs;
+       void *__iomem si_ram;
+       resource_size_t si_ram_sz;
+       spinlock_t      lock;
+       int tdms; /* TSA_TDMx ORed */
+       struct tsa_tdm tdm[2]; /* TDMa and TDMb */
+       struct tsa_serial {
+               unsigned int id;
+               struct tsa_serial_info info;
+       } serials[6];
+};
+
+static inline struct tsa *tsa_serial_get_tsa(struct tsa_serial *tsa_serial)
+{
+       /* The serials table is indexed by the serial id */
+       return container_of(tsa_serial, struct tsa, serials[tsa_serial->id]);
+}
+
+static inline void tsa_write32(void *__iomem addr, u32 val)
+{
+       iowrite32be(val, addr);
+}
+
+static inline void tsa_write8(void *__iomem addr, u32 val)
+{
+       iowrite8(val, addr);
+}
+
+static inline u32 tsa_read32(void *__iomem addr)
+{
+       return ioread32be(addr);
+}
+
+static inline void tsa_clrbits32(void *__iomem addr, u32 clr)
+{
+       tsa_write32(addr, tsa_read32(addr) & ~clr);
+}
+
+static inline void tsa_clrsetbits32(void *__iomem addr, u32 clr, u32 set)
+{
+       tsa_write32(addr, (tsa_read32(addr) & ~clr) | set);
+}
+
+int tsa_serial_connect(struct tsa_serial *tsa_serial)
+{
+       struct tsa *tsa = tsa_serial_get_tsa(tsa_serial);
+       unsigned long flags;
+       u32 clear;
+       u32 set;
+
+       switch (tsa_serial->id) {
+       case FSL_CPM_TSA_SCC2:
+               clear = TSA_SICR_SCC2(TSA_SICR_SCC_MASK);
+               set = TSA_SICR_SCC2(TSA_SICR_SCC_SCX_TSA);
+               break;
+       case FSL_CPM_TSA_SCC3:
+               clear = TSA_SICR_SCC3(TSA_SICR_SCC_MASK);
+               set = TSA_SICR_SCC3(TSA_SICR_SCC_SCX_TSA);
+               break;
+       case FSL_CPM_TSA_SCC4:
+               clear = TSA_SICR_SCC4(TSA_SICR_SCC_MASK);
+               set = TSA_SICR_SCC4(TSA_SICR_SCC_SCX_TSA);
+               break;
+       default:
+               dev_err(tsa->dev, "Unsupported serial id %u\n", tsa_serial->id);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&tsa->lock, flags);
+       tsa_clrsetbits32(tsa->si_regs + TSA_SICR, clear, set);
+       spin_unlock_irqrestore(&tsa->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(tsa_serial_connect);
+
+int tsa_serial_disconnect(struct tsa_serial *tsa_serial)
+{
+       struct tsa *tsa = tsa_serial_get_tsa(tsa_serial);
+       unsigned long flags;
+       u32 clear;
+
+       switch (tsa_serial->id) {
+       case FSL_CPM_TSA_SCC2:
+               clear = TSA_SICR_SCC2(TSA_SICR_SCC_MASK);
+               break;
+       case FSL_CPM_TSA_SCC3:
+               clear = TSA_SICR_SCC3(TSA_SICR_SCC_MASK);
+               break;
+       case FSL_CPM_TSA_SCC4:
+               clear = TSA_SICR_SCC4(TSA_SICR_SCC_MASK);
+               break;
+       default:
+               dev_err(tsa->dev, "Unsupported serial id %u\n", tsa_serial->id);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&tsa->lock, flags);
+       tsa_clrsetbits32(tsa->si_regs + TSA_SICR, clear, 0);
+       spin_unlock_irqrestore(&tsa->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(tsa_serial_disconnect);
+
+int tsa_serial_get_info(struct tsa_serial *tsa_serial, struct tsa_serial_info *info)
+{
+       memcpy(info, &tsa_serial->info, sizeof(*info));
+       return 0;
+}
+EXPORT_SYMBOL(tsa_serial_get_info);
+
+static void tsa_init_entries_area(struct tsa *tsa, struct tsa_entries_area *area,
+                                 u32 tdms, u32 tdm_id, bool is_rx)
+{
+       resource_size_t quarter;
+       resource_size_t half;
+
+       quarter = tsa->si_ram_sz/4;
+       half = tsa->si_ram_sz/2;
+
+       if (tdms == BIT(TSA_TDMA)) {
+               /* Only TDMA */
+               if (is_rx) {
+                       /* First half of si_ram */
+                       area->entries_start = tsa->si_ram;
+                       area->entries_next = area->entries_start + half;
+                       area->last_entry = NULL;
+               } else {
+                       /* Second half of si_ram */
+                       area->entries_start = tsa->si_ram + half;
+                       area->entries_next = area->entries_start + half;
+                       area->last_entry = NULL;
+               }
+       } else {
+               /* Only TDMB or both TDMs */
+               if (tdm_id == TSA_TDMA) {
+                       if (is_rx) {
+                               /* First half of first half of si_ram */
+                               area->entries_start = tsa->si_ram;
+                               area->entries_next = area->entries_start + quarter;
+                               area->last_entry = NULL;
+                       } else {
+                               /* First half of second half of si_ram */
+                               area->entries_start = tsa->si_ram + (2 * quarter);
+                               area->entries_next = area->entries_start + quarter;
+                               area->last_entry = NULL;
+                       }
+               } else {
+                       if (is_rx) {
+                               /* Second half of first half of si_ram */
+                               area->entries_start = tsa->si_ram + quarter;
+                               area->entries_next = area->entries_start + quarter;
+                               area->last_entry = NULL;
+                       } else {
+                               /* Second half of second half of si_ram */
+                               area->entries_start = tsa->si_ram + (3 * quarter);
+                               area->entries_next = area->entries_start + quarter;
+                               area->last_entry = NULL;
+                       }
+               }
+       }
+}
+
+static const char *tsa_serial_id2name(struct tsa *tsa, u32 serial_id)
+{
+       switch (serial_id) {
+       case FSL_CPM_TSA_NU:    return "Not used";
+       case FSL_CPM_TSA_SCC2:  return "SCC2";
+       case FSL_CPM_TSA_SCC3:  return "SCC3";
+       case FSL_CPM_TSA_SCC4:  return "SCC4";
+       case FSL_CPM_TSA_SMC1:  return "SMC1";
+       case FSL_CPM_TSA_SMC2:  return "SMC2";
+       default:
+               break;
+       }
+       return NULL;
+}
+
+static u32 tsa_serial_id2csel(struct tsa *tsa, u32 serial_id)
+{
+       switch (serial_id) {
+       case FSL_CPM_TSA_SCC2:  return TSA_SIRAM_ENTRY_CSEL_SCC2;
+       case FSL_CPM_TSA_SCC3:  return TSA_SIRAM_ENTRY_CSEL_SCC3;
+       case FSL_CPM_TSA_SCC4:  return TSA_SIRAM_ENTRY_CSEL_SCC4;
+       case FSL_CPM_TSA_SMC1:  return TSA_SIRAM_ENTRY_CSEL_SMC1;
+       case FSL_CPM_TSA_SMC2:  return TSA_SIRAM_ENTRY_CSEL_SMC2;
+       default:
+               break;
+       }
+       return TSA_SIRAM_ENTRY_CSEL_NU;
+}
+
+static int tsa_add_entry(struct tsa *tsa, struct tsa_entries_area *area,
+                        u32 count, u32 serial_id)
+{
+       void *__iomem addr;
+       u32 left;
+       u32 val;
+       u32 cnt;
+       u32 nb;
+
+       addr = area->last_entry ? area->last_entry + 4 : area->entries_start;
+
+       nb = DIV_ROUND_UP(count, 8);
+       if ((addr + (nb * 4)) > area->entries_next) {
+               dev_err(tsa->dev, "si ram area full\n");
+               return -ENOSPC;
+       }
+
+       if (area->last_entry) {
+               /* Clear last flag */
+               tsa_clrbits32(area->last_entry, TSA_SIRAM_ENTRY_LAST);
+       }
+
+       left = count;
+       while (left) {
+               val = TSA_SIRAM_ENTRY_BYTE | tsa_serial_id2csel(tsa, serial_id);
+
+               if (left > 16) {
+                       cnt = 16;
+               } else {
+                       cnt = left;
+                       val |= TSA_SIRAM_ENTRY_LAST;
+                       area->last_entry = addr;
+               }
+               val |= TSA_SIRAM_ENTRY_CNT(cnt - 1);
+
+               tsa_write32(addr, val);
+               addr += 4;
+               left -= cnt;
+       }
+
+       return 0;
+}
+
+static int tsa_of_parse_tdm_route(struct tsa *tsa, struct device_node *tdm_np,
+                                 u32 tdms, u32 tdm_id, bool is_rx)
+{
+       struct tsa_entries_area area;
+       const char *route_name;
+       u32 serial_id;
+       int len, i;
+       u32 count;
+       const char *serial_name;
+       struct tsa_serial_info *serial_info;
+       struct tsa_tdm *tdm;
+       int ret;
+       u32 ts;
+
+       route_name = is_rx ? "fsl,rx-ts-routes" : "fsl,tx-ts-routes";
+
+       len = of_property_count_u32_elems(tdm_np,  route_name);
+       if (len < 0) {
+               dev_err(tsa->dev, "%pOF: failed to read %s\n", tdm_np, route_name);
+               return len;
+       }
+       if (len % 2 != 0) {
+               dev_err(tsa->dev, "%pOF: wrong %s format\n", tdm_np, route_name);
+               return -EINVAL;
+       }
+
+       tsa_init_entries_area(tsa, &area, tdms, tdm_id, is_rx);
+       ts = 0;
+       for (i = 0; i < len; i += 2) {
+               of_property_read_u32_index(tdm_np, route_name, i, &count);
+               of_property_read_u32_index(tdm_np, route_name, i + 1, &serial_id);
+
+               if (serial_id >= ARRAY_SIZE(tsa->serials)) {
+                       dev_err(tsa->dev, "%pOF: invalid serial id (%u)\n",
+                               tdm_np, serial_id);
+                       return -EINVAL;
+               }
+
+               serial_name = tsa_serial_id2name(tsa, serial_id);
+               if (!serial_name) {
+                       dev_err(tsa->dev, "%pOF: unsupported serial id (%u)\n",
+                               tdm_np, serial_id);
+                       return -EINVAL;
+               }
+
+               dev_dbg(tsa->dev, "tdm_id=%u, %s ts %u..%u -> %s\n",
+                       tdm_id, route_name, ts, ts+count-1, serial_name);
+               ts += count;
+
+               ret = tsa_add_entry(tsa, &area, count, serial_id);
+               if (ret)
+                       return ret;
+
+               serial_info = &tsa->serials[serial_id].info;
+               tdm = &tsa->tdm[tdm_id];
+               if (is_rx) {
+                       serial_info->rx_fs_rate = clk_get_rate(tdm->l1rsync_clk);
+                       serial_info->rx_bit_rate = clk_get_rate(tdm->l1rclk_clk);
+                       serial_info->nb_rx_ts += count;
+               } else {
+                       serial_info->tx_fs_rate = tdm->l1tsync_clk ?
+                               clk_get_rate(tdm->l1tsync_clk) :
+                               clk_get_rate(tdm->l1rsync_clk);
+                       serial_info->tx_bit_rate = tdm->l1tclk_clk ?
+                               clk_get_rate(tdm->l1tclk_clk) :
+                               clk_get_rate(tdm->l1rclk_clk);
+                       serial_info->nb_tx_ts += count;
+               }
+       }
+       return 0;
+}
+
+static inline int tsa_of_parse_tdm_rx_route(struct tsa *tsa,
+                                           struct device_node *tdm_np,
+                                           u32 tdms, u32 tdm_id)
+{
+       return tsa_of_parse_tdm_route(tsa, tdm_np, tdms, tdm_id, true);
+}
+
+static inline int tsa_of_parse_tdm_tx_route(struct tsa *tsa,
+                                           struct device_node *tdm_np,
+                                           u32 tdms, u32 tdm_id)
+{
+       return tsa_of_parse_tdm_route(tsa, tdm_np, tdms, tdm_id, false);
+}
+
+static int tsa_of_parse_tdms(struct tsa *tsa, struct device_node *np)
+{
+       struct device_node *tdm_np;
+       struct tsa_tdm *tdm;
+       struct clk *clk;
+       u32 tdm_id, val;
+       int ret;
+       int i;
+
+       tsa->tdms = 0;
+       tsa->tdm[0].is_enable = false;
+       tsa->tdm[1].is_enable = false;
+
+       for_each_available_child_of_node(np, tdm_np) {
+               ret = of_property_read_u32(tdm_np, "reg", &tdm_id);
+               if (ret) {
+                       dev_err(tsa->dev, "%pOF: failed to read reg\n", tdm_np);
+                       of_node_put(tdm_np);
+                       return ret;
+               }
+               switch (tdm_id) {
+               case 0:
+                       tsa->tdms |= BIT(TSA_TDMA);
+                       break;
+               case 1:
+                       tsa->tdms |= BIT(TSA_TDMB);
+                       break;
+               default:
+                       dev_err(tsa->dev, "%pOF: Invalid tdm_id (%u)\n", tdm_np,
+                               tdm_id);
+                       of_node_put(tdm_np);
+                       return -EINVAL;
+               }
+       }
+
+       for_each_available_child_of_node(np, tdm_np) {
+               ret = of_property_read_u32(tdm_np, "reg", &tdm_id);
+               if (ret) {
+                       dev_err(tsa->dev, "%pOF: failed to read reg\n", tdm_np);
+                       of_node_put(tdm_np);
+                       return ret;
+               }
+
+               tdm = &tsa->tdm[tdm_id];
+               tdm->simode_tdm = TSA_SIMODE_TDM_SDM_NORM;
+
+               val = 0;
+               ret = of_property_read_u32(tdm_np, "fsl,rx-frame-sync-delay-bits",
+                                          &val);
+               if (ret && ret != -EINVAL) {
+                       dev_err(tsa->dev,
+                               "%pOF: failed to read fsl,rx-frame-sync-delay-bits\n",
+                               tdm_np);
+                       of_node_put(tdm_np);
+                       return ret;
+               }
+               if (val > 3) {
+                       dev_err(tsa->dev,
+                               "%pOF: Invalid fsl,rx-frame-sync-delay-bits (%u)\n",
+                               tdm_np, val);
+                       of_node_put(tdm_np);
+                       return -EINVAL;
+               }
+               tdm->simode_tdm |= TSA_SIMODE_TDM_RFSD(val);
+
+               val = 0;
+               ret = of_property_read_u32(tdm_np, "fsl,tx-frame-sync-delay-bits",
+                                          &val);
+               if (ret && ret != -EINVAL) {
+                       dev_err(tsa->dev,
+                               "%pOF: failed to read fsl,tx-frame-sync-delay-bits\n",
+                               tdm_np);
+                       of_node_put(tdm_np);
+                       return ret;
+               }
+               if (val > 3) {
+                       dev_err(tsa->dev,
+                               "%pOF: Invalid fsl,tx-frame-sync-delay-bits (%u)\n",
+                               tdm_np, val);
+                       of_node_put(tdm_np);
+                       return -EINVAL;
+               }
+               tdm->simode_tdm |= TSA_SIMODE_TDM_TFSD(val);
+
+               if (of_property_read_bool(tdm_np, "fsl,common-rxtx-pins"))
+                       tdm->simode_tdm |= TSA_SIMODE_TDM_CRT;
+
+               if (of_property_read_bool(tdm_np, "fsl,clock-falling-edge"))
+                       tdm->simode_tdm |= TSA_SIMODE_TDM_CE;
+
+               if (of_property_read_bool(tdm_np, "fsl,fsync-rising-edge"))
+                       tdm->simode_tdm |= TSA_SIMODE_TDM_FE;
+
+               if (of_property_read_bool(tdm_np, "fsl,double-speed-clock"))
+                       tdm->simode_tdm |= TSA_SIMODE_TDM_DSC;
+
+               clk = of_clk_get_by_name(tdm_np, "l1rsync");
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+               ret = clk_prepare_enable(clk);
+               if (ret) {
+                       clk_put(clk);
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+               tdm->l1rsync_clk = clk;
+
+               clk = of_clk_get_by_name(tdm_np, "l1rclk");
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+               ret = clk_prepare_enable(clk);
+               if (ret) {
+                       clk_put(clk);
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+               tdm->l1rclk_clk = clk;
+
+               if (!(tdm->simode_tdm & TSA_SIMODE_TDM_CRT)) {
+                       clk = of_clk_get_by_name(tdm_np, "l1tsync");
+                       if (IS_ERR(clk)) {
+                               ret = PTR_ERR(clk);
+                               of_node_put(tdm_np);
+                               goto err;
+                       }
+                       ret = clk_prepare_enable(clk);
+                       if (ret) {
+                               clk_put(clk);
+                               of_node_put(tdm_np);
+                               goto err;
+                       }
+                       tdm->l1tsync_clk = clk;
+
+                       clk = of_clk_get_by_name(tdm_np, "l1tclk");
+                       if (IS_ERR(clk)) {
+                               ret = PTR_ERR(clk);
+                               of_node_put(tdm_np);
+                               goto err;
+                       }
+                       ret = clk_prepare_enable(clk);
+                       if (ret) {
+                               clk_put(clk);
+                               of_node_put(tdm_np);
+                               goto err;
+                       }
+                       tdm->l1tclk_clk = clk;
+               }
+
+               ret = tsa_of_parse_tdm_rx_route(tsa, tdm_np, tsa->tdms, tdm_id);
+               if (ret) {
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+
+               ret = tsa_of_parse_tdm_tx_route(tsa, tdm_np, tsa->tdms, tdm_id);
+               if (ret) {
+                       of_node_put(tdm_np);
+                       goto err;
+               }
+
+               tdm->is_enable = true;
+       }
+       return 0;
+
+err:
+       for (i = 0; i < 2; i++) {
+               if (tsa->tdm[i].l1rsync_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
+                       clk_put(tsa->tdm[i].l1rsync_clk);
+               }
+               if (tsa->tdm[i].l1rclk_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
+                       clk_put(tsa->tdm[i].l1rclk_clk);
+               }
+               if (tsa->tdm[i].l1tsync_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
+                       clk_put(tsa->tdm[i].l1rsync_clk);
+               }
+               if (tsa->tdm[i].l1tclk_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
+                       clk_put(tsa->tdm[i].l1rclk_clk);
+               }
+       }
+       return ret;
+}
+
+static void tsa_init_si_ram(struct tsa *tsa)
+{
+       resource_size_t i;
+
+       /* Fill all entries as the last one */
+       for (i = 0; i < tsa->si_ram_sz; i += 4)
+               tsa_write32(tsa->si_ram + i, TSA_SIRAM_ENTRY_LAST);
+}
+
+static int tsa_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *res;
+       struct tsa *tsa;
+       unsigned int i;
+       u32 val;
+       int ret;
+
+       tsa = devm_kzalloc(&pdev->dev, sizeof(*tsa), GFP_KERNEL);
+       if (!tsa)
+               return -ENOMEM;
+
+       tsa->dev = &pdev->dev;
+
+       for (i = 0; i < ARRAY_SIZE(tsa->serials); i++)
+               tsa->serials[i].id = i;
+
+       spin_lock_init(&tsa->lock);
+
+       tsa->si_regs = devm_platform_ioremap_resource_byname(pdev, "si_regs");
+       if (IS_ERR(tsa->si_regs))
+               return PTR_ERR(tsa->si_regs);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "si_ram");
+       if (!res) {
+               dev_err(tsa->dev, "si_ram resource missing\n");
+               return -EINVAL;
+       }
+       tsa->si_ram_sz = resource_size(res);
+       tsa->si_ram = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(tsa->si_ram))
+               return PTR_ERR(tsa->si_ram);
+
+       tsa_init_si_ram(tsa);
+
+       ret = tsa_of_parse_tdms(tsa, np);
+       if (ret)
+               return ret;
+
+       /* Set SIMODE */
+       val = 0;
+       if (tsa->tdm[0].is_enable)
+               val |= TSA_SIMODE_TDMA(tsa->tdm[0].simode_tdm);
+       if (tsa->tdm[1].is_enable)
+               val |= TSA_SIMODE_TDMB(tsa->tdm[1].simode_tdm);
+
+       tsa_clrsetbits32(tsa->si_regs + TSA_SIMODE,
+                        TSA_SIMODE_TDMA(TSA_SIMODE_TDM_MASK) |
+                        TSA_SIMODE_TDMB(TSA_SIMODE_TDM_MASK),
+                        val);
+
+       /* Set SIGMR */
+       val = (tsa->tdms == BIT(TSA_TDMA)) ?
+               TSA_SIGMR_RDM_STATIC_TDMA : TSA_SIGMR_RDM_STATIC_TDMAB;
+       if (tsa->tdms & BIT(TSA_TDMA))
+               val |= TSA_SIGMR_ENA;
+       if (tsa->tdms & BIT(TSA_TDMB))
+               val |= TSA_SIGMR_ENB;
+       tsa_write8(tsa->si_regs + TSA_SIGMR, val);
+
+       platform_set_drvdata(pdev, tsa);
+
+       return 0;
+}
+
+static int tsa_remove(struct platform_device *pdev)
+{
+       struct tsa *tsa = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               if (tsa->tdm[i].l1rsync_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
+                       clk_put(tsa->tdm[i].l1rsync_clk);
+               }
+               if (tsa->tdm[i].l1rclk_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
+                       clk_put(tsa->tdm[i].l1rclk_clk);
+               }
+               if (tsa->tdm[i].l1tsync_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
+                       clk_put(tsa->tdm[i].l1rsync_clk);
+               }
+               if (tsa->tdm[i].l1tclk_clk) {
+                       clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
+                       clk_put(tsa->tdm[i].l1rclk_clk);
+               }
+       }
+       return 0;
+}
+
+static const struct of_device_id tsa_id_table[] = {
+       { .compatible = "fsl,cpm1-tsa" },
+       {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, tsa_id_table);
+
+static struct platform_driver tsa_driver = {
+       .driver = {
+               .name = "fsl-tsa",
+               .of_match_table = of_match_ptr(tsa_id_table),
+       },
+       .probe = tsa_probe,
+       .remove = tsa_remove,
+};
+module_platform_driver(tsa_driver);
+
+struct tsa_serial *tsa_serial_get_byphandle(struct device_node *np,
+                                           const char *phandle_name)
+{
+       struct of_phandle_args out_args;
+       struct platform_device *pdev;
+       struct tsa_serial *tsa_serial;
+       struct tsa *tsa;
+       int ret;
+
+       ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, &out_args);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       if (!of_match_node(tsa_driver.driver.of_match_table, out_args.np)) {
+               of_node_put(out_args.np);
+               return ERR_PTR(-EINVAL);
+       }
+
+       pdev = of_find_device_by_node(out_args.np);
+       of_node_put(out_args.np);
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       tsa = platform_get_drvdata(pdev);
+       if (!tsa) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       if (out_args.args_count != 1) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (out_args.args[0] >= ARRAY_SIZE(tsa->serials)) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       tsa_serial = &tsa->serials[out_args.args[0]];
+
+       /*
+        * Be sure that the serial id matches the phandle arg.
+        * The tsa_serials table is indexed by serial ids. The serial id is set
+        * during the probe() call and needs to be coherent.
+        */
+       if (WARN_ON(tsa_serial->id != out_args.args[0])) {
+               platform_device_put(pdev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return tsa_serial;
+}
+EXPORT_SYMBOL(tsa_serial_get_byphandle);
+
+void tsa_serial_put(struct tsa_serial *tsa_serial)
+{
+       struct tsa *tsa = tsa_serial_get_tsa(tsa_serial);
+
+       put_device(tsa->dev);
+}
+EXPORT_SYMBOL(tsa_serial_put);
+
+static void devm_tsa_serial_release(struct device *dev, void *res)
+{
+       struct tsa_serial **tsa_serial = res;
+
+       tsa_serial_put(*tsa_serial);
+}
+
+struct tsa_serial *devm_tsa_serial_get_byphandle(struct device *dev,
+                                                struct device_node *np,
+                                                const char *phandle_name)
+{
+       struct tsa_serial *tsa_serial;
+       struct tsa_serial **dr;
+
+       dr = devres_alloc(devm_tsa_serial_release, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return ERR_PTR(-ENOMEM);
+
+       tsa_serial = tsa_serial_get_byphandle(np, phandle_name);
+       if (!IS_ERR(tsa_serial)) {
+               *dr = tsa_serial;
+               devres_add(dev, dr);
+       } else {
+               devres_free(dr);
+       }
+
+       return tsa_serial;
+}
+EXPORT_SYMBOL(devm_tsa_serial_get_byphandle);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("CPM TSA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/fsl/qe/tsa.h b/drivers/soc/fsl/qe/tsa.h
new file mode 100644 (file)
index 0000000..d9df89b
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TSA management
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+#ifndef __SOC_FSL_TSA_H__
+#define __SOC_FSL_TSA_H__
+
+#include <linux/types.h>
+
+struct device_node;
+struct device;
+struct tsa_serial;
+
+struct tsa_serial *tsa_serial_get_byphandle(struct device_node *np,
+                                           const char *phandle_name);
+void tsa_serial_put(struct tsa_serial *tsa_serial);
+struct tsa_serial *devm_tsa_serial_get_byphandle(struct device *dev,
+                                                struct device_node *np,
+                                                const char *phandle_name);
+
+/* Connect and disconnect the TSA serial */
+int tsa_serial_connect(struct tsa_serial *tsa_serial);
+int tsa_serial_disconnect(struct tsa_serial *tsa_serial);
+
+/* Cell information */
+struct tsa_serial_info {
+       unsigned long rx_fs_rate;
+       unsigned long rx_bit_rate;
+       u8 nb_rx_ts;
+       unsigned long tx_fs_rate;
+       unsigned long tx_bit_rate;
+       u8 nb_tx_ts;
+};
+
+/* Get information */
+int tsa_serial_get_info(struct tsa_serial *tsa_serial, struct tsa_serial_info *info);
+
+#endif /* __SOC_FSL_TSA_H__ */
diff --git a/include/dt-bindings/soc/cpm1-fsl,tsa.h b/include/dt-bindings/soc/cpm1-fsl,tsa.h
new file mode 100644 (file)
index 0000000..2cc44e8
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_SOC_FSL_TSA_H
+#define __DT_BINDINGS_SOC_FSL_TSA_H
+
+#define FSL_CPM_TSA_NU         0       /* Pseuso Cell Id for not used item */
+#define FSL_CPM_TSA_SCC2       1
+#define FSL_CPM_TSA_SCC3       2
+#define FSL_CPM_TSA_SCC4       3
+#define FSL_CPM_TSA_SMC1       4
+#define FSL_CPM_TSA_SMC2       5
+
+#endif
index 076da4b2c28dd9ae081d9c083037388f7eb9f860..25386af184451b025bf2ab0220c06e9d506a9c8f 100644 (file)
 #define CS35L45_ASP_TX_HIZ_UNUSED      0x1
 #define CS35L45_ASP_TX_HIZ_DISABLED    0x2
 
+/*
+ * Optional GPIOX Sub-nodes:
+ *  The cs35l45 node can have up to three "cirrus,gpio-ctrlX" ('X' = [1,2,3])
+ *  sub-nodes for configuring the GPIO pins.
+ *
+ * - gpio-dir : GPIO pin direction. Valid only when 'gpio-ctrl'
+ *   is 1.
+ *    0 = Output
+ *    1 = Input (Default)
+ *
+ * - gpio-lvl : GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0.
+ *
+ *    0 = Low (Default)
+ *    1 = High
+ *
+ * - gpio-op-cfg : GPIO output configuration. Valid only when 'gpio-ctrl' is 1
+ *   and 'gpio-dir' is 0.
+ *
+ *    0 = CMOS (Default)
+ *    1 = Open Drain
+ *
+ * - gpio-pol : GPIO output polarity select. Valid only when 'gpio-ctrl' is 1
+ *   and 'gpio-dir' is 0.
+ *
+ *    0 = Non-inverted, Active High (Default)
+ *    1 = Inverted, Active Low
+ *
+ * - gpio-invert : Defines the polarity of the GPIO pin if configured
+ *   as input.
+ *
+ *    0 = Not inverted (Default)
+ *    1 = Inverted
+ *
+ * - gpio-ctrl : Defines the function of the GPIO pin.
+ *
+ * GPIO1:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2 = Pin acts as MDSYNC, direction controlled by MDSYNC
+ *   3-7 = Reserved
+ *
+ * GPIO2:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2 = Pin acts as open drain INT
+ *   3 = Reserved
+ *   4 = Pin acts as push-pull output INT. Active low.
+ *   5 = Pin acts as push-pull output INT. Active high.
+ *   6,7 = Reserved
+ *
+ * GPIO3:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2-7 = Reserved
+ */
+#define CS35L45_NUM_GPIOS      0x3
+
 #endif /* DT_CS35L45_H */
index cad828e21c72bc781e9d8da60dc3cf773bc4384e..29cd11d5a3cfd187a1bfbb6125c10a1940f027ce 100644 (file)
@@ -156,6 +156,7 @@ struct cs_dsp {
        unsigned int sysclk_reg;
        unsigned int sysclk_mask;
        unsigned int sysclk_shift;
+       bool no_core_startstop;
 
        struct list_head alg_regions;
 
diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h
new file mode 100644 (file)
index 0000000..3c61a50
--- /dev/null
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QMC management
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+#ifndef __SOC_FSL_QMC_H__
+#define __SOC_FSL_QMC_H__
+
+#include <linux/types.h>
+
+struct device_node;
+struct device;
+struct qmc_chan;
+
+struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name);
+void qmc_chan_put(struct qmc_chan *chan);
+struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np,
+                                            const char *phandle_name);
+
+enum qmc_mode {
+       QMC_TRANSPARENT,
+       QMC_HDLC,
+};
+
+struct qmc_chan_info {
+       enum qmc_mode mode;
+       unsigned long rx_fs_rate;
+       unsigned long rx_bit_rate;
+       u8 nb_rx_ts;
+       unsigned long tx_fs_rate;
+       unsigned long tx_bit_rate;
+       u8 nb_tx_ts;
+};
+
+int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info);
+
+struct qmc_chan_param {
+       enum qmc_mode mode;
+       union {
+               struct {
+                       u16 max_rx_buf_size;
+                       u16 max_rx_frame_size;
+                       bool is_crc32;
+               } hdlc;
+               struct {
+                       u16 max_rx_buf_size;
+               } transp;
+       };
+};
+
+int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param);
+
+int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
+                         void (*complete)(void *context), void *context);
+
+int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
+                        void (*complete)(void *context, size_t length),
+                        void *context);
+
+#define QMC_CHAN_READ  (1<<0)
+#define QMC_CHAN_WRITE (1<<1)
+#define QMC_CHAN_ALL   (QMC_CHAN_READ | QMC_CHAN_WRITE)
+
+int qmc_chan_start(struct qmc_chan *chan, int direction);
+int qmc_chan_stop(struct qmc_chan *chan, int direction);
+int qmc_chan_reset(struct qmc_chan *chan, int direction);
+
+#endif /* __SOC_FSL_QMC_H__ */
index 49200ec26dc417f41a739ad49cb01d42710b6b14..c495c6d5fbe008c7d1ae3e7fc2d98f0e79cbf6e5 100644 (file)
@@ -335,6 +335,9 @@ static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg,
 #ifdef CONFIG_PM
 void snd_ac97_suspend(struct snd_ac97 *ac97);
 void snd_ac97_resume(struct snd_ac97 *ac97);
+#else
+static inline void snd_ac97_suspend(struct snd_ac97 *ac97) {}
+static inline void snd_ac97_resume(struct snd_ac97 *ac97) {}
 #endif
 int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
        unsigned int id_mask);
index 9ac5918269a5da22217b9ff2d8eb2e9388315e17..7239d943942cbb2658595bc464a384c0a1d22340 100644 (file)
@@ -11,6 +11,7 @@
 #define __CS35L41_H
 
 #include <linux/regmap.h>
+#include <linux/completion.h>
 #include <linux/firmware/cirrus/cs_dsp.h>
 
 #define CS35L41_FIRSTREG               0x00000000
 
 #define CS35L36_PUP_DONE_IRQ_UNMASK    0x5F
 #define CS35L36_PUP_DONE_IRQ_MASK      0xBF
+#define CS35L41_SYNC_EN_MASK           BIT(8)
 
 #define CS35L41_AMP_SHORT_ERR          0x80000000
 #define CS35L41_BST_SHORT_ERR          0x0100
 #define CS35L41_BST_DCM_UVP_ERR                0x80
 #define CS35L41_OTP_BOOT_DONE          0x02
 #define CS35L41_PLL_UNLOCK             0x10
+#define CS35L41_PLL_LOCK               BIT(1)
 #define CS35L41_OTP_BOOT_ERR           0x80000000
 
 #define CS35L41_AMP_SHORT_ERR_RLS      0x02
 #define CS35L41_INT1_MASK_DEFAULT      0x7FFCFE3F
 #define CS35L41_INT1_UNMASK_PUP                0xFEFFFFFF
 #define CS35L41_INT1_UNMASK_PDN                0xFF7FFFFF
+#define CS35L41_INT3_PLL_LOCK_SHIFT    1
+#define CS35L41_INT3_PLL_LOCK_MASK     BIT(CS35L41_INT3_PLL_LOCK_SHIFT)
 
 #define CS35L41_GPIO_DIR_MASK          0x80000000
 #define CS35L41_GPIO_DIR_SHIFT         31
 enum cs35l41_boost_type {
        CS35L41_INT_BOOST,
        CS35L41_EXT_BOOST,
+       CS35L41_SHD_BOOST_ACTV,
+       CS35L41_SHD_BOOST_PASS,
+
+       // Not present in Binding Documentation, so no system should use this value.
+       // This value is only used in CLSA0100 Laptop
        CS35L41_EXT_BOOST_NO_VSPK_SWITCH,
 };
 
@@ -891,6 +901,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap);
 int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
                       struct cs35l41_hw_cfg *hw_cfg);
 bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type);
-int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable);
+int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
+                         struct completion *pll_lock);
 
 #endif /* __CS35L41_H */
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
new file mode 100644 (file)
index 0000000..002042b
--- /dev/null
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Common definitions for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS35L56_H
+#define __CS35L56_H
+
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+
+#define CS35L56_DEVID                                  0x0000000
+#define CS35L56_REVID                                  0x0000004
+#define CS35L56_RELID                                  0x000000C
+#define CS35L56_OTPID                                  0x0000010
+#define CS35L56_SFT_RESET                              0x0000020
+#define CS35L56_GLOBAL_ENABLES                         0x0002014
+#define CS35L56_BLOCK_ENABLES                          0x0002018
+#define CS35L56_BLOCK_ENABLES2                         0x000201C
+#define CS35L56_REFCLK_INPUT                           0x0002C04
+#define CS35L56_GLOBAL_SAMPLE_RATE                     0x0002C0C
+#define CS35L56_ASP1_ENABLES1                          0x0004800
+#define CS35L56_ASP1_CONTROL1                          0x0004804
+#define CS35L56_ASP1_CONTROL2                          0x0004808
+#define CS35L56_ASP1_CONTROL3                          0x000480C
+#define CS35L56_ASP1_FRAME_CONTROL1                    0x0004810
+#define CS35L56_ASP1_FRAME_CONTROL5                    0x0004820
+#define CS35L56_ASP1_DATA_CONTROL1                     0x0004830
+#define CS35L56_ASP1_DATA_CONTROL5                     0x0004840
+#define CS35L56_DACPCM1_INPUT                          0x0004C00
+#define CS35L56_DACPCM2_INPUT                          0x0004C08
+#define CS35L56_ASP1TX1_INPUT                          0x0004C20
+#define CS35L56_ASP1TX2_INPUT                          0x0004C24
+#define CS35L56_ASP1TX3_INPUT                          0x0004C28
+#define CS35L56_ASP1TX4_INPUT                          0x0004C2C
+#define CS35L56_DSP1RX1_INPUT                          0x0004C40
+#define CS35L56_DSP1RX2_INPUT                          0x0004C44
+#define CS35L56_SWIRE_DP3_CH1_INPUT                    0x0004C70
+#define CS35L56_SWIRE_DP3_CH2_INPUT                    0x0004C74
+#define CS35L56_SWIRE_DP3_CH3_INPUT                    0x0004C78
+#define CS35L56_SWIRE_DP3_CH4_INPUT                    0x0004C7C
+#define CS35L56_IRQ1_CFG                               0x000E000
+#define CS35L56_IRQ1_STATUS                            0x000E004
+#define CS35L56_IRQ1_EINT_1                            0x000E010
+#define CS35L56_IRQ1_EINT_2                            0x000E014
+#define CS35L56_IRQ1_EINT_4                            0x000E01C
+#define CS35L56_IRQ1_EINT_8                            0x000E02C
+#define CS35L56_IRQ1_EINT_18                           0x000E054
+#define CS35L56_IRQ1_EINT_20                           0x000E05C
+#define CS35L56_IRQ1_MASK_1                            0x000E090
+#define CS35L56_IRQ1_MASK_2                            0x000E094
+#define CS35L56_IRQ1_MASK_4                            0x000E09C
+#define CS35L56_IRQ1_MASK_8                            0x000E0AC
+#define CS35L56_IRQ1_MASK_18                           0x000E0D4
+#define CS35L56_IRQ1_MASK_20                           0x000E0DC
+#define CS35L56_DSP_VIRTUAL1_MBOX_1                    0x0011020
+#define CS35L56_DSP_VIRTUAL1_MBOX_2                    0x0011024
+#define CS35L56_DSP_VIRTUAL1_MBOX_3                    0x0011028
+#define CS35L56_DSP_VIRTUAL1_MBOX_4                    0x001102C
+#define CS35L56_DSP_VIRTUAL1_MBOX_5                    0x0011030
+#define CS35L56_DSP_VIRTUAL1_MBOX_6                    0x0011034
+#define CS35L56_DSP_VIRTUAL1_MBOX_7                    0x0011038
+#define CS35L56_DSP_VIRTUAL1_MBOX_8                    0x001103C
+#define CS35L56_DSP_RESTRICT_STS1                      0x00190F0
+#define CS35L56_DSP1_XMEM_PACKED_0                     0x2000000
+#define CS35L56_DSP1_XMEM_PACKED_6143                  0x2005FFC
+#define CS35L56_DSP1_XMEM_UNPACKED32_0                 0x2400000
+#define CS35L56_DSP1_XMEM_UNPACKED32_4095              0x2403FFC
+#define CS35L56_DSP1_SYS_INFO_ID                       0x25E0000
+#define CS35L56_DSP1_SYS_INFO_END                      0x25E004C
+#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_0               0x25E2040
+#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1               0x25E2044
+#define CS35L56_DSP1_XMEM_UNPACKED24_0                 0x2800000
+#define CS35L56_DSP1_HALO_STATE_A1                     0x2801E58
+#define CS35L56_DSP1_HALO_STATE                                0x28021E0
+#define CS35L56_DSP1_PM_CUR_STATE_A1                   0x2804000
+#define CS35L56_DSP1_PM_CUR_STATE                      0x2804308
+#define CS35L56_DSP1_XMEM_UNPACKED24_8191              0x2807FFC
+#define CS35L56_DSP1_CORE_BASE                         0x2B80000
+#define CS35L56_DSP1_SCRATCH1                          0x2B805C0
+#define CS35L56_DSP1_SCRATCH2                          0x2B805C8
+#define CS35L56_DSP1_SCRATCH3                          0x2B805D0
+#define CS35L56_DSP1_SCRATCH4                          0x2B805D8
+#define CS35L56_DSP1_YMEM_PACKED_0                     0x2C00000
+#define CS35L56_DSP1_YMEM_PACKED_4604                  0x2C047F0
+#define CS35L56_DSP1_YMEM_UNPACKED32_0                 0x3000000
+#define CS35L56_DSP1_YMEM_UNPACKED32_3070              0x3002FF8
+#define CS35L56_DSP1_YMEM_UNPACKED24_0                 0x3400000
+#define CS35L56_MAIN_RENDER_USER_MUTE                  0x3400024
+#define CS35L56_MAIN_RENDER_USER_VOLUME                        0x340002C
+#define CS35L56_MAIN_POSTURE_NUMBER                    0x3400094
+#define CS35L56_PROTECTION_STATUS                      0x34000D8
+#define CS35L56_TRANSDUCER_ACTUAL_PS                   0x3400150
+#define CS35L56_DSP1_YMEM_UNPACKED24_6141              0x3405FF4
+#define CS35L56_DSP1_PMEM_0                            0x3800000
+#define CS35L56_DSP1_PMEM_5114                         0x3804FE8
+
+/* DEVID */
+#define CS35L56_DEVID_MASK                             0x00FFFFFF
+
+/* REVID */
+#define CS35L56_AREVID_MASK                            0x000000F0
+#define CS35L56_MTLREVID_MASK                          0x0000000F
+#define CS35L56_REVID_B0                               0x000000B0
+
+/* ASP_ENABLES1 */
+#define CS35L56_ASP_RX2_EN_SHIFT                       17
+#define CS35L56_ASP_RX1_EN_SHIFT                       16
+#define CS35L56_ASP_TX4_EN_SHIFT                       3
+#define CS35L56_ASP_TX3_EN_SHIFT                       2
+#define CS35L56_ASP_TX2_EN_SHIFT                       1
+#define CS35L56_ASP_TX1_EN_SHIFT                       0
+
+/* ASP_CONTROL1 */
+#define CS35L56_ASP_BCLK_FREQ_MASK                     0x0000003F
+#define CS35L56_ASP_BCLK_FREQ_SHIFT                    0
+
+/* ASP_CONTROL2 */
+#define CS35L56_ASP_RX_WIDTH_MASK                      0xFF000000
+#define CS35L56_ASP_RX_WIDTH_SHIFT                     24
+#define CS35L56_ASP_TX_WIDTH_MASK                      0x00FF0000
+#define CS35L56_ASP_TX_WIDTH_SHIFT                     16
+#define CS35L56_ASP_FMT_MASK                           0x00000700
+#define CS35L56_ASP_FMT_SHIFT                          8
+#define CS35L56_ASP_BCLK_INV_MASK                      0x00000040
+#define CS35L56_ASP_FSYNC_INV_MASK                     0x00000004
+
+/* ASP_CONTROL3 */
+#define CS35L56_ASP1_DOUT_HIZ_CTRL_MASK                        0x00000003
+
+/* ASP_DATA_CONTROL1 */
+#define CS35L56_ASP_TX_WL_MASK                         0x0000003F
+
+/* ASP_DATA_CONTROL5 */
+#define CS35L56_ASP_RX_WL_MASK                         0x0000003F
+
+/* ASPTXn_INPUT */
+#define CS35L56_ASP_TXn_SRC_MASK                       0x0000007F
+
+/* SWIRETX[1..7]_SRC SDWTXn INPUT */
+#define CS35L56_SWIRETXn_SRC_MASK                      0x0000007F
+
+/* IRQ1_STATUS */
+#define CS35L56_IRQ1_STS_MASK                          0x00000001
+
+/* IRQ1_EINT_1 */
+#define CS35L56_AMP_SHORT_ERR_EINT1_MASK               0x80000000
+
+/* IRQ1_EINT_2 */
+#define CS35L56_DSP_VIRTUAL2_MBOX_WR_EINT1_MASK                0x00200000
+
+/* IRQ1_EINT_4 */
+#define CS35L56_OTP_BOOT_DONE_MASK                     0x00000002
+
+/* IRQ1_EINT_8 */
+#define CS35L56_TEMP_ERR_EINT1_MASK                    0x80000000
+
+/* Mixer input sources */
+#define CS35L56_INPUT_SRC_NONE                         0x00
+#define CS35L56_INPUT_SRC_ASP1RX1                      0x08
+#define CS35L56_INPUT_SRC_ASP1RX2                      0x09
+#define CS35L56_INPUT_SRC_VMON                         0x18
+#define CS35L56_INPUT_SRC_IMON                         0x19
+#define CS35L56_INPUT_SRC_ERR_VOL                      0x20
+#define CS35L56_INPUT_SRC_CLASSH                       0x21
+#define CS35L56_INPUT_SRC_VDDBMON                      0x28
+#define CS35L56_INPUT_SRC_VBSTMON                      0x29
+#define CS35L56_INPUT_SRC_DSP1TX1                      0x32
+#define CS35L56_INPUT_SRC_DSP1TX2                      0x33
+#define CS35L56_INPUT_SRC_DSP1TX3                      0x34
+#define CS35L56_INPUT_SRC_DSP1TX4                      0x35
+#define CS35L56_INPUT_SRC_DSP1TX5                      0x36
+#define CS35L56_INPUT_SRC_DSP1TX6                      0x37
+#define CS35L56_INPUT_SRC_DSP1TX7                      0x38
+#define CS35L56_INPUT_SRC_DSP1TX8                      0x39
+#define CS35L56_INPUT_SRC_TEMPMON                      0x3A
+#define CS35L56_INPUT_SRC_INTERPOLATOR                 0x40
+#define CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1           0x44
+#define CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2           0x45
+#define CS35L56_INPUT_MASK                             0x7F
+
+#define CS35L56_NUM_INPUT_SRC                          21
+
+/* ASP formats */
+#define CS35L56_ASP_FMT_DSP_A                          0
+#define CS35L56_ASP_FMT_I2S                            2
+
+/* ASP HiZ modes */
+#define CS35L56_ASP_UNUSED_HIZ_OFF_HIZ                 3
+
+/* MAIN_RENDER_ACTUAL_PS */
+#define CS35L56_PS0                                    0
+#define CS35L56_PS3                                    3
+
+/* CS35L56_DSP_RESTRICT_STS1 */
+#define CS35L56_RESTRICTED_MASK                                0x7
+
+/* CS35L56_MAIN_RENDER_USER_MUTE */
+#define CS35L56_MAIN_RENDER_USER_MUTE_MASK             1
+
+/* CS35L56_MAIN_RENDER_USER_VOLUME */
+#define CS35L56_MAIN_RENDER_USER_VOLUME_MIN            -400
+#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX            400
+#define CS35L56_MAIN_RENDER_USER_VOLUME_MASK           0x0000FFC0
+#define CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT          6
+#define CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT                9
+
+/* CS35L56_MAIN_POSTURE_NUMBER */
+#define CS35L56_MAIN_POSTURE_MIN                       0
+#define CS35L56_MAIN_POSTURE_MAX                       255
+#define CS35L56_MAIN_POSTURE_MASK                      CS35L56_MAIN_POSTURE_MAX
+
+/* CS35L56_PROTECTION_STATUS */
+#define CS35L56_FIRMWARE_MISSING                       BIT(0)
+
+/* Software Values */
+#define CS35L56_HALO_STATE_SHUTDOWN                    1
+#define CS35L56_HALO_STATE_BOOT_DONE                   2
+
+#define CS35L56_MBOX_CMD_AUDIO_PLAY                    0x0B000001
+#define CS35L56_MBOX_CMD_AUDIO_PAUSE                   0x0B000002
+#define CS35L56_MBOX_CMD_HIBERNATE_NOW                 0x02000001
+#define CS35L56_MBOX_CMD_WAKEUP                                0x02000002
+#define CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE                0x02000003
+#define CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE          0x02000004
+#define CS35L56_MBOX_CMD_SHUTDOWN                      0x02000005
+#define CS35L56_MBOX_CMD_SYSTEM_RESET                  0x02000007
+
+#define CS35L56_MBOX_TIMEOUT_US                                5000
+#define CS35L56_MBOX_POLL_US                           250
+
+#define CS35L56_PS0_POLL_US                            500
+#define CS35L56_PS0_TIMEOUT_US                         50000
+#define CS35L56_PS3_POLL_US                            500
+#define CS35L56_PS3_TIMEOUT_US                         300000
+
+#define CS35L56_CONTROL_PORT_READY_US                  2200
+#define CS35L56_HALO_STATE_POLL_US                     1000
+#define CS35L56_HALO_STATE_TIMEOUT_US                  50000
+#define CS35L56_HIBERNATE_WAKE_POLL_US                 500
+#define CS35L56_HIBERNATE_WAKE_TIMEOUT_US              5000
+#define CS35L56_RESET_PULSE_MIN_US                     1100
+
+#define CS35L56_SDW1_PLAYBACK_PORT                     1
+#define CS35L56_SDW1_CAPTURE_PORT                      3
+
+#define CS35L56_NUM_BULK_SUPPLIES                      3
+#define CS35L56_NUM_DSP_REGIONS                                5
+
+extern struct regmap_config cs35l56_regmap_i2c;
+extern struct regmap_config cs35l56_regmap_spi;
+extern struct regmap_config cs35l56_regmap_sdw;
+
+extern const struct cs_dsp_region cs35l56_dsp1_regions[CS35L56_NUM_DSP_REGIONS];
+extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
+extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
+
+void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap);
+int cs35l56_get_bclk_freq_id(unsigned int freq);
+void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
+
+#endif /* ifndef __CS35L56_H */
index 39787fecc8d9af1f0e646e25eeb43a0de96cba42..8fe80dcee71b2e7dde3d860fd6d51c7c8200151d 100644 (file)
 /* ------------------- DEFINES -------------------- */
 
 #define EMUPAGESIZE     4096
-#define MAXREQVOICES    8
 #define MAXPAGES0       4096   /* 32 bit mode */
 #define MAXPAGES1       8192   /* 31 bit mode */
-#define RESERVED        0
-#define NUM_MIDI        16
 #define NUM_G           64              /* use all channels */
-#define NUM_FXSENDS     4
 #define NUM_EFX_PLAYBACK    16
 
 /* FIXME? - according to the OSS driver the EMU10K1 needs a 29 bit DMA mask */
@@ -39,7 +35,6 @@
 #define AUDIGY_DMA_MASK                0xffffffffUL    /* 32bit mode */
 
 #define TMEMSIZE        256*1024
-#define TMEMSIZEREG     4
 
 #define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL))
 
@@ -66,8 +61,8 @@
                                                /* the relevant bits and zero to the other bits */
 #define IPR_P16V               0x80000000      /* Bit set when the CA0151 P16V chip wishes
                                                   to interrupt */
-#define IPR_GPIOMSG            0x20000000      /* GPIO message interrupt (RE'd, still not sure 
-                                                  which INTE bits enable it)                   */
+#define IPR_WATERMARK_REACHED  0x40000000
+#define IPR_A_GPIO             0x20000000      /* GPIO input pin change                        */
 
 /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)                  */
 #define IPR_A_MIDITRANSBUFEMPTY2 0x10000000    /* MIDI UART transmit buffer empty              */
@@ -97,9 +92,9 @@
 #define IPR_CHANNELLOOP                0x00000040      /* Channel (half) loop interrupt(s) pending     */
 #define IPR_CHANNELNUMBERMASK  0x0000003f      /* When IPR_CHANNELLOOP is set, indicates the   */
                                                /* highest set channel in CLIPL, CLIPH, HLIPL,  */
-                                               /* or HLIPH.  When IP is written with CL set,   */
+                                               /* or HLIPH.  When IPR is written with CL set,  */
                                                /* the bit in H/CLIPL or H/CLIPH corresponding  */
-                                               /* to the CIN value written will be cleared.    */
+                                               /* to the CN value written will be cleared.     */
 
 #define INTE                   0x0c            /* Interrupt enable register                    */
 #define INTE_VIRTUALSB_MASK    0xc0000000      /* Virtual Soundblaster I/O port capture        */
                                                /* behavior and possibly random segfaults and   */
                                                /* lockups if enabled.                          */
 
+#define INTE_A_GPIOENABLE      0x00040000      /* Enable GPIO input change interrupts          */
+
 /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)                  */
 #define INTE_A_MIDITXENABLE2   0x00020000      /* Enable MIDI transmit-buffer-empty interrupts */
 #define INTE_A_MIDIRXENABLE2   0x00010000      /* Enable MIDI receive-buffer-empty interrupts  */
 
+#define INTE_A_SPDIF_BUFFULL_ENABLE    0x00008000
+#define INTE_A_SPDIF_HALFBUFFULL_ENABLE        0x00004000
 
 #define INTE_SAMPLERATETRACKER 0x00002000      /* Enable sample rate tracker interrupts        */
                                                /* NOTE: This bit must always be enabled        */
 #define WC                     0x10            /* Wall Clock register                          */
 #define WC_SAMPLECOUNTER_MASK  0x03FFFFC0      /* Sample periods elapsed since reset           */
 #define WC_SAMPLECOUNTER       0x14060010
-#define WC_CURRENTCHANNEL      0x0000003F      /* Channel [0..63] currently being serviced     */
+#define WC_CURRENTCHANNEL_MASK 0x0000003F      /* Channel [0..63] currently being serviced     */
                                                /* NOTE: Each channel takes 1/64th of a sample  */
                                                /* period to be serviced.                       */
+#define WC_CURRENTCHANNEL      0x06000010
 
 #define HCFG                   0x14            /* Hardware config register                     */
                                                /* NOTE: There is no reason to use the legacy   */
 #define HCFG_CODECFORMAT_MASK  0x00030000      /* CODEC format                                 */
 
 /* Specific to Alice2, CA0102 */
+
 #define HCFG_CODECFORMAT_AC97_1        0x00000000      /* AC97 CODEC format -- Ver 1.03                */
 #define HCFG_CODECFORMAT_AC97_2        0x00010000      /* AC97 CODEC format -- Ver 2.1                 */
 #define HCFG_AUTOMUTE_ASYNC    0x00008000      /* When set, the async sample rate convertors   */
                                                /* I2S format input                             */
 /* Rest of HCFG 0x0000000f same as below. LOCKSOUNDCACHE etc.  */
 
-
-
 /* Older chips */
+
 #define HCFG_CODECFORMAT_AC97  0x00000000      /* AC97 CODEC format -- Primary Output          */
 #define HCFG_CODECFORMAT_I2S   0x00010000      /* I2S CODEC format -- Secondary (Rear) Output  */
 #define HCFG_GPINPUT0          0x00004000      /* External pin112                              */
                                                /* Should be set to 1 when the EMU10K1 is       */
                                                /* completely initialized.                      */
 
-//For Audigy, MPU port move to 0x70-0x74 ptr register
+// On Audigy, the MPU port moved to the 0x70-0x74 ptr registers
 
 #define MUDATA                 0x18            /* MPU401 data register (8 bits)                */
 
 #define MUSTAT_IRDYN           0x80            /* 0 = MIDI data or command ACK                 */
 #define MUSTAT_ORDYN           0x40            /* 0 = MUDATA can accept a command or data      */
 
-#define A_IOCFG                        0x18            /* GPIO on Audigy card (16bits)                 */
-#define A_GPINPUT_MASK         0xff00
+#define A_GPIO                 0x18            /* GPIO on Audigy card (16bits)                 */
+#define A_GPINPUT_MASK         0xff00          /* Alice/2 has 8 input pins                     */
+#define A3_GPINPUT_MASK                0x3f00          /* ... while Tina/2 has only 6                  */
 #define A_GPOUTPUT_MASK                0x00ff
 
+// The GPIO port is used for I/O config on Sound Blasters;
+// card-specific info can be found in the emu_chip_details table.
+// On E-MU cards the port is used as the interface to the FPGA.
+
 // Audigy output/GPIO stuff taken from the kX drivers
+#define A_IOCFG                        A_GPIO
 #define A_IOCFG_GPOUT0         0x0044          /* analog/digital                               */
 #define A_IOCFG_DISABLE_ANALOG 0x0040          /* = 'enable' for Audigy2 (chiprev=4)           */
 #define A_IOCFG_ENABLE_DIGITAL 0x0004
 #define A_IOCFG_REAR_JACK       0x8000
 #define A_IOCFG_PHONES_JACK     0x0100          /* LiveDrive                                   */
 
-/* outputs:
- *     for audigy2 platinum:   0xa00
- *     for a2 platinum ex:     0x1c00
- *     for a1 platinum:        0x0
- */
-
 #define TIMER                  0x1a            /* Timer terminal count register                */
                                                /* NOTE: After the rate is changed, a maximum   */
                                                /* of 1024 sample periods should be allowed     */
                                                /* before the new rate is guaranteed accurate.  */
-#define TIMER_RATE_MASK                0x000003ff      /* Timer interrupt rate in sample periods       */
+#define TIMER_RATE_MASK                0x03ff          /* Timer interrupt rate in sample periods       */
                                                /* 0 == 1024 periods, [1..4] are not useful     */
-#define TIMER_RATE             0x0a00001a
 
 #define AC97DATA               0x1c            /* AC97 register set data register (16 bit)     */
 
                                                /* 0x00000000 2-channel output. */
                                                /* 0x00000200 8-channel output. */
                                                /* 0x00000004 pauses stream/irq fail. */
-                                               /* Rest of bits no nothing to sound output */
+                                               /* Rest of bits do nothing to sound output */
                                                /* bit 0: Enable P16V audio.
                                                 * bit 1: Lock P16V record memory cache.
                                                 * bit 2: Lock P16V playback memory cache.
                                                 */
 #define IPR3                   0x38            /* Cdif interrupt pending register              */
 #define INTE3                  0x3c            /* Cdif interrupt enable register.      */
+
 /************************************************************************************************/
 /* PCI function 1 registers, address = <val> + PCIBASE1                                                */
 /************************************************************************************************/
 #define JOYSTICK_BUTTONS       0x0f            /* Joystick button data                         */
 #define JOYSTICK_COMPARATOR    0xf0            /* Joystick comparator data                     */
 
-
 /********************************************************************************************************/
 /* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers                    */
 /********************************************************************************************************/
 
+// No official documentation was released for EMU10K1, but some info
+// about playback can be extrapolated from the EMU8K documents:
+// "AWE32/EMU8000 Programmer’s Guide" (emu8kpgm.pdf) - registers
+// "AWE32 Developer's Information Pack" (adip301.pdf) - high-level view
+
+// The short version:
+// - The engine has 64 playback channels, also called voices. The channels
+//   operate independently, except when paired for stereo (see below).
+// - PCM samples are fetched into the cache; see description of CD0 below.
+// - Samples are consumed at the rate CPF_CURRENTPITCH.
+// - 8-bit samples are transformed upon use: cooked = (raw ^ 0x80) << 8
+// - 8 samples are read at CCR_READADDRESS:CPF_FRACADDRESS and interpolated
+//   according to CCCA_INTERPROM_*. With CCCA_INTERPROM_0 selected and a zero
+//   CPF_FRACADDRESS, this results in CCR_READADDRESS[3] being used verbatim.
+// - The value is multiplied by CVCF_CURRENTVOL.
+// - The value goes through a filter with cutoff CVCF_CURRENTFILTER;
+//   delay stages Z1 and Z2.
+// - The value is added by so-called `sends` to 4 (EMU10K1) / 8 (EMU10K2)
+//   of the 16 (EMU10K1) / 64 (EMU10K2) FX bus accumulators via FXRT*,
+//   multiplied by a per-send amount (*_FXSENDAMOUNT_*).
+//   The scaling of the send amounts is exponential-ish.
+// - The DSP has a go at FXBUS* and outputs the values to EXTOUT* or EMU32OUT*.
+// - The pitch, volume, and filter cutoff can be modulated by two envelope
+//   engines and two low frequency oscillators.
+// - To avoid abrupt changes to the parameters (which may cause audible
+//   distortion), the modulation engine sets the target registers, towards
+//   which the current registers "swerve" gradually.
+
 #define CPF                    0x00            /* Current pitch and fraction register                  */
 #define CPF_CURRENTPITCH_MASK  0xffff0000      /* Current pitch (linear, 0x4000 == unity pitch shift)  */
 #define CPF_CURRENTPITCH       0x10100000
 #define PSST_LOOPSTARTADDR_MASK        0x00ffffff      /* Loop start address of the specified channel          */
 #define PSST_LOOPSTARTADDR     0x18000006
 
-#define DSL                    0x07            /* Send D amount and loop start address register        */
+#define DSL                    0x07            /* Send D amount and loop end address register  */
 #define DSL_FXSENDAMOUNT_D_MASK        0xff000000      /* Linear level of channel output sent to FX send bus D */
 
 #define DSL_FXSENDAMOUNT_D     0x08180007
 #define DSL_LOOPENDADDR                0x18000007
 
 #define CCCA                   0x08            /* Filter Q, interp. ROM, byte size, cur. addr register */
-#define CCCA_RESONANCE         0xf0000000      /* Lowpass filter resonance (Q) height                  */
-#define CCCA_INTERPROMMASK     0x0e000000      /* Selects passband of interpolation ROM                */
+#define CCCA_RESONANCE_MASK    0xf0000000      /* Lowpass filter resonance (Q) height                  */
+#define CCCA_RESONANCE         0x041c0008
+#define CCCA_INTERPROM_MASK    0x0e000000      /* Selects passband of interpolation ROM                */
                                                /* 1 == full band, 7 == lowpass                         */
                                                /* ROM 0 is used when pitch shifting downward or less   */
                                                /* then 3 semitones upward.  Increasingly higher ROM    */
 #define CCCA_INTERPROM_6       0x0c000000      /* Select interpolation ROM 6                           */
 #define CCCA_INTERPROM_7       0x0e000000      /* Select interpolation ROM 7                           */
 #define CCCA_8BITSELECT                0x01000000      /* 1 = Sound memory for this channel uses 8-bit samples */
+                                               /* 8-bit samples are unsigned, 16-bit ones signed       */
 #define CCCA_CURRADDR_MASK     0x00ffffff      /* Current address of the selected channel              */
 #define CCCA_CURRADDR          0x18000008
 
 #define CCR                    0x09            /* Cache control register                               */
 #define CCR_CACHEINVALIDSIZE   0x07190009
-#define CCR_CACHEINVALIDSIZE_MASK      0xfe000000      /* Number of invalid samples cache for this channel     */
+#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000   /* Number of invalid samples before the read address    */
 #define CCR_CACHELOOPFLAG      0x01000000      /* 1 = Cache has a loop service pending                 */
 #define CCR_INTERLEAVEDSAMPLES 0x00800000      /* 1 = A cache service will fetch interleaved samples   */
+                                               /* Auto-set from CPF_STEREO_MASK                        */
 #define CCR_WORDSIZEDSAMPLES   0x00400000      /* 1 = A cache service will fetch word sized samples    */
+                                               /* Auto-set from CCCA_8BITSELECT                        */
 #define CCR_READADDRESS                0x06100009
-#define CCR_READADDRESS_MASK   0x003f0000      /* Location of cache just beyond current cache service  */
+#define CCR_READADDRESS_MASK   0x003f0000      /* Next cached sample to play                           */
 #define CCR_LOOPINVALSIZE      0x0000fe00      /* Number of invalid samples in cache prior to loop     */
                                                /* NOTE: This is valid only if CACHELOOPFLAG is set     */
 #define CCR_LOOPFLAG           0x00000100      /* Set for a single sample period when a loop occurs    */
-#define CCR_CACHELOOPADDRHI    0x000000ff      /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set  */
+#define CCR_CACHELOOPADDRHI    0x000000ff      /* CLP_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set  */
 
 #define CLP                    0x0a            /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
                                                /* NOTE: This register is normally not used             */
-#define CLP_CACHELOOPADDR      0x0000ffff      /* Cache loop address (DSL_LOOPSTARTADDR [0..15])       */
+#define CLP_CACHELOOPADDR      0x0000ffff      /* Cache loop address low word                          */
 
 #define FXRT                   0x0b            /* Effects send routing register                        */
                                                /* NOTE: It is illegal to assign the same routing to    */
 #define FXRT_CHANNELC          0x0f000000      /* Effects send bus number for channel's effects send C */
 #define FXRT_CHANNELD          0xf0000000      /* Effects send bus number for channel's effects send D */
 
-#define A_HR                   0x0b    /* High Resolution. 24bit playback from host to DSP. */
 #define MAPA                   0x0c            /* Cache map A                                          */
-
 #define MAPB                   0x0d            /* Cache map B                                          */
 
 #define MAP_PTE_MASK0          0xfffff000      /* The 20 MSBs of the PTE indexed by the PTI            */
 #define MAP_PTE_MASK1          0xffffe000      /* The 19 MSBs of the PTE indexed by the PTI            */
 #define MAP_PTI_MASK1          0x00001fff      /* The 13 bit index to one of the 8192 PTE dwords       */
 
-/* 0x0e, 0x0f: Not used */
+/* 0x0e, 0x0f: Internal state, at least on Audigy */
 
 #define ENVVOL                 0x10            /* Volume envelope register                             */
 #define ENVVOL_MASK            0x0000ffff      /* Current value of volume envelope state variable      */  
                                                /* 0x8000-n == 666*n usec delay                         */
 
 #define ATKHLDV                0x11            /* Volume envelope hold and attack register             */
-#define ATKHLDV_PHASE0         0x00008000      /* 0 = Begin attack phase                               */
+#define ATKHLDV_PHASE0_MASK    0x00008000      /* 0 = Begin attack phase                               */
 #define ATKHLDV_HOLDTIME_MASK  0x00007f00      /* Envelope hold time (127-n == n*88.2msec)             */
 #define ATKHLDV_ATTACKTIME_MASK        0x0000007f      /* Envelope attack time, log encoded                    */
                                                /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec       */
 
 #define DCYSUSV                0x12            /* Volume envelope sustain and decay register           */
-#define DCYSUSV_PHASE1_MASK    0x00008000      /* 0 = Begin attack phase, 1 = begin release phase      */
+#define DCYSUSV_PHASE1_MASK    0x00008000      /* 0 = Begin decay phase, 1 = begin release phase       */
 #define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00   /* 127 = full, 0 = off, 0.75dB increments               */
-#define DCYSUSV_CHANNELENABLE_MASK 0x00000080  /* 1 = Inhibit envelope engine from writing values in   */
+#define DCYSUSV_CHANNELENABLE_MASK 0x00000080  /* 0 = Inhibit envelope engine from writing values in   */
                                                /* this channel and from writing to pitch, filter and   */
                                                /* volume targets.                                      */
 #define DCYSUSV_DECAYTIME_MASK 0x0000007f      /* Volume envelope decay time, log encoded              */
                                                /* 0x8000-n == 666*n usec delay                         */
 
 #define ATKHLDM                        0x15            /* Modulation envelope hold and attack register         */
-#define ATKHLDM_PHASE0         0x00008000      /* 0 = Begin attack phase                               */
+#define ATKHLDM_PHASE0_MASK    0x00008000      /* 0 = Begin attack phase                               */
 #define ATKHLDM_HOLDTIME       0x00007f00      /* Envelope hold time (127-n == n*42msec)               */
 #define ATKHLDM_ATTACKTIME     0x0000007f      /* Envelope attack time, log encoded                    */
                                                /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec         */
 
 #define DCYSUSM                        0x16            /* Modulation envelope decay and sustain register       */
-#define DCYSUSM_PHASE1_MASK    0x00008000      /* 0 = Begin attack phase, 1 = begin release phase      */
+#define DCYSUSM_PHASE1_MASK    0x00008000      /* 0 = Begin decay phase, 1 = begin release phase       */
 #define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00   /* 127 = full, 0 = off, 0.75dB increments               */
 #define DCYSUSM_DECAYTIME_MASK 0x0000007f      /* Envelope decay time, log encoded                     */
                                                /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec            */
 #define IFATN_ATTENUATION_MASK 0x000000ff      /* Initial attenuation in 0.375dB steps                 */
 #define IFATN_ATTENUATION      0x08000019
 
-
 #define PEFE                   0x1a            /* Pitch envelope and filter envelope amount register   */
 #define PEFE_PITCHAMOUNT_MASK  0x0000ff00      /* Pitch envlope amount                                 */
                                                /* Signed 2's complement, +/- one octave peak extremes  */
 #define PEFE_FILTERAMOUNT_MASK 0x000000ff      /* Filter envlope amount                                */
                                                /* Signed 2's complement, +/- six octaves peak extremes */
 #define PEFE_FILTERAMOUNT      0x0800001a
+
 #define FMMOD                  0x1b            /* Vibrato/filter modulation from LFO register          */
 #define FMMOD_MODVIBRATO       0x0000ff00      /* Vibrato LFO modulation depth                         */
                                                /* Signed 2's complement, +/- one octave extremes       */
 #define FMMOD_MOFILTER         0x000000ff      /* Filter LFO modulation depth                          */
                                                /* Signed 2's complement, +/- three octave extremes     */
 
-
 #define TREMFRQ                0x1c            /* Tremolo amount and modulation LFO frequency register */
 #define TREMFRQ_DEPTH          0x0000ff00      /* Tremolo depth                                        */
                                                /* Signed 2's complement, with +/- 12dB extremes        */
-
 #define TREMFRQ_FREQUENCY      0x000000ff      /* Tremolo LFO frequency                                */
                                                /* ??Hz steps, maximum of ?? Hz.                        */
+
 #define FM2FRQ2                0x1d            /* Vibrato amount and vibrato LFO frequency register    */
 #define FM2FRQ2_DEPTH          0x0000ff00      /* Vibrato LFO vibrato depth                            */
                                                /* Signed 2's complement, +/- one octave extremes       */
                                                /* is 16bit, 48KHz only. All 32 channels can be enabled */
                                                /* simultaneously.                                      */
 
-#define FXWC_DEFAULTROUTE_C     (1<<0)         /* left emu out? */
-#define FXWC_DEFAULTROUTE_B     (1<<1)         /* right emu out? */
-#define FXWC_DEFAULTROUTE_A     (1<<12)
-#define FXWC_DEFAULTROUTE_D     (1<<13)
-#define FXWC_ADCLEFT            (1<<18)
-#define FXWC_CDROMSPDIFLEFT     (1<<18)
-#define FXWC_ADCRIGHT           (1<<19)
-#define FXWC_CDROMSPDIFRIGHT    (1<<19)
-#define FXWC_MIC                (1<<20)
-#define FXWC_ZOOMLEFT           (1<<20)
-#define FXWC_ZOOMRIGHT          (1<<21)
-#define FXWC_SPDIFLEFT          (1<<22)                /* 0x00400000 */
-#define FXWC_SPDIFRIGHT         (1<<23)                /* 0x00800000 */
-
 #define A_TBLSZ                        0x43    /* Effects Tank Internal Table Size. Only low byte or register used */
 
 #define TCBS                   0x44            /* Tank cache buffer size register                      */
 #define FXBA                   0x47            /* FX Buffer Address */
 #define FXBA_MASK              0xfffff000      /* 20 bit base address                                  */
 
-#define A_HWM                  0x48    /* High PCI Water Mark - word access, defaults to 3f */
+#define A_HWM                  0x48            /* High PCI Water Mark - word access, defaults to 3f */
 
 #define MICBS                  0x49            /* Microphone buffer size register                      */
 
 
 #define FXBS                   0x4b            /* FX buffer size register                              */
 
-/* register: 0x4c..4f: ffff-ffff current amounts, per-channel */
-
-/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
+/* The following mask values define the size of the ADC, MIC and FX buffers in bytes */
 #define ADCBS_BUFSIZE_NONE     0x00000000
 #define ADCBS_BUFSIZE_384      0x00000001
 #define ADCBS_BUFSIZE_448      0x00000002
 #define ADCBS_BUFSIZE_57344    0x0000001e
 #define ADCBS_BUFSIZE_65536    0x0000001f
 
-/* Current Send B, A Amounts */
-#define A_CSBA                 0x4c
-
-/* Current Send D, C Amounts */
-#define A_CSDC                 0x4d
-
-/* Current Send F, E Amounts */
-#define A_CSFE                 0x4e
-
-/* Current Send H, G Amounts */
-#define A_CSHG                 0x4f
+#define A_CSBA                 0x4c            /* FX send B & A current amounts                        */
+#define A_CSDC                 0x4d            /* FX send D & C current amounts                        */
+#define A_CSFE                 0x4e            /* FX send F & E current amounts                        */
+#define A_CSHG                 0x4f            /* FX send H & G current amounts                        */
 
+// NOTE: 0x50,51,52: 64-bit (split over voices 0 & 1)
+#define CDCS                   0x50            /* CD-ROM digital channel status register               */
 
-#define CDCS                   0x50            /* CD-ROM digital channel status register       */
+#define GPSCS                  0x51            /* General Purpose SPDIF channel status register        */
 
-#define GPSCS                  0x51            /* General Purpose SPDIF channel status register*/
+// Corresponding EMU10K1_DBG_* constants are in the public header
+#define DBG                    0x52
 
-#define DBG                    0x52            /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+#define A_SPSC                 0x52            /* S/PDIF Input C Channel Status                        */
 
-/* S/PDIF Input C Channel Status */
-#define A_SPSC                 0x52
+#define REG53                  0x53            /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP     */
 
-#define REG53                  0x53            /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+// Corresponding A_DBG_* constants are in the public header
+#define A_DBG                  0x53
 
-#define A_DBG                   0x53
-#define A_DBG_SINGLE_STEP       0x00020000     /* Set to zero to start dsp */
-#define A_DBG_ZC                0x40000000     /* zero tram counter */
-#define A_DBG_STEP_ADDR                 0x000003ff
-#define A_DBG_SATURATION_OCCURED 0x20000000
-#define A_DBG_SATURATION_ADDR   0x0ffc0000
-
-// NOTE: 0x54,55,56: 64-bit
+// NOTE: 0x54,55,56: 64-bit (split over voices 0 & 1)
 #define SPCS0                  0x54            /* SPDIF output Channel Status 0 register       */
 
 #define SPCS1                  0x55            /* SPDIF output Channel Status 1 register       */
 
 /* 0x57: Not used */
 
-/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status                      */
+/* The 32-bit CLIx and SOLEx registers all have one bit per channel control/status             */
 #define CLIEL                  0x58            /* Channel loop interrupt enable low register   */
-
 #define CLIEH                  0x59            /* Channel loop interrupt enable high register  */
 
 #define CLIPL                  0x5a            /* Channel loop interrupt pending low register  */
-
 #define CLIPH                  0x5b            /* Channel loop interrupt pending high register */
 
 #define SOLEL                  0x5c            /* Stop on loop enable low register             */
-
 #define SOLEH                  0x5d            /* Stop on loop enable high register            */
 
 #define SPBYPASS               0x5e            /* SPDIF BYPASS mode register                   */
 #define SPBYPASS_FORMAT                0x00000f00      /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit    */
 
 #define AC97SLOT               0x5f            /* additional AC97 slots enable bits            */
-#define AC97SLOT_REAR_RIGHT    0x01            /* Rear left */
-#define AC97SLOT_REAR_LEFT     0x02            /* Rear right */
-#define AC97SLOT_CNTR          0x10            /* Center enable */
-#define AC97SLOT_LFE           0x20            /* LFE enable */
+#define AC97SLOT_REAR_RIGHT    0x01            /* Rear left                                    */
+#define AC97SLOT_REAR_LEFT     0x02            /* Rear right                                   */
+#define AC97SLOT_CNTR          0x10            /* Center enable                                */
+#define AC97SLOT_LFE           0x20            /* LFE enable                                   */
 
-/* PCB Revision */
-#define A_PCB                  0x5f
+#define A_PCB                  0x5f            /* PCB Revision                                 */
 
 // NOTE: 0x60,61,62: 64-bit
 #define CDSRCS                 0x60            /* CD-ROM Sample Rate Converter status register */
 #define FXIDX_MASK             0x0000ffff      /* 16-bit value                                 */
 #define FXIDX_IDX              0x10000065
 
-/* The 32-bit HLIx and HLIPx registers all have one bit per channel control/status                     */
+/* The 32-bit HLIEx and HLIPx registers all have one bit per channel control/status                    */
 #define HLIEL                  0x66            /* Channel half loop interrupt enable low register      */
-
 #define HLIEH                  0x67            /* Channel half loop interrupt enable high register     */
 
 #define HLIPL                  0x68            /* Channel half loop interrupt pending low register     */
-
 #define HLIPH                  0x69            /* Channel half loop interrupt pending high register    */
 
-/* S/PDIF Host Record Index (bypasses SRC) */
-#define A_SPRI                 0x6a
-/* S/PDIF Host Record Address */
-#define A_SPRA                 0x6b
-/* S/PDIF Host Record Control */
-#define A_SPRC                 0x6c
-/* Delayed Interrupt Counter & Enable */
-#define A_DICE                 0x6d
-/* Tank Table Base */
-#define A_TTB                  0x6e
-/* Tank Delay Offset */
-#define A_TDOF                 0x6f
+#define A_SPRI                 0x6a            /* S/PDIF Host Record Index (bypasses SRC)      */
+#define A_SPRA                 0x6b            /* S/PDIF Host Record Address                   */
+#define A_SPRC                 0x6c            /* S/PDIF Host Record Control                   */
+
+#define A_DICE                 0x6d            /* Delayed Interrupt Counter & Enable           */
+
+#define A_TTB                  0x6e            /* Tank Table Base                              */
+#define A_TDOF                 0x6f            /* Tank Delay Offset                            */
 
 /* This is the MPU port on the card (via the game port)                                                */
 #define A_MUDATA1              0x70
 #define A_MUSTAT2              A_MUCMD2        
 
 /* The next two are the Audigy equivalent of FXWC                                              */
-/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously)            */
+/* the Audigy can record any output (16bit, 48kHz, up to 64 channels simultaneously)           */
 /* Each bit selects a channel for recording */
 #define A_FXWC1                        0x74            /* Selects 0x7f-0x60 for FX recording           */
 #define A_FXWC2                        0x75            /* Selects 0x9f-0x80 for FX recording           */
 
-/* Extended Hardware Control */
-#define A_SPDIF_SAMPLERATE     0x76            /* Set the sample rate of SPDIF output          */
-#define A_SAMPLE_RATE          0x76            /* Various sample rate settings. */
-#define A_SAMPLE_RATE_NOT_USED  0x0ffc111e     /* Bits that are not used and cannot be set.    */
-#define A_SAMPLE_RATE_UNKNOWN  0xf0030001      /* Bits that can be set, but have unknown use.  */
+#define A_EHC                  0x76            /* Extended Hardware Control */
+
+#define A_SPDIF_SAMPLERATE     A_EHC           /* Set the sample rate of SPDIF output          */
 #define A_SPDIF_RATE_MASK      0x000000e0      /* Any other values for rates, just use 48000   */
-#define A_SPDIF_48000          0x00000000
+#define A_SPDIF_48000          0x00000000      /* kX calls this BYPASS                         */
 #define A_SPDIF_192000         0x00000020
 #define A_SPDIF_96000          0x00000040
 #define A_SPDIF_44100          0x00000080
+#define A_SPDIF_MUTED          0x000000c0
 
 #define A_I2S_CAPTURE_RATE_MASK        0x00000e00      /* This sets the capture PCM rate, but it is    */
-#define A_I2S_CAPTURE_48000    0x00000000      /* unclear if this sets the ADC rate as well.   */
-#define A_I2S_CAPTURE_192000   0x00000200
-#define A_I2S_CAPTURE_96000    0x00000400
-#define A_I2S_CAPTURE_44100    0x00000800
-
-#define A_PCM_RATE_MASK                0x0000e000      /* This sets the playback PCM rate on the P16V  */
-#define A_PCM_48000            0x00000000
-#define A_PCM_192000           0x00002000
-#define A_PCM_96000            0x00004000
-#define A_PCM_44100            0x00008000
-
-/* I2S0 Sample Rate Tracker Status */
-#define A_SRT3                 0x77
-
-/* I2S1 Sample Rate Tracker Status */
-#define A_SRT4                 0x78
-
-/* I2S2 Sample Rate Tracker Status */
-#define A_SRT5                 0x79
+#define A_I2S_CAPTURE_RATE     0x03090076      /* unclear if this sets the ADC rate as well.   */
+#define A_I2S_CAPTURE_48000    0x0
+#define A_I2S_CAPTURE_192000   0x1
+#define A_I2S_CAPTURE_96000    0x2
+#define A_I2S_CAPTURE_44100    0x4
+
+#define A_EHC_SRC48_MASK       0x0000e000      /* This sets the playback PCM rate on the P16V  */
+#define A_EHC_SRC48_BYPASS     0x00000000
+#define A_EHC_SRC48_192                0x00002000
+#define A_EHC_SRC48_96         0x00004000
+#define A_EHC_SRC48_44         0x00008000
+#define A_EHC_SRC48_MUTED      0x0000c000
+
+#define A_EHC_P17V_TVM         0x00000001      /* Tank virtual memory mode                     */
+#define A_EHC_P17V_SEL0_MASK   0x00030000      /* Aka A_EHC_P16V_PB_RATE; 00: 48, 01: 44.1, 10: 96, 11: 192 */
+#define A_EHC_P17V_SEL1_MASK   0x000c0000
+#define A_EHC_P17V_SEL2_MASK   0x00300000
+#define A_EHC_P17V_SEL3_MASK   0x00c00000
+
+#define A_EHC_ASYNC_BYPASS     0x80000000
+
+#define A_SRT3                 0x77            /* I2S0 Sample Rate Tracker Status              */
+#define A_SRT4                 0x78            /* I2S1 Sample Rate Tracker Status              */
+#define A_SRT5                 0x79            /* I2S2 Sample Rate Tracker Status              */
 /* - default to 0x01080000 on my audigy 2 ZS --rlrevell        */
 
-/* Tank Table DMA Address */
-#define A_TTDA                 0x7a
-/* Tank Table DMA Data */
-#define A_TTDD                 0x7b
+#define A_SRT_ESTSAMPLERATE    0x001fffff
+#define A_SRT_RATELOCKED       0x01000000
+
+#define A_TTDA                 0x7a            /* Tank Table DMA Address                       */
+#define A_TTDD                 0x7b            /* Tank Table DMA Data                          */
 
 #define A_FXRT2                        0x7c
 #define A_FXRT_CHANNELE                0x0000003f      /* Effects send bus number for channel's effects send E */
 #define A_FXSENDAMOUNT_F_MASK  0x00FF0000
 #define A_FXSENDAMOUNT_G_MASK  0x0000FF00
 #define A_FXSENDAMOUNT_H_MASK  0x000000FF
+
 /* 0x7c, 0x7e "high bit is used for filtering" */
  
 /* The send amounts for this one are the same as used with the emu10k1 */
 #define A_FXRT_CHANNELD                0x3f000000
 
 /* 0x7f: Not used */
-/* Each FX general purpose register is 32 bits in length, all bits are used                    */
-#define FXGPREGBASE            0x100           /* FX general purpose registers base            */
-#define A_FXGPREGBASE          0x400           /* Audigy GPRs, 0x400 to 0x5ff                  */
-
-#define A_TANKMEMCTLREGBASE    0x100           /* Tank memory control registers base - only for Audigy */
-#define A_TANKMEMCTLREG_MASK   0x1f            /* only 5 bits used - only for Audigy */
-
-/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */
-/* decompressed back to 20 bits on a read.  There are a total of 160 locations, the last 32    */
-/* locations are for external TRAM.                                                            */
-#define TANKMEMDATAREGBASE     0x200           /* Tank memory data registers base              */
-#define TANKMEMDATAREG_MASK    0x000fffff      /* 20 bit tank audio data field                 */
-
-/* Combined address field and memory opcode or flag field.  160 locations, last 32 are external        */
-#define TANKMEMADDRREGBASE     0x300           /* Tank memory address registers base           */
-#define TANKMEMADDRREG_ADDR_MASK 0x000fffff    /* 20 bit tank address field                    */
-#define TANKMEMADDRREG_CLEAR   0x00800000      /* Clear tank memory                            */
-#define TANKMEMADDRREG_ALIGN   0x00400000      /* Align read or write relative to tank access  */
-#define TANKMEMADDRREG_WRITE   0x00200000      /* Write to tank memory                         */
-#define TANKMEMADDRREG_READ    0x00100000      /* Read from tank memory                        */
 
-#define MICROCODEBASE          0x400           /* Microcode data base address                  */
+/* The public header defines the GPR and TRAM base addresses that
+ * are valid for _both_ CPU and DSP addressing. */
 
 /* Each DSP microcode instruction is mapped into 2 doublewords                                         */
 /* NOTE: When writing, always write the LO doubleword first.  Reads can be in either order.    */
-#define LOWORD_OPX_MASK                0x000ffc00      /* Instruction operand X                        */
-#define LOWORD_OPY_MASK                0x000003ff      /* Instruction operand Y                        */
-#define HIWORD_OPCODE_MASK     0x00f00000      /* Instruction opcode                           */
-#define HIWORD_RESULT_MASK     0x000ffc00      /* Instruction result                           */
-#define HIWORD_OPA_MASK                0x000003ff      /* Instruction operand A                        */
+#define MICROCODEBASE          0x400           /* Microcode data base address                  */
+#define A_MICROCODEBASE                0x600
 
 
-/* Audigy Soundcard have a different instruction format */
-#define A_MICROCODEBASE                0x600
-#define A_LOWORD_OPY_MASK      0x000007ff
-#define A_LOWORD_OPX_MASK      0x007ff000
-#define A_HIWORD_OPCODE_MASK   0x0f000000
-#define A_HIWORD_RESULT_MASK   0x007ff000
-#define A_HIWORD_OPA_MASK      0x000007ff
+/************************************************************************************************/
+/* E-MU Digital Audio System overview                                                          */
+/************************************************************************************************/
+
+// - These cards use a regular PCI-attached Audigy chip (Alice2/Tina/Tina2);
+//   the PCIe variants simply put the Audigy chip behind a PCI bridge.
+// - All physical PCM I/O is routed through an additional FPGA; the regular
+//   EXTIN/EXTOUT ports are unconnected.
+// - The FPGA has a signal routing matrix, to connect each destination (output
+//   socket or capture channel) to a source (input socket or playback channel).
+// - The FPGA is controlled via Audigy's GPIO port, while sample data is
+//   transmitted via proprietary EMU32 serial links. On first-generation
+//   E-MU 1010 cards, Audigy's I2S inputs are also used for sample data.
+// - The Audio/Micro Dock is attached to Hana via EDI, a "network" link.
+// - The Audigy chip operates in slave mode; the clock is supplied by the FPGA.
+//   Gen1 E-MU 1010 cards have two crystals (for 44.1 kHz and 48 kHz multiples),
+//   while the later cards use a single crystal and a PLL chip.
+// - The whole card is switched to 2x/4x mode to achieve 88.2/96/176.4/192 kHz
+//   sample rates. Alice2/Tina keeps running at 44.1/48 kHz, but multiple channels
+//   are bundled.
+// - The number of available EMU32/EDI channels is hit in 2x/4x mode, so the total
+//   number of usable inputs/outputs is limited, esp. with ADAT in use.
+// - S/PDIF is unavailable in 4x mode (only over TOSLINK on newer 1010 cards) due
+//   to being unspecified at 176.4/192 kHz. Therefore, the Dock's S/PDIF channels
+//   can overlap with the Dock's ADC/DAC's high channels.
+// - The code names are mentioned below and in the emu_chip_details table.
 
 /************************************************************************************************/
-/* EMU1010m HANA FPGA registers                                                                        */
+/* EMU1010 FPGA registers                                                                      */
 /************************************************************************************************/
+
 #define EMU_HANA_DESTHI                0x00    /* 0000xxx  3 bits Link Destination */
 #define EMU_HANA_DESTLO                0x01    /* 00xxxxx  5 bits */
+
 #define EMU_HANA_SRCHI         0x02    /* 0000xxx  3 bits Link Source */
 #define EMU_HANA_SRCLO         0x03    /* 00xxxxx  5 bits */
+
 #define EMU_HANA_DOCK_PWR      0x04    /* 000000x  1 bits Audio Dock power */
 #define EMU_HANA_DOCK_PWR_ON           0x01 /* Audio Dock power on */
+
 #define EMU_HANA_WCLOCK                0x05    /* 0000xxx  3 bits Word Clock source select  */
                                        /* Must be written after power on to reset DLL */
                                        /* One is unable to detect the Audio dock without this */
 #define EMU_HANA_WCLOCK_INT_44_1K      0x01
 #define EMU_HANA_WCLOCK_HANA_SPDIF_IN  0x02
 #define EMU_HANA_WCLOCK_HANA_ADAT_IN   0x03
-#define EMU_HANA_WCLOCK_SYNC_BNCN      0x04
+#define EMU_HANA_WCLOCK_SYNC_BNC       0x04
 #define EMU_HANA_WCLOCK_2ND_HANA       0x05
 #define EMU_HANA_WCLOCK_SRC_RESERVED   0x06
 #define EMU_HANA_WCLOCK_OFF            0x07 /* For testing, forces fallback to DEFCLOCK */
 #define EMU_HANA_IRQ_DOCK_LOST         0x08
 
 #define EMU_HANA_SPDIF_MODE    0x0a    /* 00xxxxx  5 bits SPDIF MODE  */
-#define EMU_HANA_SPDIF_MODE_TX_COMSUMER        0x00
+#define EMU_HANA_SPDIF_MODE_TX_CONSUMER        0x00
 #define EMU_HANA_SPDIF_MODE_TX_PRO     0x01
 #define EMU_HANA_SPDIF_MODE_TX_NOCOPY  0x02
-#define EMU_HANA_SPDIF_MODE_RX_COMSUMER        0x00
+#define EMU_HANA_SPDIF_MODE_RX_CONSUMER        0x00
 #define EMU_HANA_SPDIF_MODE_RX_PRO     0x04
 #define EMU_HANA_SPDIF_MODE_RX_NOCOPY  0x08
 #define EMU_HANA_SPDIF_MODE_RX_INVALID 0x10
 #define EMU_HANA_OPTICAL_OUT_ADAT      0x02
 
 #define EMU_HANA_MIDI_IN               0x0c    /* 000000x  1 bit  Control MIDI  */
-#define EMU_HANA_MIDI_IN_FROM_HAMOA    0x00 /* HAMOA MIDI in to Alice 2 MIDI B */
-#define EMU_HANA_MIDI_IN_FROM_DOCK     0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */
+#define EMU_HANA_MIDI_INA_FROM_HAMOA   0x01 /* HAMOA MIDI in to Alice 2 MIDI A */
+#define EMU_HANA_MIDI_INA_FROM_DOCK1   0x02 /* Audio Dock-1 MIDI in to Alice 2 MIDI A */
+#define EMU_HANA_MIDI_INA_FROM_DOCK2   0x03 /* Audio Dock-2 MIDI in to Alice 2 MIDI A */
+#define EMU_HANA_MIDI_INB_FROM_HAMOA   0x08 /* HAMOA MIDI in to Alice 2 MIDI B */
+#define EMU_HANA_MIDI_INB_FROM_DOCK1   0x10 /* Audio Dock-1 MIDI in to Alice 2 MIDI B */
+#define EMU_HANA_MIDI_INB_FROM_DOCK2   0x18 /* Audio Dock-2 MIDI in to Alice 2 MIDI B */
 
 #define EMU_HANA_DOCK_LEDS_1   0x0d    /* 000xxxx  4 bit  Audio Dock LEDs  */
 #define EMU_HANA_DOCK_LEDS_1_MIDI1     0x01    /* MIDI 1 LED on */
 #define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL     0x20    /* Manual Signal detection */
 
 #define EMU_HANA_ADC_PADS      0x10    /* 0000xxx  3 bit  Audio Dock ADC 14dB pads */
-#define EMU_HANA_DOCK_ADC_PAD1 0x01    /* 14dB Attenuation on Audio Dock ADC 1 */
-#define EMU_HANA_DOCK_ADC_PAD2 0x02    /* 14dB Attenuation on Audio Dock ADC 2 */
-#define EMU_HANA_DOCK_ADC_PAD3 0x04    /* 14dB Attenuation on Audio Dock ADC 3 */
-#define EMU_HANA_0202_ADC_PAD1 0x08    /* 14dB Attenuation on 0202 ADC 1 */
+#define EMU_HANA_DOCK_ADC_PAD1         0x01    /* 14dB Attenuation on Audio Dock ADC 1 */
+#define EMU_HANA_DOCK_ADC_PAD2         0x02    /* 14dB Attenuation on Audio Dock ADC 2 */
+#define EMU_HANA_DOCK_ADC_PAD3         0x04    /* 14dB Attenuation on Audio Dock ADC 3 */
+#define EMU_HANA_0202_ADC_PAD1         0x08    /* 14dB Attenuation on 0202 ADC 1 */
 
 #define EMU_HANA_DOCK_MISC     0x11    /* 0xxxxxx  6 bit  Audio Dock misc bits */
-#define EMU_HANA_DOCK_DAC1_MUTE        0x01    /* DAC 1 Mute */
-#define EMU_HANA_DOCK_DAC2_MUTE        0x02    /* DAC 2 Mute */
-#define EMU_HANA_DOCK_DAC3_MUTE        0x04    /* DAC 3 Mute */
-#define EMU_HANA_DOCK_DAC4_MUTE        0x08    /* DAC 4 Mute */
+#define EMU_HANA_DOCK_DAC1_MUTE                0x01    /* DAC 1 Mute */
+#define EMU_HANA_DOCK_DAC2_MUTE                0x02    /* DAC 2 Mute */
+#define EMU_HANA_DOCK_DAC3_MUTE                0x04    /* DAC 3 Mute */
+#define EMU_HANA_DOCK_DAC4_MUTE                0x08    /* DAC 4 Mute */
 #define EMU_HANA_DOCK_PHONES_192_DAC1  0x00    /* DAC 1 Headphones source at 192kHz */
 #define EMU_HANA_DOCK_PHONES_192_DAC2  0x10    /* DAC 2 Headphones source at 192kHz */
 #define EMU_HANA_DOCK_PHONES_192_DAC3  0x20    /* DAC 3 Headphones source at 192kHz */
 #define EMU_HANA_DOCK_PHONES_192_DAC4  0x30    /* DAC 4 Headphones source at 192kHz */
 
 #define EMU_HANA_MIDI_OUT      0x12    /* 00xxxxx  5 bit  Source for each MIDI out port */
-#define EMU_HANA_MIDI_OUT_0202 0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */
-#define EMU_HANA_MIDI_OUT_DOCK1        0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */
-#define EMU_HANA_MIDI_OUT_DOCK2        0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */
-#define EMU_HANA_MIDI_OUT_SYNC2        0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */
-#define EMU_HANA_MIDI_OUT_LOOP 0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */
+#define EMU_HANA_MIDI_OUT_0202         0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK1                0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK2                0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_SYNC2                0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_LOOP         0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */
 
 #define EMU_HANA_DAC_PADS      0x13    /* 00xxxxx  5 bit  DAC 14dB attenuation pads */
-#define EMU_HANA_DOCK_DAC_PAD1 0x01    /* 14dB Attenuation on AudioDock DAC 1. Left and Right */
-#define EMU_HANA_DOCK_DAC_PAD2 0x02    /* 14dB Attenuation on AudioDock DAC 2. Left and Right */
-#define EMU_HANA_DOCK_DAC_PAD3 0x04    /* 14dB Attenuation on AudioDock DAC 3. Left and Right */
-#define EMU_HANA_DOCK_DAC_PAD4 0x08    /* 14dB Attenuation on AudioDock DAC 4. Left and Right */
-#define EMU_HANA_0202_DAC_PAD1 0x10    /* 14dB Attenuation on 0202 DAC 1. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD1         0x01    /* 14dB Attenuation on AudioDock DAC 1. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD2         0x02    /* 14dB Attenuation on AudioDock DAC 2. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD3         0x04    /* 14dB Attenuation on AudioDock DAC 3. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD4         0x08    /* 14dB Attenuation on AudioDock DAC 4. Left and Right */
+#define EMU_HANA_0202_DAC_PAD1         0x10    /* 14dB Attenuation on 0202 DAC 1. Left and Right */
 
 /* 0x14 - 0x1f Unused R/W registers */
-#define EMU_HANA_IRQ_STATUS    0x20    /* 000xxxx  4 bits IRQ Status  */
-#if 0  /* Already defined for reg 0x09 IRQ_ENABLE */
-#define EMU_HANA_IRQ_WCLK_CHANGED      0x01
-#define EMU_HANA_IRQ_ADAT              0x02
-#define EMU_HANA_IRQ_DOCK              0x04
-#define EMU_HANA_IRQ_DOCK_LOST         0x08
-#endif
+
+#define EMU_HANA_IRQ_STATUS    0x20    /* 00xxxxx  5 bits IRQ Status  */
+                                       /* Same bits as for EMU_HANA_IRQ_ENABLE */
+                                       /* Reading the register resets it. */
 
 #define EMU_HANA_OPTION_CARDS  0x21    /* 000xxxx  4 bits Presence of option cards */
-#define EMU_HANA_OPTION_HAMOA  0x01    /* HAMOA card present */
-#define EMU_HANA_OPTION_SYNC   0x02    /* Sync card present */
-#define EMU_HANA_OPTION_DOCK_ONLINE    0x04    /* Audio Dock online and FPGA configured */
-#define EMU_HANA_OPTION_DOCK_OFFLINE   0x08    /* Audio Dock online and FPGA not configured */
+#define EMU_HANA_OPTION_HAMOA          0x01    /* Hamoa (analog I/O) card present */
+#define EMU_HANA_OPTION_SYNC           0x02    /* Sync card present */
+#define EMU_HANA_OPTION_DOCK_ONLINE    0x04    /* Audio/Micro dock present and FPGA configured */
+#define EMU_HANA_OPTION_DOCK_OFFLINE   0x08    /* Audio/Micro dock present and FPGA not configured */
 
-#define EMU_HANA_ID            0x22    /* 1010101  7 bits ID byte & 0x7f = 0x55 */
+#define EMU_HANA_ID            0x22    /* 1010101  7 bits ID byte & 0x7f = 0x55 with Alice2 */
+                                       /* 0010101  5 bits ID byte & 0x1f = 0x15 with Tina/2 */
 
 #define EMU_HANA_MAJOR_REV     0x23    /* 0000xxx  3 bit  Hana FPGA Major rev */
 #define EMU_HANA_MINOR_REV     0x24    /* 0000xxx  3 bit  Hana FPGA Minor rev */
 #define EMU_DOCK_MINOR_REV     0x26    /* 0000xxx  3 bit  Audio Dock FPGA Minor rev */
 
 #define EMU_DOCK_BOARD_ID      0x27    /* 00000xx  2 bits Audio Dock ID pins */
-#define EMU_DOCK_BOARD_ID0     0x00    /* ID bit 0 */
-#define EMU_DOCK_BOARD_ID1     0x03    /* ID bit 1 */
+#define EMU_DOCK_BOARD_ID0             0x00    /* ID bit 0 */
+#define EMU_DOCK_BOARD_ID1             0x03    /* ID bit 1 */
 
 #define EMU_HANA_WC_SPDIF_HI   0x28    /* 0xxxxxx  6 bit  SPDIF IN Word clock, upper 6 bits */
 #define EMU_HANA_WC_SPDIF_LO   0x29    /* 0xxxxxx  6 bit  SPDIF IN Word clock, lower 6 bits */
 
 #define EMU_HANA2_WC_SPDIF_HI  0x2e    /* 0xxxxxx  6 bit  HANA2 SPDIF IN Word clock, upper 6 bits */
 #define EMU_HANA2_WC_SPDIF_LO  0x2f    /* 0xxxxxx  6 bit  HANA2 SPDIF IN Word clock, lower 6 bits */
+
 /* 0x30 - 0x3f Unused Read only registers */
 
+// The meaning of this is not clear; kX-project just calls it "lock" in some info-only code.
+#define EMU_HANA_LOCK_STS_LO   0x38    /* 0xxxxxx  lower 6 bits */
+#define EMU_HANA_LOCK_STS_HI   0x39    /* 0xxxxxx  upper 6 bits */
+
 /************************************************************************************************/
-/* EMU1010m HANA Destinations                                                                  */
+/* EMU1010 Audio Destinations                                                                  */
 /************************************************************************************************/
-/* Hana, original 1010,1212,1820 using Alice2
- * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+/* Hana, original 1010,1212m,1820[m] using Alice2
  * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
- * 0x01, 0x10-0x1f: 32 Elink channels to Audio Dock
- * 0x01, 0x00: Dock DAC 1 Left
- * 0x01, 0x04: Dock DAC 1 Right
- * 0x01, 0x08: Dock DAC 2 Left
- * 0x01, 0x0c: Dock DAC 2 Right
- * 0x01, 0x10: Dock DAC 3 Left
- * 0x01, 0x12: PHONES Left
- * 0x01, 0x14: Dock DAC 3 Right
- * 0x01, 0x16: PHONES Right
- * 0x01, 0x18: Dock DAC 4 Left
- * 0x01, 0x1a: S/PDIF Left
- * 0x01, 0x1c: Dock DAC 4 Right
- * 0x01, 0x1e: S/PDIF Right
+ * 0x01, 0x00-0x1f: 32 EDI channels to Audio Dock
+ *       0x00: Dock DAC 1 Left
+ *       0x04: Dock DAC 1 Right
+ *       0x08: Dock DAC 2 Left
+ *       0x0c: Dock DAC 2 Right
+ *       0x10: Dock DAC 3 Left
+ *       0x12: PHONES Left (n/a in 2x/4x mode; output mirrors DAC4 Left)
+ *       0x14: Dock DAC 3 Right
+ *       0x16: PHONES Right (n/a in 2x/4x mode; output mirrors DAC4 Right)
+ *       0x18: Dock DAC 4 Left
+ *       0x1a: S/PDIF Left
+ *       0x1c: Dock DAC 4 Right
+ *       0x1e: S/PDIF Right
  * 0x02, 0x00: Hana S/PDIF Left
  * 0x02, 0x01: Hana S/PDIF Right
- * 0x03, 0x00: Hanoa DAC Left
- * 0x03, 0x01: Hanoa DAC Right
+ * 0x03, 0x00: Hamoa DAC Left
+ * 0x03, 0x01: Hamoa DAC Right
  * 0x04, 0x00-0x07: Hana ADAT
  * 0x05, 0x00: I2S0 Left to Alice2
  * 0x05, 0x01: I2S0 Right to Alice2
  * Hana2 never released, but used Tina
  * Not needed.
  *
- * Hana3, rev2 1010,1212,1616 using Tina
- * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * Hana3, rev2 1010,1212m,1616[m] using Tina
  * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina
- * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
- * 0x01, 0x00: Dock DAC 1 Left
- * 0x01, 0x04: Dock DAC 1 Right
- * 0x01, 0x08: Dock DAC 2 Left
- * 0x01, 0x0c: Dock DAC 2 Right
- * 0x01, 0x10: Dock DAC 3 Left
- * 0x01, 0x12: Dock S/PDIF Left
- * 0x01, 0x14: Dock DAC 3 Right
- * 0x01, 0x16: Dock S/PDIF Right
- * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x01, 0x00-0x1f: 32 EDI channels to Micro Dock
+ *       0x00: Dock DAC 1 Left
+ *       0x04: Dock DAC 1 Right
+ *       0x08: Dock DAC 2 Left
+ *       0x0c: Dock DAC 2 Right
+ *       0x10: Dock DAC 3 Left
+ *       0x12: Dock S/PDIF Left
+ *       0x14: Dock DAC 3 Right
+ *       0x16: Dock S/PDIF Right
+ *       0x18-0x1f: Dock ADAT 0-7
  * 0x02, 0x00: Hana3 S/PDIF Left
  * 0x02, 0x01: Hana3 S/PDIF Right
- * 0x03, 0x00: Hanoa DAC Left
- * 0x03, 0x01: Hanoa DAC Right
+ * 0x03, 0x00: Hamoa DAC Left
+ * 0x03, 0x01: Hamoa DAC Right
  * 0x04, 0x00-0x07: Hana3 ADAT 0-7
  * 0x05, 0x00-0x0f: 16 EMU32B channels to Tina
  * 0x06-0x07: Not used
  *
  * HanaLite, rev1 0404 using Alice2
- * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
- * 0x01: Not used
- * 0x02, 0x00: S/PDIF Left
- * 0x02, 0x01: S/PDIF Right
- * 0x03, 0x00: DAC Left
- * 0x03, 0x01: DAC Right
- * 0x04-0x07: Not used
- *
- * HanaLiteLite, rev2 0404 using Alice2
- * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
+ * HanaLiteLite, rev2 0404 using Tina
+ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2/Tina
  * 0x01: Not used
  * 0x02, 0x00: S/PDIF Left
  * 0x02, 0x01: S/PDIF Right
  * 0x04-0x07: Not used
  *
  * Mana, Cardbus 1616 using Tina2
- * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
  * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina2
- * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
- * 0x01, 0x00: Dock DAC 1 Left
- * 0x01, 0x04: Dock DAC 1 Right
- * 0x01, 0x08: Dock DAC 2 Left
- * 0x01, 0x0c: Dock DAC 2 Right
- * 0x01, 0x10: Dock DAC 3 Left
- * 0x01, 0x12: Dock S/PDIF Left
- * 0x01, 0x14: Dock DAC 3 Right
- * 0x01, 0x16: Dock S/PDIF Right
- * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x01, 0x00-0x1f: 32 EDI channels to Micro Dock
+ *       (same as rev2 1010)
  * 0x02: Not used
  * 0x03, 0x00: Mana DAC Left
  * 0x03, 0x01: Mana DAC Right
  * 0x04, 0x00-0x0f: 16 EMU32B channels to Tina2
  * 0x05-0x07: Not used
- *
- *
  */
+
 /* 32-bit destinations of signal in the Hana FPGA. Destinations are either
- * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
- * - 16 x EMU_DST_ALICE2_EMU32_X.
- */
-/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */
-/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture.
- * Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on
- * setup of mixer control for each destination - see emumixer.c -
- * snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
+ * physical outputs of Hana, or outputs going to Alice2/Tina for capture -
+ * 16 x EMU_DST_ALICE2_EMU32_X (2x on rev2 boards). Which data is fed into
+ * a channel depends on the mixer control setting for each destination - see
+ * emumixer.c - snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
  */
 #define EMU_DST_ALICE2_EMU32_0 0x000f  /* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_1 0x0000  /* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_DOCK_SPDIF_RIGHT2      0x011f  /* Audio Dock SPDIF Right, 2nd or 96kHz */
 #define EMU_DST_HANA_SPDIF_LEFT1       0x0200  /* Hana SPDIF Left, 1st or 48kHz only */
 #define EMU_DST_HANA_SPDIF_LEFT2       0x0202  /* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_LEFT3       0x0204  /* Hana SPDIF Left, 3rd or 192kHz */
+#define EMU_DST_HANA_SPDIF_LEFT4       0x0206  /* Hana SPDIF Left, 4th or 192kHz */
 #define EMU_DST_HANA_SPDIF_RIGHT1      0x0201  /* Hana SPDIF Right, 1st or 48kHz only */
 #define EMU_DST_HANA_SPDIF_RIGHT2      0x0203  /* Hana SPDIF Right, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_RIGHT3      0x0205  /* Hana SPDIF Right, 3rd or 192kHz */
+#define EMU_DST_HANA_SPDIF_RIGHT4      0x0207  /* Hana SPDIF Right, 4th or 192kHz */
 #define EMU_DST_HAMOA_DAC_LEFT1        0x0300  /* Hamoa DAC Left, 1st or 48kHz only */
 #define EMU_DST_HAMOA_DAC_LEFT2        0x0302  /* Hamoa DAC Left, 2nd or 96kHz */
 #define EMU_DST_HAMOA_DAC_LEFT3        0x0304  /* Hamoa DAC Left, 3rd or 192kHz */
 #define EMU_DST_HAMOA_DAC_RIGHT2       0x0303  /* Hamoa DAC Right, 2nd or 96kHz */
 #define EMU_DST_HAMOA_DAC_RIGHT3       0x0305  /* Hamoa DAC Right, 3rd or 192kHz */
 #define EMU_DST_HAMOA_DAC_RIGHT4       0x0307  /* Hamoa DAC Right, 4th or 192kHz */
+// In S/MUX mode, the samples of one channel are adjacent.
 #define EMU_DST_HANA_ADAT      0x0400  /* Hana ADAT 8 channel out +0 to +7 */
 #define EMU_DST_ALICE_I2S0_LEFT                0x0500  /* Alice2 I2S0 Left */
 #define EMU_DST_ALICE_I2S0_RIGHT       0x0501  /* Alice2 I2S0 Right */
 #define EMU_DST_ALICE_I2S2_RIGHT       0x0701  /* Alice2 I2S2 Right */
 
 /* Additional destinations for 1616(M)/Microdock */
-/* Microdock S/PDIF OUT Left, 1st or 48kHz only */
-#define EMU_DST_MDOCK_SPDIF_LEFT1      0x0112
-/* Microdock S/PDIF OUT Left, 2nd or 96kHz */
-#define EMU_DST_MDOCK_SPDIF_LEFT2      0x0113
-/* Microdock S/PDIF OUT Right, 1st or 48kHz only */
-#define EMU_DST_MDOCK_SPDIF_RIGHT1     0x0116
-/* Microdock S/PDIF OUT Right, 2nd or 96kHz  */
-#define EMU_DST_MDOCK_SPDIF_RIGHT2     0x0117
-/* Microdock S/PDIF ADAT 8 channel out +8 to +f */
-#define EMU_DST_MDOCK_ADAT             0x0118
-
-/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
-#define EMU_DST_MANA_DAC_LEFT          0x0300
-/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
-#define EMU_DST_MANA_DAC_RIGHT         0x0301
+
+#define EMU_DST_MDOCK_SPDIF_LEFT1      0x0112  /* Microdock S/PDIF OUT Left, 1st or 48kHz only */
+#define EMU_DST_MDOCK_SPDIF_LEFT2      0x0113  /* Microdock S/PDIF OUT Left, 2nd or 96kHz */
+#define EMU_DST_MDOCK_SPDIF_RIGHT1     0x0116  /* Microdock S/PDIF OUT Right, 1st or 48kHz only */
+#define EMU_DST_MDOCK_SPDIF_RIGHT2     0x0117  /* Microdock S/PDIF OUT Right, 2nd or 96kHz  */
+#define EMU_DST_MDOCK_ADAT             0x0118  /* Microdock S/PDIF ADAT 8 channel out +8 to +f */
+
+#define EMU_DST_MANA_DAC_LEFT          0x0300  /* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
+#define EMU_DST_MANA_DAC_RIGHT         0x0301  /* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
 
 /************************************************************************************************/
-/* EMU1010m HANA Sources                                                                       */
+/* EMU1010 Audio Sources                                                                       */
 /************************************************************************************************/
-/* Hana, original 1010,1212,1820 using Alice2
- * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00,0x00-0x1f: Silence
- * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
- * 0x01, 0x00: Dock Mic A
- * 0x01, 0x04: Dock Mic B
- * 0x01, 0x08: Dock ADC 1 Left
- * 0x01, 0x0c: Dock ADC 1 Right
- * 0x01, 0x10: Dock ADC 2 Left
- * 0x01, 0x14: Dock ADC 2 Right
- * 0x01, 0x18: Dock ADC 3 Left
- * 0x01, 0x1c: Dock ADC 3 Right
- * 0x02, 0x00: Hana ADC Left
- * 0x02, 0x01: Hana ADC Right
+/* Hana, original 1010,1212m,1820[m] using Alice2
+ * 0x00, 0x00-0x1f: Silence
+ * 0x01, 0x00-0x1f: 32 EDI channels from Audio Dock
+ *       0x00: Dock Mic A
+ *       0x04: Dock Mic B
+ *       0x08: Dock ADC 1 Left
+ *       0x0c: Dock ADC 1 Right
+ *       0x10: Dock ADC 2 Left
+ *       0x14: Dock ADC 2 Right
+ *       0x18: Dock ADC 3 Left
+ *       0x1c: Dock ADC 3 Right
+ * 0x02, 0x00: Hamoa ADC Left
+ * 0x02, 0x01: Hamoa ADC Right
  * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
  * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
  * 0x04, 0x00-0x07: Hana ADAT
  * Hana2 never released, but used Tina
  * Not needed.
  *
- * Hana3, rev2 1010,1212,1616 using Tina
- * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00,0x00-0x1f: Silence
- * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
- * 0x01, 0x00: Dock Mic A
- * 0x01, 0x04: Dock Mic B
- * 0x01, 0x08: Dock ADC 1 Left
- * 0x01, 0x0c: Dock ADC 1 Right
- * 0x01, 0x10: Dock ADC 2 Left
- * 0x01, 0x12: Dock S/PDIF Left
- * 0x01, 0x14: Dock ADC 2 Right
- * 0x01, 0x16: Dock S/PDIF Right
- * 0x01, 0x18-0x1f: Dock ADAT 0-7
- * 0x01, 0x18: Dock ADC 3 Left
- * 0x01, 0x1c: Dock ADC 3 Right
- * 0x02, 0x00: Hanoa ADC Left
- * 0x02, 0x01: Hanoa ADC Right
+ * Hana3, rev2 1010,1212m,1616[m] using Tina
+ * 0x00, 0x00-0x1f: Silence
+ * 0x01, 0x00-0x1f: 32 EDI channels from Micro Dock
+ *       0x00: Dock Mic A
+ *       0x04: Dock Mic B
+ *       0x08: Dock ADC 1 Left
+ *       0x0c: Dock ADC 1 Right
+ *       0x10: Dock ADC 2 Left
+ *       0x12: Dock S/PDIF Left
+ *       0x14: Dock ADC 2 Right
+ *       0x16: Dock S/PDIF Right
+ *       0x18-0x1f: Dock ADAT 0-7
+ * 0x02, 0x00: Hamoa ADC Left
+ * 0x02, 0x01: Hamoa ADC Right
  * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
  * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
  * 0x04, 0x00-0x07: Hana3 ADAT
  * 0x06-0x07: Not used
  *
  * HanaLite, rev1 0404 using Alice2
- * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00,0x00-0x1f: Silence
- * 0x01: Not used
- * 0x02, 0x00: ADC Left
- * 0x02, 0x01: ADC Right
- * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
- * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
- * 0x04: Not used
- * 0x05, 0x00: S/PDIF Left
- * 0x05, 0x01: S/PDIF Right
- * 0x06-0x07: Not used
- *
- * HanaLiteLite, rev2 0404 using Alice2
- * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00,0x00-0x1f: Silence
+ * HanaLiteLite, rev2 0404 using Tina
+ * 0x00, 0x00-0x1f: Silence
  * 0x01: Not used
  * 0x02, 0x00: ADC Left
  * 0x02, 0x01: ADC Right
- * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
- * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
+ * 0x03, 0x00-0x0f: 16 inputs from Alice2/Tina Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Alice2/Tina Emu32B output
  * 0x04: Not used
  * 0x05, 0x00: S/PDIF Left
  * 0x05, 0x01: S/PDIF Right
  * 0x06-0x07: Not used
  *
  * Mana, Cardbus 1616 using Tina2
- * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
- * 0x00,0x00-0x1f: Silence
- * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
- * 0x01, 0x00: Dock Mic A
- * 0x01, 0x04: Dock Mic B
- * 0x01, 0x08: Dock ADC 1 Left
- * 0x01, 0x0c: Dock ADC 1 Right
- * 0x01, 0x10: Dock ADC 2 Left
- * 0x01, 0x12: Dock S/PDIF Left
- * 0x01, 0x14: Dock ADC 2 Right
- * 0x01, 0x16: Dock S/PDIF Right
- * 0x01, 0x18-0x1f: Dock ADAT 0-7
- * 0x01, 0x18: Dock ADC 3 Left
- * 0x01, 0x1c: Dock ADC 3 Right
+ * 0x00, 0x00-0x1f: Silence
+ * 0x01, 0x00-0x1f: 32 EDI channels from Micro Dock
+ *       (same as rev2 1010)
  * 0x02: Not used
- * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
- * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
+ * 0x03, 0x00-0x0f: 16 inputs from Tina2 Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Tina2 Emu32B output
  * 0x04-0x07: Not used
- *
  */
 
 /* 32-bit sources of signal in the Hana FPGA. The sources are routed to
- * destinations using mixer control for each destination - see emumixer.c
- * Sources are either physical inputs of FPGA,
- * or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A +
- * 16 x EMU_SRC_ALICE_EMU32B
+ * destinations using a mixer control for each destination - see emumixer.c.
+ * Sources are either physical inputs of Hana, or inputs from Alice2/Tina -
+ * 16 x EMU_SRC_ALICE_EMU32A + 16 x EMU_SRC_ALICE_EMU32B.
  */
 #define EMU_SRC_SILENCE                0x0000  /* Silence */
 #define EMU_SRC_DOCK_MIC_A1    0x0100  /* Audio Dock Mic A, 1st or 48kHz only */
 #define EMU_SRC_HAMOA_ADC_RIGHT4       0x0207  /* Hamoa ADC Right, 4th or 192kHz */
 #define EMU_SRC_ALICE_EMU32A           0x0300  /* Alice2 EMU32a 16 outputs. +0 to +0xf */
 #define EMU_SRC_ALICE_EMU32B           0x0310  /* Alice2 EMU32b 16 outputs. +0 to +0xf */
+// In S/MUX mode, the samples of one channel are adjacent.
 #define EMU_SRC_HANA_ADAT      0x0400  /* Hana ADAT 8 channel in +0 to +7 */
 #define EMU_SRC_HANA_SPDIF_LEFT1       0x0500  /* Hana SPDIF Left, 1st or 48kHz only */
 #define EMU_SRC_HANA_SPDIF_LEFT2       0x0502  /* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_SRC_HANA_SPDIF_LEFT3       0x0504  /* Hana SPDIF Left, 3rd or 192kHz */
+#define EMU_SRC_HANA_SPDIF_LEFT4       0x0506  /* Hana SPDIF Left, 4th or 192kHz */
 #define EMU_SRC_HANA_SPDIF_RIGHT1      0x0501  /* Hana SPDIF Right, 1st or 48kHz only */
 #define EMU_SRC_HANA_SPDIF_RIGHT2      0x0503  /* Hana SPDIF Right, 2nd or 96kHz */
+#define EMU_SRC_HANA_SPDIF_RIGHT3      0x0505  /* Hana SPDIF Right, 3rd or 192kHz */
+#define EMU_SRC_HANA_SPDIF_RIGHT4      0x0507  /* Hana SPDIF Right, 4th or 192kHz */
 
 /* Additional inputs for 1616(M)/Microdock */
-/* Microdock S/PDIF Left, 1st or 48kHz only */
-#define EMU_SRC_MDOCK_SPDIF_LEFT1      0x0112
-/* Microdock S/PDIF Left, 2nd or 96kHz */
-#define EMU_SRC_MDOCK_SPDIF_LEFT2      0x0113
-/* Microdock S/PDIF Right, 1st or 48kHz only */
-#define EMU_SRC_MDOCK_SPDIF_RIGHT1     0x0116
-/* Microdock S/PDIF Right, 2nd or 96kHz */
-#define EMU_SRC_MDOCK_SPDIF_RIGHT2     0x0117
-/* Microdock ADAT 8 channel in +8 to +f */
-#define EMU_SRC_MDOCK_ADAT             0x0118
+
+#define EMU_SRC_MDOCK_SPDIF_LEFT1      0x0112  /* Microdock S/PDIF Left, 1st or 48kHz only */
+#define EMU_SRC_MDOCK_SPDIF_LEFT2      0x0113  /* Microdock S/PDIF Left, 2nd or 96kHz */
+#define EMU_SRC_MDOCK_SPDIF_RIGHT1     0x0116  /* Microdock S/PDIF Right, 1st or 48kHz only */
+#define EMU_SRC_MDOCK_SPDIF_RIGHT2     0x0117  /* Microdock S/PDIF Right, 2nd or 96kHz */
+#define EMU_SRC_MDOCK_ADAT             0x0118  /* Microdock ADAT 8 channel in +8 to +f */
 
 /* 0x600 and 0x700 no used */
 
@@ -1485,7 +1434,6 @@ enum {
 struct snd_emu10k1;
 
 struct snd_emu10k1_voice {
-       struct snd_emu10k1 *emu;
        int number;
        unsigned int use: 1,
            pcm: 1,
@@ -1599,10 +1547,8 @@ struct snd_emu10k1_fx8010_pcm {
 };
 
 struct snd_emu10k1_fx8010 {
-       unsigned short fxbus_mask;      /* used FX buses (bitmask) */
-       unsigned short extin_mask;      /* used external inputs (bitmask) */
-       unsigned short extout_mask;     /* used external outputs (bitmask) */
-       unsigned short pad1;
+       unsigned short extin_mask;      /* used external inputs (bitmask); not used for Audigy */
+       unsigned short extout_mask;     /* used external outputs (bitmask); not used for Audigy */
        unsigned int itram_size;        /* internal TRAM size in samples */
        struct snd_dma_buffer etram_pages; /* external TRAM pages and size */
        unsigned int dbg;               /* FX debugger register */
@@ -1639,14 +1585,26 @@ enum {
        EMU_MODEL_EMU0404,
 };
 
+// Chip-o-logy:
+// - All SB Live! cards use EMU10K1 chips
+// - All SB Audigy cards use CA* chips, termed "emu10k2" by the driver
+// - Original Audigy uses CA0100 "Alice"
+// - Audigy 2 uses CA0102/CA10200 "Alice2"
+//   - Has an interface for CA0151 (P16V) "Alice3"
+// - Audigy 2 Value uses CA0108/CA10300 "Tina"
+//   - Approximately a CA0102 with an on-chip CA0151 (P17V)
+// - Audigy 2 ZS NB uses CA0109 "Tina2"
+//   - Cardbus version of CA0108
 struct snd_emu_chip_details {
        u32 vendor;
        u32 device;
        u32 subsystem;
        unsigned char revision;
        unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */
+                                   /* Redundant with emu10k2_chip being unset. */
        unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
        unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
+                                   /* Redundant with ca0108_chip being unset. */
        unsigned char ca0108_chip;  /* Audigy 2 Value */
        unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */
        unsigned char ca0151_chip;  /* P16V */
@@ -1656,8 +1614,8 @@ struct snd_emu_chip_details {
        unsigned char ac97_chip;    /* Has an AC97 chip: 1 = mandatory, 2 = optional */
        unsigned char ecard;        /* APS EEPROM */
        unsigned char emu_model;     /* EMU model type */
-       unsigned char spi_dac;      /* SPI interface for DAC */
-       unsigned char i2c_adc;      /* I2C interface for ADC */
+       unsigned char spi_dac;      /* SPI interface for DAC; requires ca0108_chip */
+       unsigned char i2c_adc;      /* I2C interface for ADC; requires ca0108_chip */
        unsigned char adc_1361t;    /* Use Philips 1361T ADC */
        unsigned char invert_shared_spdif; /* analog/digital switch inverted */
        const char *driver;
@@ -1691,7 +1649,6 @@ struct snd_emu10k1 {
        unsigned int revision;                  /* chip revision */
        unsigned int serial;                    /* serial number */
        unsigned short model;                   /* subsystem id */
-       unsigned int card_type;                 /* EMU10K1_CARD_* */
        unsigned int ecard_ctrl;                /* ecard control bits */
        unsigned int address_mode;              /* address mode */
        unsigned long dma_mask;                 /* PCI DMA mask */
@@ -1732,15 +1689,13 @@ struct snd_emu10k1 {
        void *synth;
        int (*get_synth_voice)(struct snd_emu10k1 *emu);
 
-       spinlock_t reg_lock;
-       spinlock_t emu_lock;
-       spinlock_t voice_lock;
+       spinlock_t reg_lock;  // high-level driver lock
+       spinlock_t emu_lock;  // low-level i/o lock
+       spinlock_t voice_lock;  // voice allocator lock
        spinlock_t spi_lock; /* serialises access to spi port */
        spinlock_t i2c_lock; /* serialises access to i2c port */
 
        struct snd_emu10k1_voice voices[NUM_G];
-       struct snd_emu10k1_voice p16v_voices[4];
-       struct snd_emu10k1_voice p16v_capture_voice;
        int p16v_device_offset;
        u32 p16v_capture_source;
        u32 p16v_capture_channel;
@@ -1760,11 +1715,11 @@ struct snd_emu10k1 {
        void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
        void (*spdif_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
        void (*dsp_interrupt)(struct snd_emu10k1 *emu);
+       void (*p16v_interrupt)(struct snd_emu10k1 *emu);
 
        struct snd_pcm_substream *pcm_capture_substream;
        struct snd_pcm_substream *pcm_capture_mic_substream;
        struct snd_pcm_substream *pcm_capture_efx_substream;
-       struct snd_pcm_substream *pcm_playback_efx_substream;
 
        struct snd_timer *timer;
 
@@ -1824,9 +1779,9 @@ unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg,
 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
 int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value);
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value);
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src);
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value);
+void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src);
 unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h
new file mode 100644 (file)
index 0000000..dbc47af
--- /dev/null
@@ -0,0 +1,166 @@
+/* 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) 2022-2023 Intel Corporation. All rights reserved.
+ */
+
+struct hdac_bus;
+struct hdac_ext_link;
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
+
+int hda_bus_ml_init(struct hdac_bus *bus);
+void hda_bus_ml_free(struct hdac_bus *bus);
+
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid);
+void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable);
+bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
+
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd);
+int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd);
+
+int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid);
+int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus);
+
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
+void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink);
+
+int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid);
+int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus);
+
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid);
+bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus);
+
+int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink);
+int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
+
+int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink);
+int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
+
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink);
+int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink);
+
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num);
+
+void hda_bus_ml_put_all(struct hdac_bus *bus);
+void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
+int hda_bus_ml_resume(struct hdac_bus *bus);
+int hda_bus_ml_suspend(struct hdac_bus *bus);
+
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus);
+struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
+
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid);
+
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable);
+
+#else
+
+static inline int
+hda_bus_ml_init(struct hdac_bus *bus) { return 0; }
+
+static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
+
+static inline int
+hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; }
+
+static inline void
+hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) { }
+
+static inline bool
+hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; }
+
+static inline int
+hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; }
+
+static inline void
+hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) { }
+
+static inline void
+hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) { }
+
+static inline int
+hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) { return 0; }
+
+static inline int
+hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) { return 0; }
+
+static inline bool
+hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) { return false; }
+
+static inline bool
+hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) { return false; }
+
+static inline int
+hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return 0;
+}
+
+static inline int
+hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) { return 0; }
+
+static inline int
+hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; }
+
+static inline int
+hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; }
+
+static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
+static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
+static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
+static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
+
+static inline struct hdac_ext_link *
+hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; }
+
+static inline struct hdac_ext_link *
+hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; }
+
+static inline struct mutex *
+hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) { return NULL; }
+
+static inline int
+hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+       return 0;
+}
+#endif /* CONFIG_SND_SOC_SOF_HDA */
index d37cf43546eba946f58c3f487261a41a413cbe57..9c7872c0ca798eb359815e349c12917c0de8ba60 100644 (file)
@@ -258,14 +258,27 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_ML_BASE                    0x40
 #define AZX_ML_INTERVAL                        0x40
 
+/* HDaudio registers valid for HDaudio and HDaudio extended links */
 #define AZX_REG_ML_LCAP                        0x00
-#define AZX_REG_ML_LCTL                        0x04
 
+#define AZX_ML_HDA_LCAP_ALT            BIT(28)
+#define AZX_ML_HDA_LCAP_ALT_HDA                0x0
+#define AZX_ML_HDA_LCAP_ALT_HDA_EXT    0x1
+
+#define AZX_ML_HDA_LCAP_INTC           BIT(27)         /* only used if ALT == 1 */
+#define AZX_ML_HDA_LCAP_OFLS           BIT(26)         /* only used if ALT == 1 */
+#define AZX_ML_HDA_LCAP_LSS            BIT(23)         /* only used if ALT == 1 */
+#define AZX_ML_HDA_LCAP_SLCOUNT                GENMASK(22, 20) /* only used if ALT == 1 */
+
+#define AZX_REG_ML_LCTL                        0x04
+#define AZX_ML_LCTL_INTSTS             BIT(31)         /* only used if ALT == 1 */
 #define AZX_ML_LCTL_CPA                        BIT(23)
 #define AZX_ML_LCTL_CPA_SHIFT          23
 #define AZX_ML_LCTL_SPA                        BIT(16)
 #define AZX_ML_LCTL_SPA_SHIFT          16
-#define AZX_ML_LCTL_SCF                        GENMASK(3, 0)
+#define AZX_ML_LCTL_INTEN              BIT(5)          /* only used if ALT == 1 */
+#define AZX_ML_LCTL_OFLEN              BIT(4)          /* only used if ALT == 1 */
+#define AZX_ML_LCTL_SCF                        GENMASK(3, 0)   /* only used if ALT == 0 */
 
 #define AZX_REG_ML_LOSIDV              0x08
 
@@ -273,12 +286,35 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_ML_LOSIDV_STREAM_MASK      0xFFFE
 
 #define AZX_REG_ML_LSDIID              0x0C
+#define AZX_REG_ML_LSDIID_OFFSET(x)    (0x0C + (x) * 0x02)     /* only used if ALT == 1 */
+
+/* HDaudio registers only valid if LCAP.ALT == 0 */
 #define AZX_REG_ML_LPSOO               0x10
 #define AZX_REG_ML_LPSIO               0x12
 #define AZX_REG_ML_LWALFC              0x18
 #define AZX_REG_ML_LOUTPAY             0x20
 #define AZX_REG_ML_LINPAY              0x30
 
+/* HDaudio Extended link registers only valid if LCAP.ALT == 1 */
+#define AZX_REG_ML_LSYNC               0x1C
+
+#define AZX_REG_ML_LSYNC_CMDSYNC       BIT(24)
+#define AZX_REG_ML_LSYNC_CMDSYNC_SHIFT 24
+#define AZX_REG_ML_LSYNC_SYNCGO                BIT(23)
+#define AZX_REG_ML_LSYNC_SYNCPU                BIT(20)
+#define AZX_REG_ML_LSYNC_SYNCPRD       GENMASK(19, 0)
+
+#define AZX_REG_ML_LEPTR               0x20
+
+#define AZX_REG_ML_LEPTR_ID            GENMASK(31, 24)
+#define AZX_REG_ML_LEPTR_ID_SHIFT      24
+#define AZX_REG_ML_LEPTR_ID_SDW                0x00
+#define AZX_REG_ML_LEPTR_ID_INTEL_SSP  0xC0
+#define AZX_REG_ML_LEPTR_ID_INTEL_DMIC  0xC1
+#define AZX_REG_ML_LEPTR_ID_INTEL_UAOL  0xC2
+#define AZX_REG_ML_LEPTR_VER           GENMASK(23, 20)
+#define AZX_REG_ML_LEPTR_PTR           GENMASK(19, 0)
+
 /* registers for DMA Resume Capability Structure */
 #define AZX_DRSM_CAP_ID                        0x5
 #define AZX_REG_DRSM_CTL               0x4
index 04127686e8d03cff61d47b9570501f63080d63da..98e06ea73b2bb6936300d6ba86646b2e55fd7284 100644 (file)
@@ -44,7 +44,7 @@ snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
                        diff += runtime->boundary;
                if (diff < 0)
-                       return -EINVAL;
+                       return -EPIPE;
                rec->sw_ready += (int)frames_to_bytes(runtime, diff);
                rec->appl_ptr = appl_ptr;
        }
@@ -83,6 +83,8 @@ snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
                                  struct snd_pcm_indirect *rec, unsigned int ptr)
 {
        int bytes = ptr - rec->hw_io;
+       int err;
+
        if (bytes < 0)
                bytes += rec->hw_buffer_size;
        rec->hw_io = ptr;
@@ -90,8 +92,11 @@ snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
        rec->sw_io += bytes;
        if (rec->sw_io >= rec->sw_buffer_size)
                rec->sw_io -= rec->sw_buffer_size;
-       if (substream->ops->ack)
-               substream->ops->ack(substream);
+       if (substream->ops->ack) {
+               err = substream->ops->ack(substream);
+               if (err == -EPIPE)
+                       return SNDRV_PCM_POS_XRUN;
+       }
        return bytes_to_frames(substream->runtime, rec->sw_io);
 }
 
@@ -112,7 +117,7 @@ snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
                        diff += runtime->boundary;
                if (diff < 0)
-                       return -EINVAL;
+                       return -EPIPE;
                rec->sw_ready -= frames_to_bytes(runtime, diff);
                rec->appl_ptr = appl_ptr;
        }
@@ -152,6 +157,8 @@ snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
 {
        int qsize;
        int bytes = ptr - rec->hw_io;
+       int err;
+
        if (bytes < 0)
                bytes += rec->hw_buffer_size;
        rec->hw_io = ptr;
@@ -162,8 +169,11 @@ snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
        rec->sw_io += bytes;
        if (rec->sw_io >= rec->sw_buffer_size)
                rec->sw_io -= rec->sw_buffer_size;
-       if (substream->ops->ack)
-               substream->ops->ack(substream);
+       if (substream->ops->ack) {
+               err = substream->ops->ack(substream);
+               if (err == -EPIPE)
+                       return SNDRV_PCM_POS_XRUN;
+       }
        return bytes_to_frames(substream->runtime, rec->sw_io);
 }
 
index 27040b472a4f69dec49c3c2bf68e3f840b67d349..19f564606ac42edc8c437b7e458ca1e935204182 100644 (file)
@@ -378,18 +378,18 @@ struct snd_pcm_runtime {
        unsigned int rate_den;
        unsigned int no_period_wakeup: 1;
 
-       /* -- SW params -- */
-       int tstamp_mode;                /* mmap timestamp is updated */
+       /* -- SW params; see struct snd_pcm_sw_params for comments -- */
+       int tstamp_mode;
        unsigned int period_step;
        snd_pcm_uframes_t start_threshold;
        snd_pcm_uframes_t stop_threshold;
-       snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
-                                               noise is nearest than this */
-       snd_pcm_uframes_t silence_size; /* Silence filling size */
-       snd_pcm_uframes_t boundary;     /* pointers wrap point */
+       snd_pcm_uframes_t silence_threshold;
+       snd_pcm_uframes_t silence_size;
+       snd_pcm_uframes_t boundary;
 
+       /* internal data of auto-silencer */
        snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
-       snd_pcm_uframes_t silence_filled; /* size filled with silence */
+       snd_pcm_uframes_t silence_filled; /* already filled part of silence area */
 
        union snd_pcm_sync_id sync;     /* hardware synchronization ID */
 
index 9d31a5c0db33c2ff36a18b8629430c218884e889..fc94dfb0021fd37c7da3acb6e38e9ee85c5bbf6d 100644 (file)
@@ -9,10 +9,25 @@
 #define __SOC_CARD_H
 
 enum snd_soc_card_subclass {
-       SND_SOC_CARD_CLASS_INIT         = 0,
+       SND_SOC_CARD_CLASS_ROOT         = 0,
        SND_SOC_CARD_CLASS_RUNTIME      = 1,
 };
 
+static inline void snd_soc_card_mutex_lock_root(struct snd_soc_card *card)
+{
+       mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_ROOT);
+}
+
+static inline void snd_soc_card_mutex_lock(struct snd_soc_card *card)
+{
+       mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+}
+
+static inline void snd_soc_card_mutex_unlock(struct snd_soc_card *card)
+{
+       mutex_unlock(&card->mutex);
+}
+
 struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
                                               const char *name);
 int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
index 64915ebd641eecfc5a1e8248ee2afe7fc95cc248..87f8e1793af1587a7a846616a116be92f514b618 100644 (file)
@@ -527,11 +527,6 @@ enum snd_soc_dapm_type {
        SND_SOC_DAPM_TYPE_COUNT
 };
 
-enum snd_soc_dapm_subclass {
-       SND_SOC_DAPM_CLASS_INIT         = 0,
-       SND_SOC_DAPM_CLASS_RUNTIME      = 1,
-};
-
 /*
  * DAPM audio route definition.
  *
index 1e7d09556fe3e9b546a3dd29c6d4277704455206..4d6ac7699833d878b56deba30292af81dcc14669 100644 (file)
@@ -91,7 +91,6 @@ struct snd_soc_dpcm_runtime {
        struct list_head fe_clients;
 
        int users;
-       struct snd_pcm_runtime *runtime;
        struct snd_pcm_hw_params hw_params;
 
        /* state and update */
index e58b43b5da7c05f5114424c5510baa2bf41e1d0f..05004c048dd531478cf63ea85fb94774636b69a5 100644 (file)
@@ -684,8 +684,11 @@ struct snd_soc_dai_link {
 
        int id; /* optional ID for machine driver link identification */
 
-       const struct snd_soc_pcm_stream *params;
-       unsigned int num_params;
+       /*
+        * for Codec2Codec
+        */
+       const struct snd_soc_pcm_stream *c2c_params;
+       unsigned int num_c2c_params;
 
        unsigned int dai_fmt;           /* format to set on init */
 
@@ -1065,7 +1068,7 @@ struct snd_soc_pcm_runtime {
        struct snd_soc_dai_link *dai_link;
        struct snd_pcm_ops ops;
 
-       unsigned int params_select; /* currently selected param for dai link */
+       unsigned int c2c_params_select; /* currently selected c2c_param for dai link */
 
        /* Dynamic PCM BE runtime data */
        struct snd_soc_dpcm_runtime dpcm[SNDRV_PCM_STREAM_LAST + 1];
@@ -1302,8 +1305,9 @@ int snd_soc_of_get_dai_link_cpus(struct device *dev,
                                 struct snd_soc_dai_link *dai_link);
 void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link);
 
-int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
-                           struct snd_soc_dai_link *dai_link);
+int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
+                            struct snd_soc_dai_link *dai_link,
+                            int num_dai_link);
 void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
                                struct snd_soc_pcm_runtime *rtd);
 
@@ -1360,17 +1364,112 @@ extern struct dentry *snd_soc_debugfs_root;
 
 extern const struct dev_pm_ops snd_soc_pm_ops;
 
-/* Helper functions */
-static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm)
+/*
+ *     DAPM helper functions
+ */
+enum snd_soc_dapm_subclass {
+       SND_SOC_DAPM_CLASS_ROOT         = 0,
+       SND_SOC_DAPM_CLASS_RUNTIME      = 1,
+};
+
+static inline void _snd_soc_dapm_mutex_lock_root_c(struct snd_soc_card *card)
+{
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_ROOT);
+}
+
+static inline void _snd_soc_dapm_mutex_lock_c(struct snd_soc_card *card)
+{
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+}
+
+static inline void _snd_soc_dapm_mutex_unlock_c(struct snd_soc_card *card)
+{
+       mutex_unlock(&card->dapm_mutex);
+}
+
+static inline void _snd_soc_dapm_mutex_assert_held_c(struct snd_soc_card *card)
+{
+       lockdep_assert_held(&card->dapm_mutex);
+}
+
+static inline void _snd_soc_dapm_mutex_lock_root_d(struct snd_soc_dapm_context *dapm)
+{
+       _snd_soc_dapm_mutex_lock_root_c(dapm->card);
+}
+
+static inline void _snd_soc_dapm_mutex_lock_d(struct snd_soc_dapm_context *dapm)
+{
+       _snd_soc_dapm_mutex_lock_c(dapm->card);
+}
+
+static inline void _snd_soc_dapm_mutex_unlock_d(struct snd_soc_dapm_context *dapm)
 {
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       _snd_soc_dapm_mutex_unlock_c(dapm->card);
 }
 
-static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
+static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context *dapm)
 {
-       mutex_unlock(&dapm->card->dapm_mutex);
+       _snd_soc_dapm_mutex_assert_held_c(dapm->card);
 }
 
+#define snd_soc_dapm_mutex_lock_root(x) _Generic((x),                  \
+       struct snd_soc_card * :         _snd_soc_dapm_mutex_lock_root_c, \
+       struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_lock_root_d)(x)
+#define snd_soc_dapm_mutex_lock(x) _Generic((x),                       \
+       struct snd_soc_card * :         _snd_soc_dapm_mutex_lock_c,     \
+       struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_lock_d)(x)
+#define snd_soc_dapm_mutex_unlock(x) _Generic((x),                     \
+       struct snd_soc_card * :         _snd_soc_dapm_mutex_unlock_c,   \
+       struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_unlock_d)(x)
+#define snd_soc_dapm_mutex_assert_held(x) _Generic((x),                        \
+       struct snd_soc_card * :         _snd_soc_dapm_mutex_assert_held_c, \
+       struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_assert_held_d)(x)
+
+/*
+ *     PCM helper functions
+ */
+static inline void _snd_soc_dpcm_mutex_lock_c(struct snd_soc_card *card)
+{
+       mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass);
+}
+
+static inline void _snd_soc_dpcm_mutex_unlock_c(struct snd_soc_card *card)
+{
+       mutex_unlock(&card->pcm_mutex);
+}
+
+static inline void _snd_soc_dpcm_mutex_assert_held_c(struct snd_soc_card *card)
+{
+       lockdep_assert_held(&card->pcm_mutex);
+}
+
+static inline void _snd_soc_dpcm_mutex_lock_r(struct snd_soc_pcm_runtime *rtd)
+{
+       _snd_soc_dpcm_mutex_lock_c(rtd->card);
+}
+
+static inline void _snd_soc_dpcm_mutex_unlock_r(struct snd_soc_pcm_runtime *rtd)
+{
+       _snd_soc_dpcm_mutex_unlock_c(rtd->card);
+}
+
+static inline void _snd_soc_dpcm_mutex_assert_held_r(struct snd_soc_pcm_runtime *rtd)
+{
+       _snd_soc_dpcm_mutex_assert_held_c(rtd->card);
+}
+
+#define snd_soc_dpcm_mutex_lock(x) _Generic((x),                       \
+        struct snd_soc_card * :        _snd_soc_dpcm_mutex_lock_c,     \
+        struct snd_soc_pcm_runtime * : _snd_soc_dpcm_mutex_lock_r)(x)
+
+#define snd_soc_dpcm_mutex_unlock(x) _Generic((x),                     \
+        struct snd_soc_card * :        _snd_soc_dpcm_mutex_unlock_c,   \
+        struct snd_soc_pcm_runtime * : _snd_soc_dpcm_mutex_unlock_r)(x)
+
+#define snd_soc_dpcm_mutex_assert_held(x) _Generic((x),                \
+       struct snd_soc_card * :         _snd_soc_dpcm_mutex_assert_held_c, \
+       struct snd_soc_pcm_runtime * :  _snd_soc_dpcm_mutex_assert_held_r)(x)
+
 #include <sound/soc-component.h>
 #include <sound/soc-card.h>
 #include <sound/soc-jack.h>
index 266e66318f9cfd22badc37befa2879b93f9d2fd3..d3c41f87ac3191db251f3ee5bd64ad15ca5be85e 100644 (file)
@@ -21,6 +21,7 @@ struct snd_sof_dev;
 /**
  * enum sof_fw_state - DSP firmware state definitions
  * @SOF_FW_BOOT_NOT_STARTED:   firmware boot is not yet started
+ * @SOF_DSPLESS_MODE:          DSP is not used
  * @SOF_FW_BOOT_PREPARE:       preparing for boot (firmware loading for exaqmple)
  * @SOF_FW_BOOT_IN_PROGRESS:   firmware boot is in progress
  * @SOF_FW_BOOT_FAILED:                firmware boot failed
@@ -31,6 +32,7 @@ struct snd_sof_dev;
  */
 enum sof_fw_state {
        SOF_FW_BOOT_NOT_STARTED = 0,
+       SOF_DSPLESS_MODE,
        SOF_FW_BOOT_PREPARE,
        SOF_FW_BOOT_IN_PROGRESS,
        SOF_FW_BOOT_FAILED,
@@ -130,6 +132,9 @@ struct sof_dev_desc {
        unsigned int ipc_supported_mask;
        enum sof_ipc_type ipc_default;
 
+       /* The platform supports DSPless mode */
+       bool dspless_mode_supported;
+
        /* defaults paths for firmware, library and topology files */
        const char *default_fw_path[SOF_IPC_TYPE_COUNT];
        const char *default_lib_path[SOF_IPC_TYPE_COUNT];
index d31349bf011d3ab6a38645f18a1915e69320e9fd..78568abe26735ed164419ac8ac8aeba5a8e5b1e1 100644 (file)
@@ -176,6 +176,10 @@ enum sof_ipc4_pipeline_state {
 #define SOF_IPC4_GLB_PIPE_EXT_LP_MASK          BIT(0)
 #define SOF_IPC4_GLB_PIPE_EXT_LP(x)            ((x) << SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT)
 
+#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT    20
+#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK     GENMASK(23, 20)
+#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x)       ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT)
+
 /* pipeline set state ipc msg */
 #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT               16
 #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK                GENMASK(23, 16)
@@ -192,6 +196,35 @@ enum sof_ipc4_pipeline_state {
 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16
 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x)    ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
 
+/* chain dma ipc message */
+#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT   0
+#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK    GENMASK(4, 0)
+#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(x)      (((x) << SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT) & \
+                                                SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK)
+
+#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT   8
+#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK    GENMASK(12, 8)
+#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(x)      (((x) << SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT) & \
+                                                SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK)
+
+#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT  16
+#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK   BIT(16)
+#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE(x)     (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT)
+
+#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT    17
+#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK     BIT(17)
+#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE(x)       (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT)
+
+#define SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT       18
+#define SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK                BIT(18)
+#define SOF_IPC4_GLB_CHAIN_DMA_SCS(x)          (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT)
+
+#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT 0
+#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK  GENMASK(24, 0)
+#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(x)           (((x) << \
+                                                    SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT) & \
+                                                   SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK)
+
 enum sof_ipc4_channel_config {
        /* one channel only. */
        SOF_IPC4_CHANNEL_CONFIG_MONO,
index de6810e94abed3e91de3ef42d1eaca0353c1b8d3..0aa955aa82463a63872a037dbd6ef7d3f08dfec1 100644 (file)
@@ -429,9 +429,14 @@ struct snd_pcm_sw_params {
        snd_pcm_uframes_t avail_min;            /* min avail frames for wakeup */
        snd_pcm_uframes_t xfer_align;           /* obsolete: xfer size need to be a multiple */
        snd_pcm_uframes_t start_threshold;      /* min hw_avail frames for automatic start */
-       snd_pcm_uframes_t stop_threshold;       /* min avail frames for automatic stop */
-       snd_pcm_uframes_t silence_threshold;    /* min distance from noise for silence filling */
-       snd_pcm_uframes_t silence_size;         /* silence block size */
+       /*
+        * The following two thresholds alleviate playback buffer underruns; when
+        * hw_avail drops below the threshold, the respective action is triggered:
+        */
+       snd_pcm_uframes_t stop_threshold;       /* - stop playback */
+       snd_pcm_uframes_t silence_threshold;    /* - pre-fill buffer with silence */
+       snd_pcm_uframes_t silence_size;         /* max size of silence pre-fill; when >= boundary,
+                                                * fill played area with silence immediately */
        snd_pcm_uframes_t boundary;             /* pointers wrap point */
        unsigned int proto;                     /* protocol version */
        unsigned int tstamp_type;               /* timestamp type (req. proto >= 2.0.12) */
@@ -570,7 +575,8 @@ struct __snd_pcm_mmap_status64 {
 struct __snd_pcm_mmap_control64 {
        __pad_before_uframe __pad1;
        snd_pcm_uframes_t appl_ptr;      /* RW: appl ptr (0...boundary-1) */
-       __pad_before_uframe __pad2;
+       __pad_before_uframe __pad2;      // This should be __pad_after_uframe, but binary
+                                        // backwards compatibility constraints prevent a fix.
 
        __pad_before_uframe __pad3;
        snd_pcm_uframes_t  avail_min;    /* RW: min available frames for wakeup */
index 1c1f1dd446118ea457dc155ef6b26f6e2c9162fb..c8e131d6da00cee2d174b501007d9b50a0a62356 100644 (file)
@@ -15,9 +15,6 @@
  * ---- FX8010 ----
  */
 
-#define EMU10K1_CARD_CREATIVE                  0x00000000
-#define EMU10K1_CARD_EMUAPS                    0x00000001
-
 #define EMU10K1_FX8010_PCM_COUNT               8
 
 /*
 #define iINTERP  0x0e  /* R = A + (X * (Y - A) >> 31)  ; saturation */
 #define iSKIP    0x0f  /* R = A (cc_reg), X (count), Y (cc_test) */
 
+#define LOWORD_OPX_MASK                0x000ffc00      /* Instruction operand X                        */
+#define LOWORD_OPY_MASK                0x000003ff      /* Instruction operand Y                        */
+#define HIWORD_OPCODE_MASK     0x00f00000      /* Instruction opcode                           */
+#define HIWORD_RESULT_MASK     0x000ffc00      /* Instruction result                           */
+#define HIWORD_OPA_MASK                0x000003ff      /* Instruction operand A                        */
+
+/* Audigy Soundcards have a different instruction format */
+#define A_LOWORD_OPX_MASK      0x007ff000
+#define A_LOWORD_OPY_MASK      0x000007ff
+#define A_HIWORD_OPCODE_MASK   0x0f000000
+#define A_HIWORD_RESULT_MASK   0x007ff000
+#define A_HIWORD_OPA_MASK      0x000007ff
+
 /* GPRs */
 #define FXBUS(x)       (0x00 + (x))    /* x = 0x00 - 0x0f */
 #define EXTIN(x)       (0x10 + (x))    /* x = 0x00 - 0x0f */
 #define FXBUS2(x)      (0x30 + (x))    /* x = 0x00 - 0x0f copies of fx buses for capture -> FXWC high 16 bits */
                                        /* NB: 0x31 and 0x32 are shared with Center/LFE on SB live 5.1 */
 
+#define A_FXBUS(x)     (0x00 + (x))    /* x = 0x00 - 0x3f FX buses */
+#define A_EXTIN(x)     (0x40 + (x))    /* x = 0x00 - 0x0f physical ins */
+#define A_P16VIN(x)    (0x50 + (x))    /* x = 0x00 - 0x0f p16v ins (A2 only) "EMU32 inputs" */
+#define A_EXTOUT(x)    (0x60 + (x))    /* x = 0x00 - 0x1f physical outs -> A_FXWC1 0x79-7f unknown   */
+#define A_FXBUS2(x)    (0x80 + (x))    /* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */
+#define A_EMU32OUTH(x) (0xa0 + (x))    /* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" */
+#define A_EMU32OUTL(x) (0xb0 + (x))    /* x = 0x00 - 0x0f "EMU32_OUT_01 - _0F" */
+#define A3_EMU32IN(x)  (0x160 + (x))   /* x = 0x00 - 0x1f "EMU32_IN_00 - _1F" - Only when .device = 0x0008 */
+#define A3_EMU32OUT(x) (0x1E0 + (x))   /* x = 0x00 - 0x1f "EMU32_OUT_00 - _1F" - Only when .device = 0x0008 */
+
 #define C_00000000     0x40
 #define C_00000001     0x41
 #define C_00000002     0x42
 #define GPR_NOISE1     0x59            /* noise source */
 #define GPR_IRQ                0x5a            /* IRQ register */
 #define GPR_DBAC       0x5b            /* TRAM Delay Base Address Counter */
+
+/* Audigy constants */
+#define A_C_00000000   0xc0
+#define A_C_00000001   0xc1
+#define A_C_00000002   0xc2
+#define A_C_00000003   0xc3
+#define A_C_00000004   0xc4
+#define A_C_00000008   0xc5
+#define A_C_00000010   0xc6
+#define A_C_00000020   0xc7
+#define A_C_00000100   0xc8
+#define A_C_00010000   0xc9
+#define A_C_00000800   0xca
+#define A_C_10000000   0xcb
+#define A_C_20000000   0xcc
+#define A_C_40000000   0xcd
+#define A_C_80000000   0xce
+#define A_C_7fffffff   0xcf
+#define A_C_ffffffff   0xd0
+#define A_C_fffffffe   0xd1
+#define A_C_c0000000   0xd2
+#define A_C_4f1bbcdc   0xd3
+#define A_C_5a7ef9db   0xd4
+#define A_C_00100000   0xd5
+#define A_GPR_ACCU     0xd6            /* ACCUM, accumulator */
+#define A_GPR_COND     0xd7            /* CCR, condition register */
+#define A_GPR_NOISE0   0xd8            /* noise source */
+#define A_GPR_NOISE1   0xd9            /* noise source */
+#define A_GPR_IRQ      0xda            /* IRQ register */
+#define A_GPR_DBAC     0xdb            /* TRAM Delay Base Address Counter - internal */
+#define A_GPR_DBACE    0xde            /* TRAM Delay Base Address Counter - external */
+
+/* Each FX general purpose register is 32 bits in length, all bits are used                    */
+#define FXGPREGBASE            0x100           /* FX general purpose registers base            */
+#define A_FXGPREGBASE          0x400           /* Audigy GPRs, 0x400 to 0x5ff                  */
+
+#define A_TANKMEMCTLREGBASE    0x100           /* Tank memory control registers base - only for Audigy */
+#define A_TANKMEMCTLREG_MASK   0x1f            /* only 5 bits used - only for Audigy */
+
+/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */
+/* decompressed back to 20 bits on a read.  There are a total of 160 locations, the last 32    */
+/* locations are for external TRAM.                                                            */
+#define TANKMEMDATAREGBASE     0x200           /* Tank memory data registers base              */
+#define TANKMEMDATAREG_MASK    0x000fffff      /* 20 bit tank audio data field                 */
+
+/* Combined address field and memory opcode or flag field.  160 locations, last 32 are external        */
+#define TANKMEMADDRREGBASE     0x300           /* Tank memory address registers base           */
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff    /* 20 bit tank address field                    */
+#define TANKMEMADDRREG_CLEAR   0x00800000      /* Clear tank memory                            */
+#define TANKMEMADDRREG_ALIGN   0x00400000      /* Align read or write relative to tank access  */
+#define TANKMEMADDRREG_WRITE   0x00200000      /* Write to tank memory                         */
+#define TANKMEMADDRREG_READ    0x00100000      /* Read from tank memory                        */
+
 #define GPR(x)         (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */
 #define ITRAM_DATA(x)  (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
 #define ETRAM_DATA(x)  (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
 #define ITRAM_ADDR(x)  (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
 #define ETRAM_ADDR(x)  (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
 
+#define A_GPR(x)       (A_FXGPREGBASE + (x))
 #define A_ITRAM_DATA(x)        (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
 #define A_ETRAM_DATA(x)        (TANKMEMDATAREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */
 #define A_ITRAM_ADDR(x)        (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
 #define A_ITRAM_CTL(x) (A_TANKMEMCTLREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
 #define A_ETRAM_CTL(x) (A_TANKMEMCTLREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */
 
-#define A_FXBUS(x)     (0x00 + (x))    /* x = 0x00 - 0x3f FX buses */
-#define A_EXTIN(x)     (0x40 + (x))    /* x = 0x00 - 0x0f physical ins */
-#define A_P16VIN(x)    (0x50 + (x))    /* x = 0x00 - 0x0f p16v ins (A2 only) "EMU32 inputs" */
-#define A_EXTOUT(x)    (0x60 + (x))    /* x = 0x00 - 0x1f physical outs -> A_FXWC1 0x79-7f unknown   */
-#define A_FXBUS2(x)    (0x80 + (x))    /* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */
-#define A_EMU32OUTH(x) (0xa0 + (x))    /* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" - ??? */
-#define A_EMU32OUTL(x) (0xb0 + (x))    /* x = 0x00 - 0x0f "EMU32_OUT_1 - _F" - ??? */
-#define A3_EMU32IN(x)  (0x160 + (x))   /* x = 0x00 - 0x3f "EMU32_IN_00 - _3F" - Only when .device = 0x0008 */
-#define A3_EMU32OUT(x) (0x1E0 + (x))   /* x = 0x00 - 0x0f "EMU32_OUT_00 - _3F" - Only when .device = 0x0008 */
-#define A_GPR(x)       (A_FXGPREGBASE + (x))
-
 /* cc_reg constants */
 #define CC_REG_NORMALIZED C_00000001
 #define CC_REG_BORROW  C_00000002
 #define CC_REG_SATURATE        C_00000010
 #define CC_REG_NONZERO C_00000100
 
+#define A_CC_REG_NORMALIZED    A_C_00000001
+#define A_CC_REG_BORROW                A_C_00000002
+#define A_CC_REG_MINUS         A_C_00000004
+#define A_CC_REG_ZERO          A_C_00000008
+#define A_CC_REG_SATURATE      A_C_00000010
+#define A_CC_REG_NONZERO       A_C_00000100
+
 /* FX buses */
+// These are arbitrary mappings; our DSP code simply expects
+// the config files to route the channels this way.
+// The numbers are documented in {audigy,sb-live}-mixer.rst.
 #define FXBUS_PCM_LEFT         0x00
 #define FXBUS_PCM_RIGHT                0x01
 #define FXBUS_PCM_LEFT_REAR    0x02
 #define A_EXTOUT_ADC_CAP_R     0x17    /*                    right */
 #define A_EXTOUT_MIC_CAP       0x18    /* Mic capture buffer */
 
-/* Audigy constants */
-#define A_C_00000000   0xc0
-#define A_C_00000001   0xc1
-#define A_C_00000002   0xc2
-#define A_C_00000003   0xc3
-#define A_C_00000004   0xc4
-#define A_C_00000008   0xc5
-#define A_C_00000010   0xc6
-#define A_C_00000020   0xc7
-#define A_C_00000100   0xc8
-#define A_C_00010000   0xc9
-#define A_C_00000800   0xca
-#define A_C_10000000   0xcb
-#define A_C_20000000   0xcc
-#define A_C_40000000   0xcd
-#define A_C_80000000   0xce
-#define A_C_7fffffff   0xcf
-#define A_C_ffffffff   0xd0
-#define A_C_fffffffe   0xd1
-#define A_C_c0000000   0xd2
-#define A_C_4f1bbcdc   0xd3
-#define A_C_5a7ef9db   0xd4
-#define A_C_00100000   0xd5
-#define A_GPR_ACCU     0xd6            /* ACCUM, accumulator */
-#define A_GPR_COND     0xd7            /* CCR, condition register */
-#define A_GPR_NOISE0   0xd8            /* noise source */
-#define A_GPR_NOISE1   0xd9            /* noise source */
-#define A_GPR_IRQ      0xda            /* IRQ register */
-#define A_GPR_DBAC     0xdb            /* TRAM Delay Base Address Counter - internal */
-#define A_GPR_DBACE    0xde            /* TRAM Delay Base Address Counter - external */
-
-/* definitions for debug register */
+/* Definitions for debug register. Note that these are for emu10k1 ONLY. */
 #define EMU10K1_DBG_ZC                 0x80000000      /* zero tram counter */
 #define EMU10K1_DBG_SATURATION_OCCURED 0x02000000      /* saturation control */
 #define EMU10K1_DBG_SATURATION_ADDR    0x01ff0000      /* saturation address */
 #define EMU10K1_DBG_CONDITION_CODE     0x00003e00      /* condition code */
 #define EMU10K1_DBG_SINGLE_STEP_ADDR   0x000001ff      /* single step address */
 
-/* tank memory address line */
-#ifndef __KERNEL__
-#define TANKMEMADDRREG_ADDR_MASK 0x000fffff    /* 20 bit tank address field                    */
-#define TANKMEMADDRREG_CLEAR    0x00800000     /* Clear tank memory                            */
-#define TANKMEMADDRREG_ALIGN    0x00400000     /* Align read or write relative to tank access  */
-#define TANKMEMADDRREG_WRITE    0x00200000     /* Write to tank memory                         */
-#define TANKMEMADDRREG_READ     0x00100000     /* Read from tank memory                        */
-#endif
+/* Definitions for emu10k2 debug register. */
+#define A_DBG_ZC                       0x40000000      /* zero tram counter */
+#define A_DBG_SATURATION_OCCURED       0x20000000
+#define A_DBG_SATURATION_ADDR          0x0ffc0000
+#define A_DBG_SINGLE_STEP              0x00020000      /* Set to zero to start dsp */
+#define A_DBG_STEP                     0x00010000
+#define A_DBG_CONDITION_CODE           0x0000f800
+#define A_DBG_STEP_ADDR                        0x000003ff
 
 struct snd_emu10k1_fx8010_info {
        unsigned int internal_tram_size;        /* in samples */
index 3566630ca965b05bffc938f78c77a6206c02a18e..45c657c3919e688d85c96b1d2710030e4fa9e4d2 100644 (file)
@@ -60,5 +60,7 @@
 
 /* SOF ABI magic number "SOF\0". */
 #define SOF_ABI_MAGIC          0x00464F53
+/* SOF IPC4 ABI magic number "SOF4". */
+#define SOF_IPC4_ABI_MAGIC     0x34464F53
 
 #endif
index e9bba93a53990b4f32ad148ea8f51295fff3e3d5..cb3c1ace69e3191f5143f4f766c03180ff8770d5 100644 (file)
 
 #include <linux/types.h>
 
-/*
- * Header for all non IPC ABI data.
+/**
+ * struct sof_abi_hdr - Header for all non IPC ABI data.
+ * @magic: Magic number for validation
+ *        for IPC3 data: 0x00464F53 ('S', 'O', 'F', '\0')
+ *        for IPC4 data: 0x34464F53 ('S', 'O', 'F', '4')
+ * @type: module specific parameter
+ *       for IPC3: Component specific type
+ *       for IPC4: parameter ID (param_id) of the data
+ * @size: The size in bytes of the data, excluding this struct
+ * @abi: SOF ABI version. The version is valid in scope of the 'magic', IPC3 and
+ *      IPC4 ABI version numbers have no relationship.
+ * @reserved: Reserved for future use
+ * @data: Component data - opaque to core
  *
  * Identifies data type, size and ABI.
  * Used by any bespoke component data structures or binary blobs.
  */
 struct sof_abi_hdr {
-       __u32 magic;            /**< 'S', 'O', 'F', '\0' */
-       __u32 type;             /**< component specific type */
-       __u32 size;             /**< size in bytes of data excl. this struct */
-       __u32 abi;              /**< SOF ABI version */
-       __u32 reserved[4];      /**< reserved for future use */
-       __u32 data[];           /**< Component data - opaque to core */
+       __u32 magic;
+       __u32 type;
+       __u32 size;
+       __u32 abi;
+       __u32 reserved[4];
+       __u32 data[];
 }  __packed;
 
 #define SOF_MANIFEST_DATA_TYPE_NHLT 1
index bacaf8a6317e3e267256eff26b45fb881222e88a..bbc37877aaff551493bd17c493c8cc532d7068d5 100644 (file)
@@ -54,6 +54,7 @@
 #define SOF_TKN_SCHED_DYNAMIC_PIPELINE         206
 #define SOF_TKN_SCHED_LP_MODE                  207
 #define SOF_TKN_SCHED_MEM_USAGE                        208
+#define SOF_TKN_SCHED_USE_CHAIN_DMA            209
 
 /* volume */
 #define SOF_TKN_VOLUME_RAMP_STEP_TYPE          250
 #define SOF_TKN_COMP_CPC                       406
 #define SOF_TKN_COMP_IS_PAGES                  409
 #define SOF_TKN_COMP_NUM_AUDIO_FORMATS         410
-#define SOF_TKN_COMP_NUM_SINK_PINS             411
-#define SOF_TKN_COMP_NUM_SOURCE_PINS           412
+#define SOF_TKN_COMP_NUM_INPUT_PINS            411
+#define SOF_TKN_COMP_NUM_OUTPUT_PINS           412
 /*
- * The token for sink/source pin binding, it specifies the widget
- * name that the sink/source pin is connected from/to.
+ * The token for input/output pin binding, it specifies the widget
+ * name that the input/output pin is connected from/to.
  */
-#define SOF_TKN_COMP_SINK_PIN_BINDING_WNAME    413
-#define SOF_TKN_COMP_SRC_PIN_BINDING_WNAME     414
+#define SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME   413
+#define SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME  414
+#define SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS   415
+#define SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS  416
 
 
 /* SSP */
 /* CAVS AUDIO FORMAT */
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE      1900
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH 1901
-#define SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID_BIT 1902
+#define SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID_BIT_DEPTH   1902
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CHANNELS  1903
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP    1904
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG    1905
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE        1906
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG   1907
 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_SAMPLE_TYPE       1908
+#define SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX            1909
 /* intentional token numbering discontinuity, reserved for future use */
 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE     1930
 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH        1931
-#define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID_BIT        1932
+#define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID_BIT_DEPTH 1932
 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CHANNELS 1933
 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP   1934
 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG   1935
 
 /* COPIER */
 #define SOF_TKN_INTEL_COPIER_NODE_TYPE         1980
+#define SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS        1981
 
 /* ACP I2S */
 #define SOF_TKN_AMD_ACPI2S_RATE                        1700
index b4685c53ff114acbb79baf8fc23d825875bfb56d..c7aee8c42c554a326f0d1736511b4fb271e22eab 100644 (file)
@@ -75,19 +75,8 @@ int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
 }
 EXPORT_SYMBOL_GPL(snd_ac97_reset);
 
-/*
- * Let drivers decide whether they want to support given codec from their
- * probe method. Drivers have direct access to the struct snd_ac97
- * structure and may  decide based on the id field amongst other things.
- */
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
-{
-       return 1;
-}
-
 struct bus_type ac97_bus_type = {
        .name           = "ac97",
-       .match          = ac97_bus_match,
 };
 
 static int __init ac97_bus_init(void)
index c162086455ad59a6ecb3ca5558cb422f02e5d97d..2d83ad91f96800538fe0b4aea9e16d4274d0b03d 100644 (file)
@@ -262,7 +262,7 @@ err_dev:
        return ret;
 }
 
-static int pxa2xx_ac97_remove(struct platform_device *dev)
+static void pxa2xx_ac97_remove(struct platform_device *dev)
 {
        struct snd_card *card = platform_get_drvdata(dev);
 
@@ -270,13 +270,11 @@ static int pxa2xx_ac97_remove(struct platform_device *dev)
                snd_card_free(card);
                pxa2xx_ac97_hw_remove(dev);
        }
-
-       return 0;
 }
 
 static struct platform_driver pxa2xx_ac97_driver = {
        .probe          = pxa2xx_ac97_probe,
-       .remove         = pxa2xx_ac97_remove,
+       .remove_new     = pxa2xx_ac97_remove,
        .driver         = {
                .name   = "pxa2xx-ac97",
 #ifdef CONFIG_PM_SLEEP
index 66ecbd4d034e805263359760aa7796893b4649cd..c8912b8a1dc5b5ab2c1640cf9cf3d847295a162e 100644 (file)
@@ -843,7 +843,7 @@ static SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume
 #define ATMEL_AC97C_PM_OPS     NULL
 #endif
 
-static int atmel_ac97c_remove(struct platform_device *pdev)
+static void atmel_ac97c_remove(struct platform_device *pdev)
 {
        struct snd_card *card = platform_get_drvdata(pdev);
        struct atmel_ac97c *chip = get_chip(card);
@@ -858,13 +858,11 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
        free_irq(chip->irq, chip);
 
        snd_card_free(card);
-
-       return 0;
 }
 
 static struct platform_driver atmel_ac97c_driver = {
        .probe          = atmel_ac97c_probe,
-       .remove         = atmel_ac97c_remove,
+       .remove_new     = atmel_ac97c_remove,
        .driver         = {
                .name   = "atmel_ac97c",
                .pm     = ATMEL_AC97C_PM_OPS,
index 02fd65993e7e52e269b6a7fec59d48d3e5e5fda4..d21c73944efdeecaca7ddc86dd9fd0facd283638 100644 (file)
@@ -42,70 +42,56 @@ static int fill_silence_frames(struct snd_pcm_substream *substream,
  *
  * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
  */
-void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
+void snd_pcm_playback_silence(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       snd_pcm_uframes_t frames, ofs, transfer;
+       snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
+       snd_pcm_sframes_t added, hw_avail, frames;
+       snd_pcm_uframes_t noise_dist, ofs, transfer;
        int err;
 
+       added = appl_ptr - runtime->silence_start;
+       if (added) {
+               if (added < 0)
+                       added += runtime->boundary;
+               if (added < runtime->silence_filled)
+                       runtime->silence_filled -= added;
+               else
+                       runtime->silence_filled = 0;
+               runtime->silence_start = appl_ptr;
+       }
+
+       // This will "legitimately" turn negative on underrun, and will be mangled
+       // into a huge number by the boundary crossing handling. The initial state
+       // might also be not quite sane. The code below MUST account for these cases.
+       hw_avail = appl_ptr - runtime->status->hw_ptr;
+       if (hw_avail < 0)
+               hw_avail += runtime->boundary;
+
+       noise_dist = hw_avail + runtime->silence_filled;
        if (runtime->silence_size < runtime->boundary) {
-               snd_pcm_sframes_t noise_dist, n;
-               snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
-               if (runtime->silence_start != appl_ptr) {
-                       n = appl_ptr - runtime->silence_start;
-                       if (n < 0)
-                               n += runtime->boundary;
-                       if ((snd_pcm_uframes_t)n < runtime->silence_filled)
-                               runtime->silence_filled -= n;
-                       else
-                               runtime->silence_filled = 0;
-                       runtime->silence_start = appl_ptr;
-               }
-               if (runtime->silence_filled >= runtime->buffer_size)
-                       return;
-               noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
-               if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
-                       return;
                frames = runtime->silence_threshold - noise_dist;
+               if (frames <= 0)
+                       return;
                if (frames > runtime->silence_size)
                        frames = runtime->silence_size;
        } else {
-               if (new_hw_ptr == ULONG_MAX) {  /* initialization */
-                       snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
-                       if (avail > runtime->buffer_size)
-                               avail = runtime->buffer_size;
-                       runtime->silence_filled = avail > 0 ? avail : 0;
-                       runtime->silence_start = (runtime->status->hw_ptr +
-                                                 runtime->silence_filled) %
-                                                runtime->boundary;
-               } else {
-                       ofs = runtime->status->hw_ptr;
-                       frames = new_hw_ptr - ofs;
-                       if ((snd_pcm_sframes_t)frames < 0)
-                               frames += runtime->boundary;
-                       runtime->silence_filled -= frames;
-                       if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
-                               runtime->silence_filled = 0;
-                               runtime->silence_start = new_hw_ptr;
-                       } else {
-                               runtime->silence_start = ofs;
-                       }
-               }
-               frames = runtime->buffer_size - runtime->silence_filled;
+               frames = runtime->buffer_size - noise_dist;
+               if (frames <= 0)
+                       return;
        }
+
        if (snd_BUG_ON(frames > runtime->buffer_size))
                return;
-       if (frames == 0)
-               return;
-       ofs = runtime->silence_start % runtime->buffer_size;
-       while (frames > 0) {
+       ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
+       do {
                transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
                err = fill_silence_frames(substream, ofs, transfer);
                snd_BUG_ON(err < 0);
                runtime->silence_filled += transfer;
                frames -= transfer;
                ofs = 0;
-       }
+       } while (frames > 0);
        snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
 }
 
@@ -439,10 +425,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                return 0;
        }
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-           runtime->silence_size > 0)
-               snd_pcm_playback_silence(substream, new_hw_ptr);
-
        if (in_interrupt) {
                delta = new_hw_ptr - runtime->hw_ptr_interrupt;
                if (delta < 0)
@@ -460,6 +442,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                runtime->hw_ptr_wrap += runtime->boundary;
        }
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           runtime->silence_size > 0)
+               snd_pcm_playback_silence(substream);
+
        update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
 
        return snd_pcm_update_state(substream, runtime);
@@ -1878,15 +1864,14 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
                if (substream->wait_time) {
                        wait_time = substream->wait_time;
                } else {
-                       wait_time = 10;
+                       wait_time = 100;
 
                        if (runtime->rate) {
-                               long t = runtime->period_size * 2 /
-                                        runtime->rate;
+                               long t = runtime->buffer_size * 1100 / runtime->rate;
                                wait_time = max(t, wait_time);
                        }
-                       wait_time = msecs_to_jiffies(wait_time * 1000);
                }
+               wait_time = msecs_to_jiffies(wait_time);
        }
 
        for (;;) {
@@ -1934,8 +1919,8 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
                }
                if (!tout) {
                        pcm_dbg(substream->pcm,
-                               "%s write error (DMA or IRQ trouble?)\n",
-                               is_playback ? "playback" : "capture");
+                               "%s timeout (DMA or IRQ trouble?)\n",
+                               is_playback ? "playback write" : "capture read");
                        err = -EIO;
                        break;
                }
index ecb21697ae3a4d66931d85fc13d6eac89b32ddec..42fe3a4e9154f20395a6424dd3c09c2478e96c2a 100644 (file)
@@ -29,8 +29,7 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
                         struct snd_pcm_runtime *runtime);
 int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
 
-void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
-                             snd_pcm_uframes_t new_hw_ptr);
+void snd_pcm_playback_silence(struct snd_pcm_substream *substream);
 
 static inline snd_pcm_uframes_t
 snd_pcm_avail(struct snd_pcm_substream *substream)
index 5868661d461bc85c8ad2abfd60e4533fa04cd680..91c87cdb786e81c9f91ded5b0e61af41c950aba8 100644 (file)
@@ -958,7 +958,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        if (snd_pcm_running(substream)) {
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
                    runtime->silence_size > 0)
-                       snd_pcm_playback_silence(substream, ULONG_MAX);
+                       snd_pcm_playback_silence(substream);
                err = snd_pcm_update_state(substream, runtime);
        }
        snd_pcm_stream_unlock_irq(substream);
@@ -1455,7 +1455,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream,
        __snd_pcm_set_state(runtime, state);
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
-               snd_pcm_playback_silence(substream, ULONG_MAX);
+               snd_pcm_playback_silence(substream);
        snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
 }
 
@@ -1916,7 +1916,7 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *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_playback_silence(substream);
        snd_pcm_stream_unlock_irq(substream);
 }
 
@@ -2159,12 +2159,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
                if (runtime->no_period_wakeup)
                        tout = MAX_SCHEDULE_TIMEOUT;
                else {
-                       tout = 10;
+                       tout = 100;
                        if (runtime->rate) {
-                               long t = runtime->period_size * 2 / runtime->rate;
+                               long t = runtime->buffer_size * 1100 / runtime->rate;
                                tout = max(t, tout);
                        }
-                       tout = msecs_to_jiffies(tout * 1000);
+                       tout = msecs_to_jiffies(tout);
                }
                tout = schedule_timeout(tout);
 
@@ -2187,7 +2187,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
                                result = -ESTRPIPE;
                        else {
                                dev_dbg(substream->pcm->card->dev,
-                                       "playback drain error (DMA or IRQ trouble?)\n");
+                                       "playback drain timeout (DMA or IRQ trouble?)\n");
                                snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
                                result = -EIO;
                        }
index f0d34cf70c3e0166bca9be7835470a47222c5a30..5cfd0e99a13f5b093bf60ab6b09a02f8ed8c4d24 100644 (file)
@@ -999,19 +999,17 @@ __err:
        return err;
 }
 
-static int snd_mts64_remove(struct platform_device *pdev)
+static void snd_mts64_remove(struct platform_device *pdev)
 {
        struct snd_card *card = platform_get_drvdata(pdev);
 
        if (card)
                snd_card_free(card);
-
-       return 0;
 }
 
 static struct platform_driver snd_mts64_driver = {
        .probe  = snd_mts64_probe,
-       .remove = snd_mts64_remove,
+       .remove_new = snd_mts64_remove,
        .driver = {
                .name = PLATFORM_DRIVER,
        }
index 52a656735365e0498d23ea6c709c6fb225eb2350..619e3f5944772cee06e1a8e7825ddd98bbda0c66 100644 (file)
@@ -182,21 +182,11 @@ static inline void portman_write_command(struct portman *pm, u8 value)
        parport_write_control(pm->pardev->port, value);
 }
 
-static inline u8 portman_read_command(struct portman *pm)
-{
-       return parport_read_control(pm->pardev->port);
-}
-
 static inline u8 portman_read_status(struct portman *pm)
 {
        return parport_read_status(pm->pardev->port);
 }
 
-static inline u8 portman_read_data(struct portman *pm)
-{
-       return parport_read_data(pm->pardev->port);
-}
-
 static inline void portman_write_data(struct portman *pm, u8 value)
 {
        parport_write_data(pm->pardev->port, value);
@@ -795,20 +785,18 @@ __err:
        return err;
 }
 
-static int snd_portman_remove(struct platform_device *pdev)
+static void snd_portman_remove(struct platform_device *pdev)
 {
        struct snd_card *card = platform_get_drvdata(pdev);
 
        if (card)
                snd_card_free(card);
-
-       return 0;
 }
 
 
 static struct platform_driver snd_portman_driver = {
        .probe  = snd_portman_probe,
-       .remove = snd_portman_remove,
+       .remove_new = snd_portman_remove,
        .driver = {
                .name = PLATFORM_DRIVER,
        }
index 9ac9b58d7c8cdd94e0929d03153dc71ae795b4d5..3c26334227bb49ea9f8aaae8d101a3fcdf4c7a84 100644 (file)
@@ -877,17 +877,16 @@ static int hal2_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int hal2_remove(struct platform_device *pdev)
+static void hal2_remove(struct platform_device *pdev)
 {
        struct snd_card *card = platform_get_drvdata(pdev);
 
        snd_card_free(card);
-       return 0;
 }
 
 static struct platform_driver hal2_driver = {
        .probe  = hal2_probe,
-       .remove = hal2_remove,
+       .remove_new = hal2_remove,
        .driver = {
                .name   = "sgihal2",
        }
index 989f656e2de7dbf6f5ca6350e35d5a5d26737bb0..a8551ccdd1bf8a0792ae86fa75592a3afee7e016 100644 (file)
@@ -908,17 +908,16 @@ static int snd_sgio2audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int snd_sgio2audio_remove(struct platform_device *pdev)
+static void snd_sgio2audio_remove(struct platform_device *pdev)
 {
        struct snd_card *card = platform_get_drvdata(pdev);
 
        snd_card_free(card);
-       return 0;
 }
 
 static struct platform_driver sgio2audio_driver = {
        .probe  = snd_sgio2audio_probe,
-       .remove = snd_sgio2audio_remove,
+       .remove_new = snd_sgio2audio_remove,
        .driver = {
                .name   = "sgio2audio",
        }
index a558362254019a9e010926029ff0a9fb3d4c643a..861958451ef56d15e02ab486241d9cdefb75534f 100644 (file)
@@ -461,7 +461,7 @@ config SND_INDIGODJX
          will be called snd-indigodjx
 
 config SND_EMU10K1
-       tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
+       tristate "Emu10k1 (SB Live!, Audigy, E-MU APS/0404/1010/1212/1616/1820)"
        select FW_LOADER
        select SND_HWDEP
        select SND_RAWMIDI
@@ -471,7 +471,7 @@ config SND_EMU10K1
        depends on ZONE_DMA
        help
          Say Y to include support for Sound Blaster PCI 512, Live!,
-         Audigy and E-mu APS (partially supported) soundcards.
+         Audigy and E-MU APS/0404/1010/1212/1616/1820 soundcards.
 
          The confusing multitude of mixer controls is documented in
          <file:Documentation/sound/cards/sb-live-mixer.rst> and
index 88d902997b7491203858dedcf392b1e7df2c4719..72aa135d69f85e9f8ef2733e582ebbc5eee36533 100644 (file)
@@ -1253,7 +1253,6 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
        int local_count = count;
        int xfer_size;
        u32 *pdata = dest;
-       u32 loop_count = 0;
 
        while (local_count) {
                if (local_count > c6711_burst_size)
@@ -1273,7 +1272,6 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
                pdata += xfer_size;
                local_hpi_address += sizeof(u32) * xfer_size;
                local_count -= xfer_size;
-               loop_count++;
        }
 
        if (time_out)
index 672af4b9597bbbbd4ba506b7bc9bced485c1ddaf..b8163f26004ab32b5fa6e9299d441b0ac178bbd5 100644 (file)
@@ -68,17 +68,6 @@ static const struct pci_device_id snd_emu10k1_ids[] = {
        { 0, }
 };
 
-/*
- * Audigy 2 Value notes:
- * A_IOCFG Input (GPIO)
- * 0x400  = Front analog jack plugged in. (Green socket)
- * 0x1000 = Read analog jack plugged in. (Black socket)
- * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
- * A_IOCFG Output (GPIO)
- * 0x60 = Sound out of front Left.
- * Win sets it to 0xXX61
- */
-
 MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
 
 static int snd_card_emu10k1_probe(struct pci_dev *pci,
index dba1e9fc2eecf78b0ad717520f3e0dd8181917b1..c6d15257518194ec128dc4166b20aa268d0133b2 100644 (file)
@@ -531,8 +531,5 @@ set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
 static void
 set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
 {
-       unsigned int val;
-       val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
-       val |= (vp->reg.parm.filterQ << 28);
-       snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+       snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
 }
index 3880f359e688933f532aa6133fd88c2048b67db3..3abdaf1b96244d5c90e0c6b8c42e3728393e5ba8 100644 (file)
@@ -140,7 +140,7 @@ static const unsigned int i2c_adc_init[][2] = {
        { 0x15, ADC_MUX_2 },  /* ADC Mixer control. Mic for A2ZS Notebook */
 };
 
-static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir)
 {
        unsigned int silent_page;
        int ch;
@@ -162,6 +162,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        outl(0, emu->port + INTE);
        snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
        snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+
+       /* disable stop on loop end */
        snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
        snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
 
@@ -181,13 +183,11 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
        snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
 
-       if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+       if (emu->card_capabilities->emu_model) {
+       } else if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
                /* Hacks for Alice3 to work independent of haP16V driver */
                /* Setup SRCMulti_I2S SamplingRate */
-               tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
-               tmp &= 0xfffff1ff;
-               tmp |= (0x2<<9);
-               snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+               snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
 
                /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
                snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
@@ -199,32 +199,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                outl(0x0201, emu->port + HCFG2);
                /* Set playback routing. */
                snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
-       }
-       if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+       } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
                /* Hacks for Alice3 to work independent of haP16V driver */
                dev_info(emu->card->dev, "Audigy2 value: Special config.\n");
                /* Setup SRCMulti_I2S SamplingRate */
-               tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
-               tmp &= 0xfffff1ff;
-               tmp |= (0x2<<9);
-               snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+               snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
 
                /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
-               outl(0x600000, emu->port + 0x20);
-               outl(0x14, emu->port + 0x24);
+               snd_emu10k1_ptr20_write(emu, P17V_SRCSel, 0, 0x14);
 
                /* Setup SRCMulti Input Audio Enable */
-               outl(0x7b0000, emu->port + 0x20);
-               outl(0xFF000000, emu->port + 0x24);
+               snd_emu10k1_ptr20_write(emu, P17V_MIXER_I2S_ENABLE, 0, 0xFF000000);
 
                /* Setup SPDIF Out Audio Enable */
                /* The Audigy 2 Value has a separate SPDIF out,
                 * so no need for a mixer switch
                 */
-               outl(0x7a0000, emu->port + 0x20);
-               outl(0xFF000000, emu->port + 0x24);
-               tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
-               outl(tmp, emu->port + A_IOCFG);
+               snd_emu10k1_ptr20_write(emu, P17V_MIXER_SPDIF_ENABLE, 0, 0xFF000000);
+
+               tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+               outw(tmp, emu->port + A_IOCFG);
        }
        if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
                int size, n;
@@ -244,15 +238,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                 * GPIO6: Unknown
                 * GPIO7: Unknown
                 */
-               outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+               outw(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
        }
        if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
                int size, n;
 
                snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
-               tmp = inl(emu->port + A_IOCFG);
-               outl(tmp | 0x4, emu->port + A_IOCFG);  /* Set bit 2 for mic input */
-               tmp = inl(emu->port + A_IOCFG);
+               tmp = inw(emu->port + A_IOCFG);
+               outw(tmp | 0x4, emu->port + A_IOCFG);  /* Set bit 2 for mic input */
+               tmp = inw(emu->port + A_IOCFG);
                size = ARRAY_SIZE(i2c_adc_init);
                for (n = 0; n < size; n++)
                        snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
@@ -308,12 +302,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                } else if (emu->card_capabilities->i2c_adc) {
                        ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
                } else if (emu->audigy) {
-                       unsigned int reg = inl(emu->port + A_IOCFG);
-                       outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+                       u16 reg = inw(emu->port + A_IOCFG);
+                       outw(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
                        udelay(500);
-                       outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+                       outw(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
                        udelay(100);
-                       outl(reg, emu->port + A_IOCFG);
+                       outw(reg, emu->port + A_IOCFG);
                } else {
                        unsigned int reg = inl(emu->port + HCFG);
                        outl(reg | HCFG_GPOUT2, emu->port + HCFG);
@@ -329,8 +323,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        } else if (emu->card_capabilities->i2c_adc) {
                ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
        } else if (emu->audigy) {       /* enable analog output */
-               unsigned int reg = inl(emu->port + A_IOCFG);
-               outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+               u16 reg = inw(emu->port + A_IOCFG);
+               outw(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
        }
 
        if (emu->address_mode == 0) {
@@ -354,19 +348,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
        } else if (emu->card_capabilities->i2c_adc) {
                ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
        } else if (emu->audigy) {
-               outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+               outw(inw(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
 
                if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
                        /* Unmute Analog now.  Set GPO6 to 1 for Apollo.
                         * This has to be done after init ALice3 I2SOut beyond 48KHz.
                         * So, sequence is important. */
-                       outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+                       outw(inw(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
                } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
                        /* Unmute Analog now. */
-                       outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+                       outw(inw(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
                } else {
                        /* Disable routing from AC97 line out to Front speakers */
-                       outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+                       outw(inw(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
                }
        }
 
@@ -651,26 +645,27 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
                                     const struct firmware *fw_entry)
 {
        int n, i;
-       int reg;
-       int value;
-       __always_unused unsigned int write_post;
+       u16 reg;
+       u8 value;
+       __always_unused u16 write_post;
        unsigned long flags;
 
        if (!fw_entry)
                return -EIO;
 
        /* The FPGA is a Xilinx Spartan IIE XC2S50E */
+       /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */
        /* GPIO7 -> FPGA PGMN
         * GPIO6 -> FPGA CCLK
         * GPIO5 -> FPGA DIN
         * FPGA CONFIG OFF -> FPGA PGMN
         */
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
-       write_post = inl(emu->port + A_IOCFG);
+       outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */
+       write_post = inw(emu->port + A_GPIO);
        udelay(100);
-       outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
-       write_post = inl(emu->port + A_IOCFG);
+       outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */
+       write_post = inw(emu->port + A_GPIO);
        udelay(100); /* Allow FPGA memory to clean */
        for (n = 0; n < fw_entry->size; n++) {
                value = fw_entry->data[n];
@@ -679,15 +674,15 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
                        if (value & 0x1)
                                reg = reg | 0x20;
                        value = value >> 1;
-                       outl(reg, emu->port + A_IOCFG);
-                       write_post = inl(emu->port + A_IOCFG);
-                       outl(reg | 0x40, emu->port + A_IOCFG);
-                       write_post = inl(emu->port + A_IOCFG);
+                       outw(reg, emu->port + A_GPIO);
+                       write_post = inw(emu->port + A_GPIO);
+                       outw(reg | 0x40, emu->port + A_GPIO);
+                       write_post = inw(emu->port + A_GPIO);
                }
        }
        /* After programming, set GPIO bit 4 high again. */
-       outl(0x10, emu->port + A_IOCFG);
-       write_post = inl(emu->port + A_IOCFG);
+       outw(0x10, emu->port + A_GPIO);
+       write_post = inw(emu->port + A_GPIO);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 
        return 0;
@@ -782,7 +777,7 @@ static void emu1010_firmware_work(struct work_struct *work)
        } else if (!reg && emu->emu1010.last_reg) {
                /* Audio Dock removed */
                dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
-               /* Unmute all */
+               /* The hardware auto-mutes all, so we unmute again */
                snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
        }
 
@@ -794,29 +789,6 @@ static void emu1010_firmware_work(struct work_struct *work)
 }
 
 /*
- * EMU-1010 - details found out from this driver, official MS Win drivers,
- * testing the card:
- *
- * Audigy2 (aka Alice2):
- * ---------------------
- *     * communication over PCI
- *     * conversion of 32-bit data coming over EMU32 links from HANA FPGA
- *       to 2 x 16-bit, using internal DSP instructions
- *     * slave mode, clock supplied by HANA
- *     * linked to HANA using:
- *             32 x 32-bit serial EMU32 output channels
- *             16 x EMU32 input channels
- *             (?) x I2S I/O channels (?)
- *
- * FPGA (aka HANA):
- * ---------------
- *     * provides all (?) physical inputs and outputs of the card
- *             (ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
- *     * provides clock signal for the card and Alice2
- *     * two crystals - for 44.1kHz and 48kHz multiples
- *     * provides internal routing of signal sources to signal destinations
- *     * inputs/outputs to Alice2 - see above
- *
  * Current status of the driver:
  * ----------------------------
  *     * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
@@ -831,24 +803,10 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
        int err;
 
        dev_info(emu->card->dev, "emu1010: Special config.\n");
-       /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
-        * Lock Sound Memory Cache, Lock Tank Memory Cache,
-        * Mute all codecs.
-        */
-       outl(0x0005a00c, emu->port + HCFG);
-       /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
-        * Lock Tank Memory Cache,
-        * Mute all codecs.
-        */
-       outl(0x0005a004, emu->port + HCFG);
-       /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
-        * Mute all codecs.
-        */
-       outl(0x0005a000, emu->port + HCFG);
-       /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
-        * Mute all codecs.
-        */
-       outl(0x0005a000, emu->port + HCFG);
+
+       /* Mute, and disable audio and lock cache, just in case.
+        * Proper init follows in snd_emu10k1_init(). */
+       outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
 
        /* Disable 48Volt power to Audio Dock */
        snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
@@ -897,34 +855,20 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
 
        snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
        dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
-       dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
        /* Optical -> ADAT I/O  */
-       /* 0 : SPDIF
-        * 1 : ADAT
-        */
        emu->emu1010.optical_in = 1; /* IN_ADAT */
-       emu->emu1010.optical_out = 1; /* IN_ADAT */
-       tmp = 0;
+       emu->emu1010.optical_out = 1; /* OUT_ADAT */
        tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
                (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
        snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
-       snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
        /* Set no attenuation on Audio Dock pads. */
        snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
        emu->emu1010.adc_pads = 0x00;
-       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
        /* Unmute Audio dock DACs, Headphone source DAC-4. */
        snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
-       snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
        /* DAC PADs. */
        snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
        emu->emu1010.dac_pads = 0x0f;
-       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
-       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
        /* SPDIF Format. Set Consumer mode, 24bit, copy enable */
        snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
        /* MIDI routing */
@@ -936,8 +880,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
        /* IRQ Enable: All off */
        snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
-       dev_info(emu->card->dev, "emu1010: Card options3 = 0x%x\n", reg);
+       emu->emu1010.internal_clock = 1; /* 48000 */
        /* Default WCLK set to 48kHz. */
        snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
        /* Word Clock source, Internal 48kHz x1 */
@@ -1073,29 +1016,6 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
                EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
-
-       /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
-        * Lock Sound Memory Cache, Lock Tank Memory Cache,
-        * Mute all codecs.
-        */
-       outl(0x0000a000, emu->port + HCFG);
-       /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
-        * Lock Sound Memory Cache, Lock Tank Memory Cache,
-        * Un-Mute all codecs.
-        */
-       outl(0x0000a001, emu->port + HCFG);
-
-       /* Initial boot complete. Now patches */
-
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
-       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
-       snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
-
 #if 0
        snd_emu1010_fpga_link_dst_src_write(emu,
                EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
@@ -1215,21 +1135,6 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
                        EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
                emu->emu1010.output_source[23] = 28;
        }
-       /* TEMP: Select SPDIF in/out */
-       /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
-
-       /* TEMP: Select 48kHz SPDIF out */
-       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
-       /* Word Clock source, Internal 48kHz x1 */
-       snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
-       /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
-       emu->emu1010.internal_clock = 1; /* 48000 */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
-       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
-       /* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
-       /* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
-       /* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
 
        return 0;
 }
@@ -1343,6 +1248,15 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         * AC97: STAC9750
         * CA0151: None
         */
+       /*
+        * A_IOCFG Input (GPIO)
+        * 0x400  = Front analog jack plugged in. (Green socket)
+        * 0x1000 = Rear analog jack plugged in. (Black socket)
+        * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+        * A_IOCFG Output (GPIO)
+        * 0x60 = Sound out of front Left.
+        * Win sets it to 0xXX61
+        */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
         .driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
         .id = "Audigy2",
@@ -1391,6 +1305,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spi_dac = 1,
         .i2c_adc = 1,
         .spk71 = 1} ,
+       /* This is MAEM8950 "Mana" */
+       /* Attach MicroDock[M] to make it an E-MU 1616[m]. */
+       /* Does NOT support sync daughter card (obviously). */
        /* Tested by James@superbug.co.uk 4th Nov 2007. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
         .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
@@ -1401,7 +1318,10 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spk71 = 1 ,
         .emu_model = EMU_MODEL_EMU1616},
        /* Tested by James@superbug.co.uk 4th Nov 2007. */
-       /* This is MAEM8960, 0202 is MAEM 8980 */
+       /* This is MAEM8960 "Hana3", 0202 is MAEM8980 */
+       /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+        * MicroDock[M] to make it an E-MU 1616[m]. */
+       /* Does NOT support sync daughter card. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
         .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
         .id = "EMU1010",
@@ -1411,6 +1331,11 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
        /* Tested by Maxim Kachur <mcdebugger@duganet.ru> 17th Oct 2012. */
        /* This is MAEM8986, 0202 is MAEM8980 */
+       /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+        * MicroDockM to make it an E-MU 1616m. The non-m
+        * version was never sold with this card, but should
+        * still work. */
+       /* Does NOT support sync daughter card. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40071102,
         .driver = "Audigy2", .name = "E-mu 1010 PCIe [MAEM8986]",
         .id = "EMU1010",
@@ -1419,7 +1344,10 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 PCIe */
        /* Tested by James@superbug.co.uk 8th July 2005. */
-       /* This is MAEM8810, 0202 is MAEM8820 */
+       /* This is MAEM8810 "Hana", 0202 is MAEM8820 "Hamoa" */
+       /* Attach 0202 daughter card to make it an E-MU 1212m, OR an
+        * AudioDock[M] to make it an E-MU 1820[m]. */
+       /* Supports sync daughter card. */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
         .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
         .id = "EMU1010",
@@ -1427,7 +1355,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .ca0102_chip = 1,
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
-       /* EMU0404b */
+       /* This is MAEM8852 "HanaLiteLite" */
+       /* Supports sync daughter card. */
+       /* Tested by oswald.buddenhagen@gmx.de Mar 2023. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
         .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
         .id = "EMU0404",
@@ -1435,6 +1365,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .ca0108_chip = 1,
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
+       /* This is MAEM8850 "HanaLite" */
+       /* Supports sync daughter card. */
        /* Tested by James@superbug.co.uk 20-3-2007. */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
         .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
@@ -1444,6 +1376,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
        /* EMU0404 PCIe */
+       /* Does NOT support sync daughter card. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102,
         .driver = "Audigy2", .name = "E-mu 0404 PCIe [MAEM8984]",
         .id = "EMU0404",
@@ -1451,7 +1384,6 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .ca0108_chip = 1,
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */
-       /* Note that all E-mu cards require kernel 2.6 or newer. */
        {.vendor = 0x1102, .device = 0x0008,
         .driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
         .id = "Audigy2",
@@ -1532,6 +1464,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spdif_bug = 1,
         .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
         .ac97_chip = 1} ,
+       /* Audigy 2 Platinum EX */
+       /* Win driver sets A_IOCFG output to 0x1c00 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
         .driver = "Audigy2", .name = "Audigy 2 Platinum EX [SB0280]",
         .id = "Audigy2",
@@ -1552,6 +1486,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .spdif_bug = 1,
         .invert_shared_spdif = 1,      /* digital/analog switch swapped */
         .ac97_chip = 1} ,
+       /* Audigy 2 Platinum */
+       /* Win driver sets A_IOCFG output to 0xa00 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
         .driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
         .id = "Audigy2",
@@ -1657,6 +1593,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
+       /* SB Live! Platinum */
+       /* Win driver sets A_IOCFG output to 0 */
+       /* Tested by Jonathan Dowland <jon@dow.land> Apr 2023. */
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
         .driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
         .id = "Live",
@@ -1901,11 +1840,12 @@ int snd_emu10k1_create(struct snd_card *card,
 
        pci_set_master(pci);
 
-       emu->fx8010.fxbus_mask = 0x303f;
+       // The masks are not used for Audigy.
+       // FIXME: these should come from the card_capabilites table.
        if (extin_mask == 0)
-               extin_mask = 0x3fcf;
+               extin_mask = 0x3fcf;  // EXTIN_*
        if (extout_mask == 0)
-               extout_mask = 0x7fff;
+               extout_mask = 0x7fff;  // EXTOUT_*
        emu->fx8010.extin_mask = extin_mask;
        emu->fx8010.extout_mask = extout_mask;
        emu->enable_ir = enable_ir;
@@ -1970,12 +1910,10 @@ int snd_emu10k1_create(struct snd_card *card,
                pgtbl[idx] = cpu_to_le32(silent_page | idx);
 
        /* set up voice indices */
-       for (idx = 0; idx < NUM_G; idx++) {
-               emu->voices[idx].emu = emu;
+       for (idx = 0; idx < NUM_G; idx++)
                emu->voices[idx].number = idx;
-       }
 
-       err = snd_emu10k1_init(emu, enable_ir, 0);
+       err = snd_emu10k1_init(emu, enable_ir);
        if (err < 0)
                return err;
 #ifdef CONFIG_PM_SLEEP
@@ -2007,7 +1945,7 @@ static const unsigned char saved_regs[] = {
        0xff /* end */
 };
 static const unsigned char saved_regs_audigy[] = {
-       A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+       A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_EHC,
        A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
        0xff /* end */
 };
@@ -2054,7 +1992,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
                                *val = snd_emu10k1_ptr_read(emu, *reg, i);
        }
        if (emu->audigy)
-               emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+               emu->saved_a_iocfg = inw(emu->port + A_IOCFG);
        emu->saved_hcfg = inl(emu->port + HCFG);
 }
 
@@ -2068,7 +2006,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
                snd_emu10k1_emu1010_init(emu);
        else
                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
-       snd_emu10k1_init(emu, emu->enable_ir, 1);
+       snd_emu10k1_init(emu, emu->enable_ir);
 }
 
 void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
@@ -2081,7 +2019,7 @@ void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
 
        /* resore for spdif */
        if (emu->audigy)
-               outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+               outw(emu->saved_a_iocfg, emu->port + A_IOCFG);
        outl(emu->saved_hcfg, emu->port + HCFG);
 
        val = emu->saved_ptr;
index 6cf7c8b1de47d5086b71f6e466f4e8a3d22b1d7f..db211a6e8a47087101b07a51d6d5369c08008d78 100644 (file)
@@ -641,8 +641,8 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu,
        list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
                kcontrol = ctl->kcontrol;
                if (kcontrol->id.iface == id->iface &&
-                   !strcmp(kcontrol->id.name, id->name) &&
-                   kcontrol->id.index == id->index)
+                   kcontrol->id.index == id->index &&
+                   !strcmp(kcontrol->id.name, id->name))
                        return ctl;
        }
        return NULL;
@@ -1187,8 +1187,8 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
 }
 
 /*
- * Used for emu1010 - conversion from 32-bit capture inputs from HANA
- * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Used for emu1010 - conversion from 32-bit capture inputs from the FPGA
+ * to 2 x 16-bit registers in Audigy - their values are read via DMA.
  * Conversion is performed by Audigy DSP instructions of FX8010.
  */
 static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
@@ -1213,7 +1213,7 @@ static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
 
 static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 {
-       int err, i, z, gpr, nctl;
+       int err, z, gpr, nctl;
        int bit_shifter16;
        const int playback = 10;
        const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
@@ -1245,12 +1245,10 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
        icode->code = icode->tram_addr_map + 256;
 
        /* clear free GPRs */
-       for (i = 0; i < 512; i++)
-               set_bit(i, icode->gpr_valid);
+       memset(icode->gpr_valid, 0xff, 512 / 8);
                
        /* clear TRAM data & address lines */
-       for (i = 0; i < 256; i++)
-               set_bit(i, icode->tram_valid);
+       memset(icode->tram_valid, 0xff, 256 / 8);
 
        strcpy(icode->name, "Audigy DSP code for ALSA");
        ptr = 0;
@@ -1261,9 +1259,6 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
        gpr_map[gpr++] = 0x0000ffff;
        bit_shifter16 = gpr;
 
-       /* stop FX processor */
-       snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
-
 #if 1
        /* PCM front Playback Volume (independent from stereo mix)
         * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
@@ -1332,8 +1327,9 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 #define A_ADD_VOLUME_IN(var,vol,input) \
 A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
-       /* emu1212 DSP 0 and DSP 1 Capture */
        if (emu->card_capabilities->emu_model) {
+               /* EMU1010 DSP 0 and DSP 1 Capture */
+               // The 24 MSB hold the actual value. We implicitly discard the 16 LSB.
                if (emu->card_capabilities->ca0108_chip) {
                        /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
                        A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
@@ -1638,8 +1634,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 #endif
 
        if (emu->card_capabilities->emu_model) {
+               /* Capture 16 channels of S32_LE sound. */
                if (emu->card_capabilities->ca0108_chip) {
                        dev_info(emu->card->dev, "EMU2 inputs on\n");
+                       /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */
+
                        for (z = 0; z < 0x10; z++) {
                                snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, 
                                                                        bit_shifter16,
@@ -1648,7 +1647,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
                        }
                } else {
                        dev_info(emu->card->dev, "EMU inputs on\n");
-                       /* Capture 16 (originally 8) channels of S32_LE sound */
+                       /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */
 
                        /*
                        dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n",
@@ -1886,12 +1885,10 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
        icode->code = icode->tram_addr_map + 160;
        
        /* clear free GPRs */
-       for (i = 0; i < 256; i++)
-               set_bit(i, icode->gpr_valid);
+       memset(icode->gpr_valid, 0xff, 256 / 8);
 
        /* clear TRAM data & address lines */
-       for (i = 0; i < 160; i++)
-               set_bit(i, icode->tram_valid);
+       memset(icode->tram_valid, 0xff, 160 / 8);
 
        strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
        ptr = 0; i = 0;
@@ -1903,9 +1900,6 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
        tmp = 0x88;     /* we need 4 temporary GPR */
        /* from 0x8c to 0xff is the area for tone control */
 
-       /* stop FX processor */
-       snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
-
        /*
         *  Process FX Buses
         */
@@ -2523,7 +2517,7 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
                                   struct snd_emu10k1_fx8010_info *info)
 {
        const char * const *fxbus, * const *extin, * const *extout;
-       unsigned short fxbus_mask, extin_mask, extout_mask;
+       unsigned short extin_mask, extout_mask;
        int res;
 
        info->internal_tram_size = emu->fx8010.itram_size;
@@ -2531,11 +2525,10 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
        fxbus = fxbuses;
        extin = emu->audigy ? audigy_ins : creative_ins;
        extout = emu->audigy ? audigy_outs : creative_outs;
-       fxbus_mask = emu->fx8010.fxbus_mask;
-       extin_mask = emu->fx8010.extin_mask;
-       extout_mask = emu->fx8010.extout_mask;
+       extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
+       extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
        for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
-               copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+               copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res);
                copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
                copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
        }
@@ -2651,17 +2644,19 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
                        return -EPERM;
                if (get_user(addr, (unsigned int __user *)argp))
                        return -EFAULT;
-               if (addr > 0x1ff)
-                       return -EINVAL;
-               if (emu->audigy)
-                       snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
-               else
-                       snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
-               udelay(10);
-               if (emu->audigy)
-                       snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
-               else
-                       snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+               if (emu->audigy) {
+                       if (addr > A_DBG_STEP_ADDR)
+                               return -EINVAL;
+                       snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+                       udelay(10);
+                       snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_STEP | addr);
+               } else {
+                       if (addr > EMU10K1_DBG_SINGLE_STEP_ADDR)
+                               return -EINVAL;
+                       snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+                       udelay(10);
+                       snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_STEP | addr);
+               }
                return 0;
        case SNDRV_EMU10K1_IOCTL_DBG_READ:
                if (emu->audigy)
index 3c115f8ab96c044a3f94d1a27dfa1a811964ca53..8fce3413f4ae8e1fcf57f458c460cd9c5b294537 100644 (file)
@@ -346,7 +346,7 @@ static const unsigned int emu1616_output_dst[] = {
 };
 
 /*
- * Data destinations - HANA outputs going to Alice2 (audigy) for
+ * Data destinations - FPGA outputs going to Alice2 (Audigy) for
  *   capture (EMU32 + I2S links)
  * Each destination has an enum mixer control to choose a data source
  */
@@ -367,6 +367,7 @@ static const unsigned int emu1010_input_dst[] = {
        EMU_DST_ALICE2_EMU32_D,
        EMU_DST_ALICE2_EMU32_E,
        EMU_DST_ALICE2_EMU32_F,
+       /* These exist only on rev1 EMU1010 cards. */
        EMU_DST_ALICE_I2S0_LEFT,
        EMU_DST_ALICE_I2S0_RIGHT,
        EMU_DST_ALICE_I2S1_LEFT,
@@ -708,7 +709,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
                        /* 44100 */
                        /* Mute all */
                        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
-                       /* Default fallback clock 48kHz */
+                       /* Default fallback clock 44.1kHz */
                        snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
                        /* Word Clock source, Internal 44.1kHz x1 */
                        snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
@@ -924,7 +925,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int source_id;
        unsigned int ngain, ogain;
-       u32 gpio;
+       u16 gpio;
        int change = 0;
        unsigned long flags;
        u32 source;
@@ -941,11 +942,11 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
        if (change) {
                snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
                spin_lock_irqsave(&emu->emu_lock, flags);
-               gpio = inl(emu->port + A_IOCFG);
+               gpio = inw(emu->port + A_IOCFG);
                if (source_id==0)
-                       outl(gpio | 0x4, emu->port + A_IOCFG);
+                       outw(gpio | 0x4, emu->port + A_IOCFG);
                else
-                       outl(gpio & ~0x4, emu->port + A_IOCFG);
+                       outw(gpio & ~0x4, emu->port + A_IOCFG);
                spin_unlock_irqrestore(&emu->emu_lock, flags);
 
                ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
@@ -1005,7 +1006,7 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int ogain;
-       unsigned int ngain;
+       unsigned int ngain0, ngain1;
        unsigned int source_id;
        int change = 0;
 
@@ -1014,24 +1015,24 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
         /*        capture_source: uinfo->value.enumerated.items = 2 */
        if (source_id >= 2)
                return -EINVAL;
+       ngain0 = ucontrol->value.integer.value[0];
+       ngain1 = ucontrol->value.integer.value[1];
+       if (ngain0 > 0xff)
+               return -EINVAL;
+       if (ngain1 > 0xff)
+               return -EINVAL;
        ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
-       ngain = ucontrol->value.integer.value[0];
-       if (ngain > 0xff)
-               return 0;
-       if (ogain != ngain) {
+       if (ogain != ngain0) {
                if (emu->i2c_capture_source == source_id)
-                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
-               emu->i2c_capture_volume[source_id][0] = ngain;
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ngain0);
+               emu->i2c_capture_volume[source_id][0] = ngain0;
                change = 1;
        }
        ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
-       ngain = ucontrol->value.integer.value[1];
-       if (ngain > 0xff)
-               return 0;
-       if (ogain != ngain) {
+       if (ogain != ngain1) {
                if (emu->i2c_capture_source == source_id)
-                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
-               emu->i2c_capture_volume[source_id][1] = ngain;
+                       snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ngain1);
+               emu->i2c_capture_volume[source_id][1] = ngain1;
                change = 1;
        }
 
@@ -1632,7 +1633,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
 
        if (emu->audigy)
-               ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+               ucontrol->value.integer.value[0] = inw(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
        else
                ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
        if (emu->card_capabilities->invert_shared_spdif)
@@ -1657,13 +1658,13 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
        if ( emu->card_capabilities->i2c_adc) {
                /* Do nothing for Audigy 2 ZS Notebook */
        } else if (emu->audigy) {
-               reg = inl(emu->port + A_IOCFG);
+               reg = inw(emu->port + A_IOCFG);
                val = sw ? A_IOCFG_GPOUT0 : 0;
                change = (reg & A_IOCFG_GPOUT0) != val;
                if (change) {
                        reg &= ~A_IOCFG_GPOUT0;
                        reg |= val;
-                       outl(reg | val, emu->port + A_IOCFG);
+                       outw(reg | val, emu->port + A_IOCFG);
                }
        }
        reg = inl(emu->port + HCFG);
index 6ec394fb1846845464b5e80158ce401ea4c03fcd..b0c0ef342756f825c161520b2a80019c9f58949f 100644 (file)
@@ -76,23 +76,6 @@ static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
        snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
 }       
 
-static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_emu10k1_pcm *epcm = runtime->private_data;
-       unsigned int ptr;
-
-       if (!epcm->running)
-               return 0;
-       ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
-       ptr += runtime->buffer_size;
-       ptr -= epcm->ccca_start_addr;
-       ptr %= runtime->buffer_size;
-
-       return ptr;
-}
-
 static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
 {
        int err, i;
@@ -343,7 +326,6 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
        } else
                snd_emu10k1_ptr_write(emu, FXRT, voice,
                                      snd_emu10k1_compose_send_routing(send_routing));
-       /* Stop CA */
        /* Assumption that PT is already 0 so no harm overwriting */
        snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
        snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
@@ -429,36 +411,6 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_emu10k1_pcm *epcm;
-
-       if (runtime->private_data == NULL)
-               return 0;
-       epcm = runtime->private_data;
-       if (epcm->extra) {
-               snd_emu10k1_voice_free(epcm->emu, epcm->extra);
-               epcm->extra = NULL;
-       }
-       if (epcm->voices[1]) {
-               snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
-               epcm->voices[1] = NULL;
-       }
-       if (epcm->voices[0]) {
-               snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
-               epcm->voices[0] = NULL;
-       }
-       if (epcm->memblk) {
-               snd_emu10k1_free_pages(emu, epcm->memblk);
-               epcm->memblk = NULL;
-               epcm->start_addr = 0;
-       }
-       snd_pcm_lib_free_pages(substream);
-       return 0;
-}
-
-static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -527,9 +479,6 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
        start_addr = epcm->start_addr;
        end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
 
-       /*
-        * the kX driver leaves some space between voices
-        */
        channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
 
        snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
@@ -1091,8 +1040,6 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
        epcm->type = PLAYBACK_EFX;
        epcm->substream = substream;
        
-       emu->pcm_playback_efx_substream = substream;
-
        runtime->private_data = epcm;
        runtime->private_free = snd_emu10k1_pcm_free_substream;
        runtime->hw = snd_emu10k1_efx_playback;
@@ -1267,9 +1214,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
        runtime->hw.rate_min = runtime->hw.rate_max = 48000;
        spin_lock_irq(&emu->reg_lock);
        if (emu->card_capabilities->emu_model) {
-               /*  Nb. of channels has been increased to 16 */
                /* TODO
-                * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
                 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
                 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
                 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
@@ -1280,13 +1225,14 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
                 * Need to add mixer control to fix sample rate
                 *                 
                 * There are 32 mono channels of 16bits each.
-                * 24bit Audio uses 2x channels over 16bit
-                * 96kHz uses 2x channels over 48kHz
-                * 192kHz uses 4x channels over 48kHz
-                * So, for 48kHz 24bit, one has 16 channels
-                * for 96kHz 24bit, one has 8 channels
-                * for 192kHz 24bit, one has 4 channels
-                *
+                * 24bit Audio uses 2x channels over 16bit,
+                * 96kHz uses 2x channels over 48kHz,
+                * 192kHz uses 4x channels over 48kHz.
+                * So, for 48kHz 24bit, one has 16 channels,
+                * for 96kHz 24bit, one has 8 channels,
+                * for 192kHz 24bit, one has 4 channels.
+                * 1010rev2 and 1616(m) cards have double that,
+                * but we don't exceed 16 channels anyway.
                 */
 #if 1
                switch (emu->emu1010.internal_clock) {
@@ -1372,10 +1318,10 @@ static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
        .open =                 snd_emu10k1_efx_playback_open,
        .close =                snd_emu10k1_efx_playback_close,
        .hw_params =            snd_emu10k1_playback_hw_params,
-       .hw_free =              snd_emu10k1_efx_playback_hw_free,
+       .hw_free =              snd_emu10k1_playback_hw_free,
        .prepare =              snd_emu10k1_efx_playback_prepare,
        .trigger =              snd_emu10k1_efx_playback_trigger,
-       .pointer =              snd_emu10k1_efx_playback_pointer,
+       .pointer =              snd_emu10k1_playback_pointer,
 };
 
 int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
@@ -1508,11 +1454,12 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
                        nval[idx / 32] |= 1 << (idx % 32);
                        bits++;
                }
-               
+
+       // Check that the number of requested channels is a power of two
+       // not bigger than the number of available channels.
        for (idx = 0; idx < nefxb; idx++)
                if (1 << idx == bits)
                        break;
-       
        if (idx >= nefxb)
                return -EINVAL;
 
@@ -1802,7 +1749,6 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
         * to these
         */     
        
-       /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
        if (emu->audigy) {
                emu->efx_voices_mask[0] = 0;
                if (emu->card_capabilities->emu_model)
index 6e20cca9c98fd44b303c4b5a1c76c1843108ef2b..bec72dc60a4133aa1bbfca7b5f6e3955230d1a80 100644 (file)
@@ -477,10 +477,7 @@ static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
        for(i = offset; i < offset+length; i++) {
                snd_iprintf(buffer, "%02X: ",i);
                for (j = 0; j < voices; j++) {
-                       if(iobase == 0)
-                               value = snd_ptr_read(emu, 0, i, j);
-                       else
-                               value = snd_ptr_read(emu, 0x20, i, j);
+                       value = snd_ptr_read(emu, iobase, i, j);
                        snd_iprintf(buffer, "%08lX ", value);
                }
                snd_iprintf(buffer, "\n");
index e15092ce9848cd61e3b990cc88a133bff9aaf19c..c60573f14ea8269ef7939b74faa4f24ac5357814 100644 (file)
@@ -233,56 +233,57 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
        return err;
 }
 
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
 {
        unsigned long flags;
 
-       if (reg > 0x3f)
-               return 1;
+       if (snd_BUG_ON(reg > 0x3f))
+               return;
        reg += 0x40; /* 0x40 upwards are registers. */
-       if (value > 0x3f) /* 0 to 0x3f are values */
-               return 1;
+       if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
+               return;
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outl(reg, emu->port + A_IOCFG);
+       outw(reg, emu->port + A_GPIO);
        udelay(10);
-       outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
        udelay(10);
-       outl(value, emu->port + A_IOCFG);
+       outw(value, emu->port + A_GPIO);
        udelay(10);
-       outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       outw(value | 0x80 , emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
        spin_unlock_irqrestore(&emu->emu_lock, flags);
-
-       return 0;
 }
 
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
 {
+       // The higest input pin is used as the designated interrupt trigger,
+       // so it needs to be masked out.
+       u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
        unsigned long flags;
-       if (reg > 0x3f)
-               return 1;
+       if (snd_BUG_ON(reg > 0x3f))
+               return;
        reg += 0x40; /* 0x40 upwards are registers. */
        spin_lock_irqsave(&emu->emu_lock, flags);
-       outl(reg, emu->port + A_IOCFG);
+       outw(reg, emu->port + A_GPIO);
        udelay(10);
-       outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
        udelay(10);
-       *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+       *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
-
-       return 0;
 }
 
 /* Each Destination has one and only one Source,
  * but one Source can feed any number of Destinations simultaneously.
  */
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
+void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
 {
-       snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
-       snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
-       snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
-       snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
-
-       return 0;
+       if (snd_BUG_ON(dst & ~0x71f))
+               return;
+       if (snd_BUG_ON(src & ~0x71f))
+               return;
+       snd_emu1010_fpga_write(emu, 0x00, dst >> 8);
+       snd_emu1010_fpga_write(emu, 0x01, dst & 0x1f);
+       snd_emu1010_fpga_write(emu, 0x02, src >> 8);
+       snd_emu1010_fpga_write(emu, 0x03, src & 0x1f);
 }
 
 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
@@ -313,7 +314,6 @@ void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenu
        unsigned int val;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(CLIEH << 16, emu->port + PTR);
                val = inl(emu->port + DATA);
@@ -333,7 +333,6 @@ void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicen
        unsigned int val;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(CLIEH << 16, emu->port + PTR);
                val = inl(emu->port + DATA);
@@ -352,7 +351,6 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
        unsigned long flags;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(CLIPH << 16, emu->port + PTR);
                voicenum = 1 << (voicenum - 32);
@@ -370,7 +368,6 @@ void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned i
        unsigned int val;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(HLIEH << 16, emu->port + PTR);
                val = inl(emu->port + DATA);
@@ -390,7 +387,6 @@ void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned
        unsigned int val;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(HLIEH << 16, emu->port + PTR);
                val = inl(emu->port + DATA);
@@ -409,7 +405,6 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
        unsigned long flags;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(HLIPH << 16, emu->port + PTR);
                voicenum = 1 << (voicenum - 32);
@@ -427,7 +422,6 @@ void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voice
        unsigned int sol;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(SOLEH << 16, emu->port + PTR);
                sol = inl(emu->port + DATA);
@@ -447,7 +441,6 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi
        unsigned int sol;
 
        spin_lock_irqsave(&emu->emu_lock, flags);
-       /* voice interrupt */
        if (voicenum >= 32) {
                outl(SOLEH << 16, emu->port + PTR);
                sol = inl(emu->port + DATA);
index ebb2275efb6c973172ab67adc1b177eebeca6206..dfb44e5e69a7abb64a09282ebcd8cc71d6d36a74 100644 (file)
@@ -18,7 +18,7 @@
 irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
 {
        struct snd_emu10k1 *emu = dev_id;
-       unsigned int status, status2, orig_status, orig_status2;
+       unsigned int status, orig_status;
        int handled = 0;
        int timeout = 0;
 
@@ -139,32 +139,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
                        status &= ~IPR_FXDSP;
                }
                if (status & IPR_P16V) {
-                       while ((status2 = inl(emu->port + IPR2)) != 0) {
-                               u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
-                               struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
-                               struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
-
-                               /* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */
-                               orig_status2 = status2;
-                               if(status2 & mask) {
-                                       if(pvoice->use) {
-                                               snd_pcm_period_elapsed(pvoice->epcm->substream);
-                                       } else { 
-                                               dev_err(emu->card->dev,
-                                                       "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n",
-                                                       status2, mask, pvoice,
-                                                       pvoice->use);
-                                       }
-                               }
-                               if(status2 & 0x110000) {
-                                       /* dev_info(emu->card->dev, "capture int found\n"); */
-                                       if(cvoice->use) {
-                                               /* dev_info(emu->card->dev, "capture period_elapsed\n"); */
-                                               snd_pcm_period_elapsed(cvoice->epcm->substream);
-                                       }
-                               }
-                               outl(orig_status2, emu->port + IPR2); /* ack all */
-                       }
+                       if (emu->p16v_interrupt)
+                               emu->p16v_interrupt(emu);
+                       else
+                               outl(0, emu->port + INTE2);
                        status &= ~IPR_P16V;
                }
 
index 18a1b0740e6b5ca70889e574f9b5b6b9fe711daf..ce4d3450959c12c7a079f58e2ed374c3173e0793 100644 (file)
@@ -149,42 +149,19 @@ static const struct snd_pcm_hardware snd_p16v_capture_hw = {
        .fifo_size =            0,
 };
 
-static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
-{
-       struct snd_emu10k1_pcm *epcm = runtime->private_data;
-
-       kfree(epcm);
-}
-
 /* open_playback callback */
 static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
 {
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-        struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
-       struct snd_emu10k1_pcm *epcm;
        struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
-       /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
-
-       if (epcm == NULL)
-               return -ENOMEM;
-       epcm->emu = emu;
-       epcm->substream = substream;
        /*
        dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
                   substream->pcm->device, channel_id);
        */
-       runtime->private_data = epcm;
-       runtime->private_free = snd_p16v_pcm_free_substream;
   
        runtime->hw = snd_p16v_playback_hw;
 
-        channel->emu = emu;
-        channel->number = channel_id;
-
-        channel->use=1;
 #if 0 /* debug */
        dev_dbg(emu->card->dev,
                   "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
@@ -193,7 +170,6 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea
               channel_id, chip, channel);
 #endif /* debug */
        /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
-       channel->epcm = epcm;
        err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
        if (err < 0)
                 return err;
@@ -205,44 +181,20 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea
 
        return 0;
 }
+
 /* open_capture callback */
 static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
 {
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-       struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
-       struct snd_emu10k1_pcm *epcm;
        struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
-       /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
-
-       if (epcm == NULL)
-               return -ENOMEM;
-       epcm->emu = emu;
-       epcm->substream = substream;
        /*
        dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
                   substream->pcm->device, channel_id);
        */
-       runtime->private_data = epcm;
-       runtime->private_free = snd_p16v_pcm_free_substream;
   
        runtime->hw = snd_p16v_capture_hw;
 
-       channel->emu = emu;
-       channel->number = channel_id;
-
-       channel->use=1;
-#if 0 /* debug */
-       dev_dbg(emu->card->dev,
-                  "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
-                  channel_id, channel, channel->use);
-       dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
-              channel_id, chip, channel);
-#endif /* debug */
-       /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
-       channel->epcm = epcm;
        err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
        if (err < 0)
                return err;
@@ -254,22 +206,12 @@ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream
 /* close callback */
 static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
 {
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-       //struct snd_pcm_runtime *runtime = substream->runtime;
-       //struct snd_emu10k1_pcm *epcm = runtime->private_data;
-       emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
-       /* FIXME: maybe zero others */
        return 0;
 }
 
 /* close callback */
 static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
 {
-       struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
-       //struct snd_pcm_runtime *runtime = substream->runtime;
-       //struct snd_emu10k1_pcm *epcm = runtime->private_data;
-       emu->p16v_capture_voice.use = 0;
-       /* FIXME: maybe zero others */
        return 0;
 }
 
@@ -411,13 +353,48 @@ static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
+static void snd_p16v_interrupt(struct snd_emu10k1 *emu)
+{
+       unsigned int status;
+
+       while ((status = inl(emu->port + IPR2)) != 0) {
+               u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
+
+               /* dev_dbg(emu->card->dev, "p16v status=0x%x\n", status); */
+               if (status & mask) {
+                       struct snd_pcm_substream *substream =
+                                       emu->pcm_p16v->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+                       struct snd_pcm_runtime *runtime = substream->runtime;
+
+                       if (runtime && runtime->private_data) {
+                               snd_pcm_period_elapsed(substream);
+                       } else {
+                               dev_err(emu->card->dev,
+                                       "p16v: status: 0x%08x, mask=0x%08x\n",
+                                       status, mask);
+                       }
+               }
+               if (status & 0x110000) {
+                       struct snd_pcm_substream *substream =
+                                       emu->pcm_p16v->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+                       struct snd_pcm_runtime *runtime = substream->runtime;
+
+                       /* dev_info(emu->card->dev, "capture int found\n"); */
+                       if (runtime && runtime->private_data) {
+                               /* dev_info(emu->card->dev, "capture period_elapsed\n"); */
+                               snd_pcm_period_elapsed(substream);
+                       }
+               }
+               outl(status, emu->port + IPR2); /* ack all */
+       }
+}
+
 /* trigger_playback callback */
 static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
                                    int cmd)
 {
        struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime;
-       struct snd_emu10k1_pcm *epcm;
        int channel;
        int result = 0;
         struct snd_pcm_substream *s;
@@ -439,10 +416,9 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
                    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
                        continue;
                runtime = s->runtime;
-               epcm = runtime->private_data;
                channel = substream->pcm->device-emu->p16v_device_offset;
                /* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
-               epcm->running = running;
+               runtime->private_data = (void *)(ptrdiff_t)running;
                basic |= (0x1<<channel);
                inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
                 snd_pcm_trigger_done(s, substream);
@@ -471,7 +447,6 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
 {
        struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_emu10k1_pcm *epcm = runtime->private_data;
        int channel = 0;
        int result = 0;
        u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
@@ -480,13 +455,13 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_START:
                snd_p16v_intr_enable(emu, inte);
                snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
-               epcm->running = 1;
+               runtime->private_data = (void *)1;
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
                snd_p16v_intr_disable(emu, inte);
                //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
-               epcm->running = 0;
+               runtime->private_data = NULL;
                break;
        default:
                result = -EINVAL;
@@ -501,10 +476,10 @@ snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
 {
        struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_emu10k1_pcm *epcm = runtime->private_data;
        snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
        int channel = substream->pcm->device - emu->p16v_device_offset;
-       if (!epcm->running)
+
+       if (!runtime->private_data)
                return 0;
 
        ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
@@ -526,11 +501,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
 {
        struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_emu10k1_pcm *epcm = runtime->private_data;
        snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
        int channel = 0;
 
-       if (!epcm->running)
+       if (!runtime->private_data)
                return 0;
 
        ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
@@ -591,6 +565,7 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "p16v");
        emu->pcm_p16v = pcm;
+       emu->p16v_interrupt = snd_p16v_interrupt;
 
        for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
            substream; 
index 3cdafa311617f7f22175baee95fee724aa3d6762..9d429ad1feff06fdd87dcfbb4894a204671fe817 100644 (file)
@@ -64,7 +64,7 @@
  */
 
 /********************************************************************************************************/
-/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers                     */
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers              */
 /********************************************************************************************************/
                                                                                                                            
 /* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
index 3a6568346fad14d1ee8ef70b623ffe6a176985bf..d4ada1c430c8df2c07f98c2491776224b0a32ad0 100644 (file)
@@ -6,8 +6,8 @@
  */
 
 /******************************************************************************/
-/* Audigy2Value Tina (P17V) pointer-offset register set,
- * accessed through the PTR20 and DATA24 registers  */
+/* Audigy2Value Tina (P17V) pointer-offset register set,                      */
+/* accessed through the PTR2 and DATA2 registers                              */
 /******************************************************************************/
 
 /* 00 - 07: Not used */
index 75020edd39e75fe2810a7a608fa197029609877a..b5210abb5141f5a0a20a79dac1c8d5a2d13d9efc 100644 (file)
@@ -514,13 +514,13 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
                break;
        case HDA_GEN_PCM_ACT_PREPARE:
                mutex_lock(&cs35l41->fw_mutex);
-               ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1);
+               ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL);
                mutex_unlock(&cs35l41->fw_mutex);
                break;
        case HDA_GEN_PCM_ACT_CLEANUP:
                mutex_lock(&cs35l41->fw_mutex);
                regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
-               ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0);
+               ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL);
                mutex_unlock(&cs35l41->fw_mutex);
                break;
        case HDA_GEN_PCM_ACT_CLOSE:
@@ -672,7 +672,7 @@ static int cs35l41_runtime_suspend(struct device *dev)
        if (cs35l41->playback_started) {
                regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
                                       ARRAY_SIZE(cs35l41_hda_mute));
-               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
+               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL);
                regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
                                   CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
                if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
index 77a592f219472d9a472fa33d5e18a4fc6818350d..881b2f3a1551f4337c17ca11434cd5c38a55414b 100644 (file)
@@ -2528,6 +2528,9 @@ static const struct pci_device_id azx_ids[] = {
        /* Meteorlake-P */
        { PCI_DEVICE(0x8086, 0x7e28),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* Lunarlake-P */
+       { PCI_DEVICE(0x8086, 0xa828),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Broxton-P(Apollolake) */
        { PCI_DEVICE(0x8086, 0x5a98),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
index c2bf867818946554c5dcf549174fdd79e121c112..9d0ab043880b03b57501e68b85eb90044478c02a 100644 (file)
@@ -580,12 +580,10 @@ static void hda_tegra_probe_work(struct work_struct *work)
        return; /* no error return from async probe */
 }
 
-static int hda_tegra_remove(struct platform_device *pdev)
+static void hda_tegra_remove(struct platform_device *pdev)
 {
        snd_card_free(dev_get_drvdata(&pdev->dev));
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static void hda_tegra_shutdown(struct platform_device *pdev)
@@ -607,7 +605,7 @@ static struct platform_driver tegra_platform_hda = {
                .of_match_table = hda_tegra_match,
        },
        .probe = hda_tegra_probe,
-       .remove = hda_tegra_remove,
+       .remove_new = hda_tegra_remove,
        .shutdown = hda_tegra_shutdown,
 };
 module_platform_driver(tegra_platform_hda);
index 5c6980394dcec2e2d92730e0cbb54eb7b73f2277..ee051bdfaff6f7dd69e880145b96e3f172a9143d 100644 (file)
@@ -2104,10 +2104,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                goto unlock;
        }
 
-       if (snd_BUG_ON(pin_idx < 0)) {
-               err = -EINVAL;
-               goto unlock;
-       }
        per_pin = get_pin(spec, pin_idx);
 
        /* Verify pin:cvt selections to avoid silent audio after S3.
@@ -2199,13 +2195,13 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                snd_hda_spdif_ctls_unassign(codec, pcm_idx);
                clear_bit(pcm_idx, &spec->pcm_in_use);
                pin_idx = hinfo_to_pin_index(codec, hinfo);
+               /*
+                * In such a case, return 0 to match the behavior in
+                * hdmi_pcm_open()
+                */
                if (pin_idx < 0)
                        goto unlock;
 
-               if (snd_BUG_ON(pin_idx < 0)) {
-                       err = -EINVAL;
-                       goto unlock;
-               }
                per_pin = get_pin(spec, pin_idx);
 
                if (spec->dyn_pin_out) {
index fa1812e7a49dca872a3325510d7649d24fdf1e54..267c7848974aeebe3aca7178adeba5cfdc675ed0 100644 (file)
@@ -6146,12 +6146,6 @@ static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
        return 0;
 }
 
-static inline int copy_u32_le(void __user *dest, void __iomem *src)
-{
-       u32 val = readl(src);
-       return copy_to_user(dest, &val, 4);
-}
-
 static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
                unsigned int cmd, unsigned long arg)
 {
index 82d4e0fda91be6a0bfac709c780405d47277afd8..b033bd2909405241d0aee3faf3205ac1c41275ab 100644 (file)
@@ -98,8 +98,10 @@ static int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev,
                case 0x204: legacy_ctrl2 |= 2 << 6; break;
                case 0x205: legacy_ctrl2 |= 3 << 6; break;
                default:
-                       dev_err(chip->card->dev,
-                               "invalid joystick port %#x", io_port);
+                       if (io_port > 0)
+                               dev_err(chip->card->dev,
+                                       "The %s does not support arbitrary IO ports for the game port (requested 0x%x)\n",
+                                       chip->card->shortname, (unsigned int)io_port);
                        return -EINVAL;
                }
        }
@@ -186,6 +188,13 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
        default: model = str = "???"; break;
        }
 
+       strcpy(card->driver, str);
+       sprintf(card->shortname, "Yamaha %s (%s)", model, str);
+       sprintf(card->longname, "%s at 0x%lx, irq %i",
+               card->shortname,
+               chip->reg_area_phys,
+               chip->irq);
+
        legacy_ctrl = 0;
        legacy_ctrl2 = 0x0800;  /* SBEN = 0, SMOD = 01, LAD = 0 */
 
@@ -218,7 +227,13 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
                case 0x398: legacy_ctrl2 |= 1; break;
                case 0x3a0: legacy_ctrl2 |= 2; break;
                case 0x3a8: legacy_ctrl2 |= 3; break;
-               default: fm_port[dev] = 0; break;
+               default:
+                       if (fm_port[dev] > 0)
+                               dev_err(card->dev,
+                                       "The %s does not support arbitrary IO ports for FM (requested 0x%x)\n",
+                                       card->shortname, (unsigned int)fm_port[dev]);
+                       fm_port[dev] = 0;
+                       break;
                }
                if (fm_port[dev] > 0)
                        fm_res = devm_request_region(&pci->dev, fm_port[dev],
@@ -234,7 +249,13 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
                case 0x300: legacy_ctrl2 |= 1 << 4; break;
                case 0x332: legacy_ctrl2 |= 2 << 4; break;
                case 0x334: legacy_ctrl2 |= 3 << 4; break;
-               default: mpu_port[dev] = 0; break;
+               default:
+                       if (mpu_port[dev] > 0)
+                               dev_err(card->dev,
+                                       "The %s does not support arbitrary IO ports for MPU-401 (requested 0x%x)\n",
+                                       card->shortname, (unsigned int)mpu_port[dev]);
+                       mpu_port[dev] = 0;
+                       break;
                }
                if (mpu_port[dev] > 0)
                        mpu_res = devm_request_region(&pci->dev, mpu_port[dev],
@@ -257,12 +278,6 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
        if (err  < 0)
                return err;
 
-       strcpy(card->driver, str);
-       sprintf(card->shortname, "Yamaha %s (%s)", model, str);
-       sprintf(card->longname, "%s at 0x%lx, irq %i",
-               card->shortname,
-               chip->reg_area_phys,
-               chip->irq);
        err = snd_ymfpci_pcm(chip, 0);
        if (err < 0)
                return err;
@@ -337,11 +352,9 @@ static struct pci_driver ymfpci_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_ymfpci_ids,
        .probe = snd_card_ymfpci_probe,
-#ifdef CONFIG_PM_SLEEP
        .driver = {
-               .pm = &snd_ymfpci_pm,
+               .pm = pm_sleep_ptr(&snd_ymfpci_pm),
        },
-#endif
 };
 
 module_pci_driver(ymfpci_driver);
index 66968776478a2504309d7316dae8dc797f61026f..a408785cfa1ba451df5d806fb3dcd478bf5f5fe4 100644 (file)
@@ -268,6 +268,49 @@ struct snd_ymfpci_pcm {
        u32 shift;
 };
 
+static const int saved_regs_index[] = {
+       /* spdif */
+       YDSXGR_SPDIFOUTCTRL,
+       YDSXGR_SPDIFOUTSTATUS,
+       YDSXGR_SPDIFINCTRL,
+       /* volumes */
+       YDSXGR_PRIADCLOOPVOL,
+       YDSXGR_NATIVEDACINVOL,
+       YDSXGR_NATIVEDACOUTVOL,
+       YDSXGR_BUF441OUTVOL,
+       YDSXGR_NATIVEADCINVOL,
+       YDSXGR_SPDIFLOOPVOL,
+       YDSXGR_SPDIFOUTVOL,
+       YDSXGR_ZVOUTVOL,
+       YDSXGR_LEGACYOUTVOL,
+       /* address bases */
+       YDSXGR_PLAYCTRLBASE,
+       YDSXGR_RECCTRLBASE,
+       YDSXGR_EFFCTRLBASE,
+       YDSXGR_WORKBASE,
+       /* capture set up */
+       YDSXGR_MAPOFREC,
+       YDSXGR_RECFORMAT,
+       YDSXGR_RECSLOTSR,
+       YDSXGR_ADCFORMAT,
+       YDSXGR_ADCSLOTSR,
+};
+#define YDSXGR_NUM_SAVED_REGS  ARRAY_SIZE(saved_regs_index)
+
+static const int pci_saved_regs_index[] = {
+       /* All Chips */
+       PCIR_DSXG_LEGACY,
+       PCIR_DSXG_ELEGACY,
+       /* YMF 744/754 */
+       PCIR_DSXG_FMBASE,
+       PCIR_DSXG_SBBASE,
+       PCIR_DSXG_MPU401BASE,
+       PCIR_DSXG_JOYBASE,
+};
+#define DSXG_PCI_NUM_SAVED_REGS        ARRAY_SIZE(pci_saved_regs_index)
+#define DSXG_PCI_NUM_SAVED_LEGACY_REGS 2
+static_assert(DSXG_PCI_NUM_SAVED_LEGACY_REGS <= DSXG_PCI_NUM_SAVED_REGS);
+
 struct snd_ymfpci {
        int irq;
 
@@ -276,7 +319,7 @@ struct snd_ymfpci {
        unsigned long reg_area_phys;
        void __iomem *reg_area_virt;
 
-       unsigned short old_legacy_ctrl;
+       u16 old_legacy_ctrl;
 #ifdef SUPPORT_JOYSTICK
        struct gameport *gameport;
 #endif
@@ -345,17 +388,14 @@ struct snd_ymfpci {
        const struct firmware *dsp_microcode;
        const struct firmware *controller_microcode;
 
-#ifdef CONFIG_PM_SLEEP
-       u32 *saved_regs;
+       u32 saved_regs[YDSXGR_NUM_SAVED_REGS];
        u32 saved_ydsxgr_mode;
-       u16 saved_dsxg_legacy;
-       u16 saved_dsxg_elegacy;
-#endif
+       u16 saved_dsxg_pci_regs[DSXG_PCI_NUM_SAVED_REGS];
 };
 
 int snd_ymfpci_create(struct snd_card *card,
                      struct pci_dev *pci,
-                     unsigned short old_legacy_ctrl);
+                     u16 old_legacy_ctrl);
 void snd_ymfpci_free_gameport(struct snd_ymfpci *chip);
 
 extern const struct dev_pm_ops snd_ymfpci_pm;
index b492c32ce07049aaff4c941be5fef2f76cd9bdb8..6971eec45a4dbb5a2a613b95286d50f5d2e71a11 100644 (file)
 
 static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip);
 
-static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset)
-{
-       return readb(chip->reg_area_virt + offset);
-}
-
 static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val)
 {
        writeb(val, chip->reg_area_virt + offset);
@@ -2219,57 +2214,33 @@ static void snd_ymfpci_free(struct snd_card *card)
 
        snd_ymfpci_free_gameport(chip);
        
-       pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
+       pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, chip->old_legacy_ctrl);
        
        release_firmware(chip->dsp_microcode);
        release_firmware(chip->controller_microcode);
 }
 
-#ifdef CONFIG_PM_SLEEP
-static const int saved_regs_index[] = {
-       /* spdif */
-       YDSXGR_SPDIFOUTCTRL,
-       YDSXGR_SPDIFOUTSTATUS,
-       YDSXGR_SPDIFINCTRL,
-       /* volumes */
-       YDSXGR_PRIADCLOOPVOL,
-       YDSXGR_NATIVEDACINVOL,
-       YDSXGR_NATIVEDACOUTVOL,
-       YDSXGR_BUF441OUTVOL,
-       YDSXGR_NATIVEADCINVOL,
-       YDSXGR_SPDIFLOOPVOL,
-       YDSXGR_SPDIFOUTVOL,
-       YDSXGR_ZVOUTVOL,
-       YDSXGR_LEGACYOUTVOL,
-       /* address bases */
-       YDSXGR_PLAYCTRLBASE,
-       YDSXGR_RECCTRLBASE,
-       YDSXGR_EFFCTRLBASE,
-       YDSXGR_WORKBASE,
-       /* capture set up */
-       YDSXGR_MAPOFREC,
-       YDSXGR_RECFORMAT,
-       YDSXGR_RECSLOTSR,
-       YDSXGR_ADCFORMAT,
-       YDSXGR_ADCSLOTSR,
-};
-#define YDSXGR_NUM_SAVED_REGS  ARRAY_SIZE(saved_regs_index)
-
 static int snd_ymfpci_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct snd_ymfpci *chip = card->private_data;
-       unsigned int i;
-       
+       unsigned int i, legacy_reg_count = DSXG_PCI_NUM_SAVED_LEGACY_REGS;
+
+       if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+               legacy_reg_count = DSXG_PCI_NUM_SAVED_REGS;
+
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        snd_ac97_suspend(chip->ac97);
+
        for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
                chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
+
        chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
-       pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY,
-                            &chip->saved_dsxg_legacy);
-       pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY,
-                            &chip->saved_dsxg_elegacy);
+
+       for (i = 0; i < legacy_reg_count; i++)
+               pci_read_config_word(chip->pci, pci_saved_regs_index[i],
+                                     chip->saved_dsxg_pci_regs + i);
+
        snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
        snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
        snd_ymfpci_disable_dsp(chip);
@@ -2281,7 +2252,10 @@ static int snd_ymfpci_resume(struct device *dev)
        struct pci_dev *pci = to_pci_dev(dev);
        struct snd_card *card = dev_get_drvdata(dev);
        struct snd_ymfpci *chip = card->private_data;
-       unsigned int i;
+       unsigned int i, legacy_reg_count = DSXG_PCI_NUM_SAVED_LEGACY_REGS;
+
+       if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+               legacy_reg_count = DSXG_PCI_NUM_SAVED_REGS;
 
        snd_ymfpci_aclink_reset(pci);
        snd_ymfpci_codec_ready(chip, 0);
@@ -2293,10 +2267,9 @@ static int snd_ymfpci_resume(struct device *dev)
 
        snd_ac97_resume(chip->ac97);
 
-       pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY,
-                             chip->saved_dsxg_legacy);
-       pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY,
-                             chip->saved_dsxg_elegacy);
+       for (i = 0; i < legacy_reg_count; i++)
+               pci_write_config_word(chip->pci, pci_saved_regs_index[i],
+                                     chip->saved_dsxg_pci_regs[i]);
 
        /* start hw again */
        if (chip->start_count > 0) {
@@ -2309,12 +2282,11 @@ static int snd_ymfpci_resume(struct device *dev)
        return 0;
 }
 
-SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
-#endif /* CONFIG_PM_SLEEP */
+DEFINE_SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
 
 int snd_ymfpci_create(struct snd_card *card,
                      struct pci_dev *pci,
-                     unsigned short old_legacy_ctrl)
+                     u16 old_legacy_ctrl)
 {
        struct snd_ymfpci *chip = card->private_data;
        int err;
@@ -2379,13 +2351,6 @@ int snd_ymfpci_create(struct snd_card *card,
        if (err < 0)
                return err;
 
-#ifdef CONFIG_PM_SLEEP
-       chip->saved_regs = devm_kmalloc_array(&pci->dev, YDSXGR_NUM_SAVED_REGS,
-                                             sizeof(u32), GFP_KERNEL);
-       if (!chip->saved_regs)
-               return -ENOMEM;
-#endif
-
        snd_ymfpci_proc_init(card, chip);
 
        return 0;
index db414b61157e08234bf99d45ff7b19f538601a14..e17af46abddd56747a39e76fda81998b3b100703 100644 (file)
@@ -130,10 +130,9 @@ __error:
 }
 
 
-static int snd_pmac_remove(struct platform_device *devptr)
+static void snd_pmac_remove(struct platform_device *devptr)
 {
        snd_card_free(platform_get_drvdata(devptr));
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -161,7 +160,7 @@ static SIMPLE_DEV_PM_OPS(snd_pmac_pm, snd_pmac_driver_suspend, snd_pmac_driver_r
 
 static struct platform_driver snd_pmac_driver = {
        .probe          = snd_pmac_probe,
-       .remove         = snd_pmac_remove,
+       .remove_new     = snd_pmac_remove,
        .driver         = {
                .name   = SND_PMAC_DRIVER,
                .pm     = SND_PMAC_PM_OPS,
index f3f8ad7c3df8dc5af611b172e63bd306db966eb2..12f1e10db1c4b882239521fb5a91eeaa772e9055 100644 (file)
@@ -1361,9 +1361,9 @@ int snd_pmac_tumbler_init(struct snd_pmac *chip)
 
        for_each_child_of_node(chip->node, np) {
                if (of_node_name_eq(np, "sound")) {
-                       if (of_get_property(np, "has-anded-reset", NULL))
+                       if (of_property_read_bool(np, "has-anded-reset"))
                                mix->anded_reset = 1;
-                       if (of_get_property(np, "layout-id", NULL))
+                       if (of_property_present(np, "layout-id"))
                                mix->reset_on_sleep = 0;
                        of_node_put(np);
                        break;
index 6e9d6bd67369afca349114f379875841c7eda79f..320ac792c7fe2459b10024afd05719af2082772d 100644 (file)
@@ -539,15 +539,12 @@ static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
        return 0;
 }
 
-static int snd_aica_remove(struct platform_device *devptr)
+static void snd_aica_remove(struct platform_device *devptr)
 {
        struct snd_card_aica *dreamcastcard;
        dreamcastcard = platform_get_drvdata(devptr);
-       if (unlikely(!dreamcastcard))
-               return -ENODEV;
        snd_card_free(dreamcastcard->card);
        kfree(dreamcastcard);
-       return 0;
 }
 
 static int snd_aica_probe(struct platform_device *devptr)
@@ -594,7 +591,7 @@ static int snd_aica_probe(struct platform_device *devptr)
 
 static struct platform_driver snd_aica_driver = {
        .probe = snd_aica_probe,
-       .remove = snd_aica_remove,
+       .remove_new = snd_aica_remove,
        .driver = {
                .name = SND_AICA_DRIVER,
        },
index 8ebd972846acb5e7522dd0482d0758e5b2cc1305..8cf571955c9dd924f8f578a76fc9687a413369ea 100644 (file)
@@ -257,10 +257,9 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
 
 
 /* driver .remove  --  destructor */
-static int snd_sh_dac_remove(struct platform_device *devptr)
+static void snd_sh_dac_remove(struct platform_device *devptr)
 {
        snd_card_free(platform_get_drvdata(devptr));
-       return 0;
 }
 
 /* free -- it has been defined by create */
@@ -403,7 +402,7 @@ probe_error:
  */
 static struct platform_driver sh_dac_driver = {
        .probe  = snd_sh_dac_probe,
-       .remove = snd_sh_dac_remove,
+       .remove_new = snd_sh_dac_remove,
        .driver = {
                .name = "dac_audio",
        },
index b1342351bff43c85bcf02b34c6e8d6388f572ae0..d5b6f5187f8e8e0bec237daa3b8d2bb5e034e340 100644 (file)
@@ -274,13 +274,11 @@ err_clk_disable:
        return ret;
 }
 
-static int axi_i2s_dev_remove(struct platform_device *pdev)
+static void axi_i2s_dev_remove(struct platform_device *pdev)
 {
        struct axi_i2s *i2s = platform_get_drvdata(pdev);
 
        clk_disable_unprepare(i2s->clk);
-
-       return 0;
 }
 
 static const struct of_device_id axi_i2s_of_match[] = {
@@ -295,7 +293,7 @@ static struct platform_driver axi_i2s_driver = {
                .of_match_table = axi_i2s_of_match,
        },
        .probe = axi_i2s_probe,
-       .remove = axi_i2s_dev_remove,
+       .remove_new = axi_i2s_dev_remove,
 };
 module_platform_driver(axi_i2s_driver);
 
index 51b968ea21daa119976682883cbd71a6b3867bf8..e4c99bbc9cdd1b684de8f5be2f721b3292a4e35a 100644 (file)
@@ -239,13 +239,11 @@ err_clk_disable:
        return ret;
 }
 
-static int axi_spdif_dev_remove(struct platform_device *pdev)
+static void axi_spdif_dev_remove(struct platform_device *pdev)
 {
        struct axi_spdif *spdif = platform_get_drvdata(pdev);
 
        clk_disable_unprepare(spdif->clk);
-
-       return 0;
 }
 
 static const struct of_device_id axi_spdif_of_match[] = {
@@ -260,7 +258,7 @@ static struct platform_driver axi_spdif_driver = {
                .of_match_table = axi_spdif_of_match,
        },
        .probe = axi_spdif_probe,
-       .remove = axi_spdif_dev_remove,
+       .remove_new = axi_spdif_dev_remove,
 };
 module_platform_driver(axi_spdif_driver);
 
index c88ebd84bdd50adc6ec3efae42540983576e4d93..08e42082f5e96da12cf05bae60fc8e4945fd20b8 100644 (file)
@@ -90,6 +90,7 @@ config SND_SOC_AMD_VANGOGH_MACH
 
 config SND_SOC_AMD_ACP6x
        tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
+       select SND_AMD_ACP_CONFIG
        depends on X86 && PCI
        help
          This option enables Audio Coprocessor i.e ACP v6.x support on
@@ -130,6 +131,7 @@ config SND_SOC_AMD_RPL_ACP6x
 
 config SND_SOC_AMD_PS
         tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
+       select SND_AMD_ACP_CONFIG
         depends on X86 && PCI && ACPI
         help
           This option enables Audio Coprocessor i.e ACP v6.3 support on
index 198358d28ea9b19e87a2912bedc9758bfa264d16..d41df316da58e3bee4473d5f6774888f53812add 100644 (file)
@@ -1323,7 +1323,7 @@ static int acp_audio_probe(struct platform_device *pdev)
        return status;
 }
 
-static int acp_audio_remove(struct platform_device *pdev)
+static void acp_audio_remove(struct platform_device *pdev)
 {
        int status;
        struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
@@ -1332,8 +1332,6 @@ static int acp_audio_remove(struct platform_device *pdev)
        if (status)
                dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static int acp_pcm_resume(struct device *dev)
@@ -1428,7 +1426,7 @@ static const struct dev_pm_ops acp_pm_ops = {
 
 static struct platform_driver acp_dma_driver = {
        .probe = acp_audio_probe,
-       .remove = acp_audio_remove,
+       .remove_new = acp_audio_remove,
        .driver = {
                .name = DRV_NAME,
                .pm = &acp_pm_ops,
index 2b57c0ca4e9981d77362a9a5da59a0aee3fd0dbb..5c455cc041137a65acc47ec0875454d6143df58d 100644 (file)
@@ -366,28 +366,21 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int rembrandt_audio_remove(struct platform_device *pdev)
+static void rembrandt_audio_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct acp_dev_data *adata = dev_get_drvdata(dev);
-       struct acp_chip_info *chip;
-
-       chip = dev_get_platdata(&pdev->dev);
-       if (!chip || !chip->base) {
-               dev_err(&pdev->dev, "ACP chip data is NULL\n");
-               return -ENODEV;
-       }
+       struct acp_chip_info *chip = dev_get_platdata(dev);
 
        rmb_acp_deinit(chip->base);
 
        acp6x_disable_interrupts(adata);
        acp_platform_unregister(dev);
-       return 0;
 }
 
 static struct platform_driver rembrandt_driver = {
        .probe = rembrandt_audio_probe,
-       .remove = rembrandt_audio_remove,
+       .remove_new = rembrandt_audio_remove,
        .driver = {
                .name = "acp_asoc_rembrandt",
        },
index 2a89a0d2e60192cd246db90711186eee63859f30..b3cbc7f19ec544b329534c74057aa0f4044bfc67 100644 (file)
@@ -313,7 +313,7 @@ static int renoir_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int renoir_audio_remove(struct platform_device *pdev)
+static void renoir_audio_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct acp_dev_data *adata = dev_get_drvdata(dev);
@@ -329,12 +329,11 @@ static int renoir_audio_remove(struct platform_device *pdev)
                dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret));
 
        acp_platform_unregister(dev);
-       return 0;
 }
 
 static struct platform_driver renoir_driver = {
        .probe = renoir_audio_probe,
-       .remove = renoir_audio_remove,
+       .remove_new = renoir_audio_remove,
        .driver = {
                .name = "acp_asoc_renoir",
        },
index 6bf29b520511decc8f7b9aed93e2d481b8dc3ff4..dd36790b25aefef5478a8eee806e6f68fbdc9706 100644 (file)
@@ -111,3 +111,5 @@ struct acp63_dev_data {
        u16 pdev_count;
        u16 pdm_dev_index;
 };
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
index e86f23d97584f6fab89e17391c9999f39802f63c..afddb9a77ba491d6131e121304c7d473fc4225f6 100644 (file)
@@ -91,7 +91,6 @@ static int acp63_init(void __iomem *acp_base, struct device *dev)
                dev_err(dev, "ACP reset failed\n");
                return ret;
        }
-       acp63_writel(0x03, acp_base + ACP_CLKMUX_SEL);
        acp63_enable_interrupts(acp_base);
        return 0;
 }
@@ -106,7 +105,6 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
                dev_err(dev, "ACP reset failed\n");
                return ret;
        }
-       acp63_writel(0, acp_base + ACP_CLKMUX_SEL);
        acp63_writel(0, acp_base + ACP_CONTROL);
        return 0;
 }
@@ -249,11 +247,17 @@ static int snd_acp63_probe(struct pci_dev *pci,
 {
        struct acp63_dev_data *adata;
        u32 addr;
-       u32 irqflags;
+       u32 irqflags, flag;
        int val;
        int ret;
 
        irqflags = IRQF_SHARED;
+
+       /* Return if acp config flag is defined */
+       flag = snd_amd_acp_find_config(pci);
+       if (flag)
+               return -ENODEV;
+
        /* Pink Sardine device check */
        switch (pci->revision) {
        case 0x63:
index 454dab062e4f57d9ce3e6fa0d1728ec2f77cab68..46b91327168f8a3725f70b673e276d39c53ae7e9 100644 (file)
@@ -399,10 +399,9 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int acp63_pdm_audio_remove(struct platform_device *pdev)
+static void acp63_pdm_audio_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static int __maybe_unused acp63_pdm_resume(struct device *dev)
@@ -451,7 +450,7 @@ static const struct dev_pm_ops acp63_pdm_pm_ops = {
 
 static struct platform_driver acp63_pdm_dma_driver = {
        .probe = acp63_pdm_audio_probe,
-       .remove = acp63_pdm_audio_remove,
+       .remove_new = acp63_pdm_audio_remove,
        .driver = {
                .name = "acp_ps_pdm_dma",
                .pm = &acp63_pdm_pm_ops,
index 6aec11cf0a6ae66c06fa4bc87c5e4fd5063119ad..7362dd15ad307fba8c9bfe62f7d899ce5e7ef8d0 100644 (file)
@@ -421,10 +421,9 @@ static int acp3x_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int acp3x_audio_remove(struct platform_device *pdev)
+static void acp3x_audio_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static int acp3x_resume(struct device *dev)
@@ -509,7 +508,7 @@ static const struct dev_pm_ops acp3x_pm_ops = {
 
 static struct platform_driver acp3x_dma_driver = {
        .probe = acp3x_audio_probe,
-       .remove = acp3x_audio_remove,
+       .remove_new = acp3x_audio_remove,
        .driver = {
                .name = "acp3x_rv_i2s_dma",
                .pm = &acp3x_pm_ops,
index 0d8b693aecc9fd8ec39c769366e25daaaba4c138..4e299f96521fef8b15d78c2199ab23f9d5fe5e46 100644 (file)
@@ -435,10 +435,9 @@ static int acp_pdm_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int acp_pdm_audio_remove(struct platform_device *pdev)
+static void acp_pdm_audio_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static int acp_pdm_resume(struct device *dev)
@@ -489,7 +488,7 @@ static const struct dev_pm_ops acp_pdm_pm_ops = {
 
 static struct platform_driver acp_pdm_dma_driver = {
        .probe = acp_pdm_audio_probe,
-       .remove = acp_pdm_audio_remove,
+       .remove_new = acp_pdm_audio_remove,
        .driver = {
                .name = "acp_rn_pdm_dma",
                .pm = &acp_pdm_pm_ops,
index eebf2650ad272415f1bdf5866985630b21fe8dcc..e5bcd1e6eb73acb61538ddb92c524a630cfb1556 100644 (file)
@@ -6,37 +6,40 @@
  * Copyright 2021 Advanced Micro Devices, Inc.
  */
 
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include <sound/jack.h>
-#include <linux/clk.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/i2c.h>
-#include <linux/input.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input-event-codes.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
 
 #include "../../codecs/nau8821.h"
-#include "../../codecs/cs35l41.h"
-
 #include "acp5x.h"
 
-#define DRV_NAME "acp5x_mach"
-#define DUAL_CHANNEL           2
-#define ACP5X_NUVOTON_CODEC_DAI        "nau8821-hifi"
-#define VG_JUPITER 1
-#define ACP5X_NUVOTON_BCLK 3072000
-#define ACP5X_NAU8821_FREQ_OUT 12288000
+#define DRV_NAME                       "acp5x_mach"
+#define DUAL_CHANNEL                   2
+#define VG_JUPITER                     1
+#define ACP5X_NAU8821_BCLK             3072000
+#define ACP5X_NAU8821_FREQ_OUT         12288000
+#define ACP5X_NAU8821_COMP_NAME        "i2c-NVTN2020:00"
+#define ACP5X_NAU8821_DAI_NAME         "nau8821-hifi"
+#define ACP5X_CS35L41_COMP_LNAME       "spi-VLV1776:00"
+#define ACP5X_CS35L41_COMP_RNAME       "spi-VLV1776:01"
+#define ACP5X_CS35L41_DAI_NAME         "cs35l41-pcm"
 
 static unsigned long acp5x_machine_id;
 static struct snd_soc_jack vg_headset;
 
+SND_SOC_DAILINK_DEF(platform,  DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
+SND_SOC_DAILINK_DEF(acp5x_i2s, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp5x_bt,  DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
+SND_SOC_DAILINK_DEF(nau8821,   DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_NAU8821_COMP_NAME,
+                                                            ACP5X_NAU8821_DAI_NAME)));
+
 static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
        {
                .pin    = "Headphone",
@@ -48,18 +51,54 @@ static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
        },
 };
 
+static const struct snd_kcontrol_new acp5x_8821_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct snd_soc_dai *dai;
+       int ret = 0;
+
+       dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+       if (!dai) {
+               dev_err(card->dev, "Codec dai not found\n");
+               return -EIO;
+       }
+
+       if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
+               if (ret < 0) {
+                       dev_err(card->dev, "set sysclk err = %d\n", ret);
+                       return -EIO;
+               }
+       } else {
+               ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+               if (ret < 0)
+                       dev_err(dai->dev, "can't set BLK clock %d\n", ret);
+               ret = snd_soc_dai_set_pll(dai, 0, 0, ACP5X_NAU8821_BCLK, ACP5X_NAU8821_FREQ_OUT);
+               if (ret < 0)
+                       dev_err(dai->dev, "can't set FLL: %d\n", ret);
+       }
+
+       return ret;
+}
+
 static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
        int ret;
-       struct snd_soc_card *card = rtd->card;
-       struct snd_soc_component *component =
-                                       asoc_rtd_to_codec(rtd, 0)->component;
 
        /*
         * Headset buttons map to the google Reference headset.
         * These can be configured by userspace.
         */
-       ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
                                         SND_JACK_HEADSET | SND_JACK_BTN_0,
                                         &vg_headset, acp5x_nau8821_jack_pins,
                                         ARRAY_SIZE(acp5x_nau8821_jack_pins));
@@ -70,12 +109,8 @@ static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
 
        snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
        nau8821_enable_jack_detect(component, &vg_headset);
-       return ret;
-}
 
-static int acp5x_cs35l41_init(struct snd_soc_pcm_runtime *rtd)
-{
-       return 0;
+       return ret;
 }
 
 static const unsigned int rates[] = {
@@ -109,8 +144,7 @@ static int acp5x_8821_startup(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_card *card = rtd->card;
-       struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
+       struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
 
        machine->play_i2s_instance = I2S_SP_INSTANCE;
        machine->cap_i2s_instance = I2S_SP_INSTANCE;
@@ -123,6 +157,7 @@ static int acp5x_8821_startup(struct snd_pcm_substream *substream)
        snd_pcm_hw_constraint_list(substream->runtime, 0,
                                   SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                                   &constraints_sample_bits);
+
        return 0;
 }
 
@@ -131,29 +166,36 @@ static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_soc_card *card = rtd->card;
-       struct snd_soc_dai *codec_dai =
-                       snd_soc_card_get_codec_dai(card,
-                                                  ACP5X_NUVOTON_CODEC_DAI);
-       int ret;
+       struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+       int ret, bclk;
 
-       ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
-                                    SND_SOC_CLOCK_IN);
+       ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
        if (ret < 0)
                dev_err(card->dev, "can't set FS clock %d\n", ret);
-       ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
-                                 params_rate(params) * 256);
+
+       bclk = snd_soc_params_to_bclk(params);
+       if (bclk < 0) {
+               dev_err(dai->dev, "Fail to get BCLK rate: %d\n", bclk);
+               return bclk;
+       }
+
+       ret = snd_soc_dai_set_pll(dai, 0, 0, bclk, params_rate(params) * 256);
        if (ret < 0)
                dev_err(card->dev, "can't set FLL: %d\n", ret);
 
        return ret;
 }
 
+static const struct snd_soc_ops acp5x_8821_ops = {
+       .startup = acp5x_8821_startup,
+       .hw_params = acp5x_nau8821_hw_params,
+};
+
 static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_card *card = rtd->card;
-       struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
+       struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+       struct snd_pcm_runtime *runtime = substream->runtime;
 
        machine->play_i2s_instance = I2S_HS_INSTANCE;
 
@@ -162,6 +204,7 @@ static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
                                   &constraints_channels);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                   &constraints_rates);
+
        return 0;
 }
 
@@ -169,80 +212,66 @@ static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_card *card = rtd->card;
-       struct snd_soc_dai *codec_dai;
+       unsigned int bclk, rate = params_rate(params);
+       struct snd_soc_component *comp;
        int ret, i;
-       unsigned int num_codecs = rtd->dai_link->num_codecs;
-       unsigned int bclk_val;
-
-       ret = 0;
-       for (i = 0; i < num_codecs; i++) {
-               codec_dai = asoc_rtd_to_codec(rtd, i);
-               if (strcmp(codec_dai->name, "cs35l41-pcm") == 0) {
-                       switch (params_rate(params)) {
-                       case 48000:
-                               bclk_val = 1536000;
-                               break;
-                       default:
-                               dev_err(card->dev, "Invalid Samplerate:0x%x\n",
-                                       params_rate(params));
+
+       switch (rate) {
+       case 48000:
+               bclk = 1536000;
+               break;
+       default:
+               bclk = 0;
+               break;
+       }
+
+       for_each_rtd_components(rtd, i, comp) {
+               if (!(strcmp(comp->name, ACP5X_CS35L41_COMP_LNAME)) ||
+                   !(strcmp(comp->name, ACP5X_CS35L41_COMP_RNAME))) {
+                       if (!bclk) {
+                               dev_err(comp->dev, "Invalid sample rate: 0x%x\n", rate);
                                return -EINVAL;
                        }
-                       ret = snd_soc_component_set_sysclk(codec_dai->component,
-                                                          0, 0, bclk_val, SND_SOC_CLOCK_IN);
-                       if (ret < 0) {
-                               dev_err(card->dev, "failed to set sysclk for CS35l41 dai\n");
+
+                       ret = snd_soc_component_set_sysclk(comp, 0, 0, bclk, SND_SOC_CLOCK_IN);
+                       if (ret) {
+                               dev_err(comp->dev, "failed to set SYSCLK: %d\n", ret);
                                return ret;
                        }
                }
        }
 
-       return ret;
-}
+       return 0;
 
-static const struct snd_soc_ops acp5x_8821_ops = {
-       .startup = acp5x_8821_startup,
-       .hw_params = acp5x_nau8821_hw_params,
-};
+}
 
 static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
        .startup = acp5x_cs35l41_startup,
        .hw_params = acp5x_cs35l41_hw_params,
 };
 
-static struct snd_soc_codec_conf cs35l41_conf[] = {
+static struct snd_soc_codec_conf acp5x_cs35l41_conf[] = {
        {
-               .dlc = COMP_CODEC_CONF("spi-VLV1776:00"),
+               .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_LNAME),
                .name_prefix = "Left",
        },
        {
-               .dlc = COMP_CODEC_CONF("spi-VLV1776:01"),
+               .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_RNAME),
                .name_prefix = "Right",
        },
 };
 
-SND_SOC_DAILINK_DEF(acp5x_i2s,
-                   DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(cs35l41, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_CS35L41_COMP_LNAME,
+                                                          ACP5X_CS35L41_DAI_NAME),
+                                               COMP_CODEC(ACP5X_CS35L41_COMP_RNAME,
+                                                          ACP5X_CS35L41_DAI_NAME)));
 
-SND_SOC_DAILINK_DEF(acp5x_bt,
-                   DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
-
-SND_SOC_DAILINK_DEF(nau8821,
-                   DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
-                                                 "nau8821-hifi")));
-
-SND_SOC_DAILINK_DEF(cs35l41,
-                   DAILINK_COMP_ARRAY(COMP_CODEC("spi-VLV1776:00", "cs35l41-pcm"),
-                                      COMP_CODEC("spi-VLV1776:01", "cs35l41-pcm")));
-
-SND_SOC_DAILINK_DEF(platform,
-                   DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
-
-static struct snd_soc_dai_link acp5x_dai[] = {
+static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {
        {
                .name = "acp5x-8821-play",
                .stream_name = "Playback/Capture",
-               .dai_fmt = SND_SOC_DAIFMT_I2S  | SND_SOC_DAIFMT_NB_NF |
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                          SND_SOC_DAIFMT_NB_NF |
                           SND_SOC_DAIFMT_CBC_CFC,
                .dpcm_playback = 1,
                .dpcm_capture = 1,
@@ -253,65 +282,28 @@ static struct snd_soc_dai_link acp5x_dai[] = {
        {
                .name = "acp5x-CS35L41-Stereo",
                .stream_name = "CS35L41 Stereo Playback",
-               .dai_fmt = SND_SOC_DAIFMT_I2S  | SND_SOC_DAIFMT_NB_NF |
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                          SND_SOC_DAIFMT_NB_NF |
                           SND_SOC_DAIFMT_CBC_CFC,
                .dpcm_playback = 1,
                .playback_only = 1,
                .ops = &acp5x_cs35l41_play_ops,
-               .init = acp5x_cs35l41_init,
                SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),
        },
 };
 
-static int platform_clock_control(struct snd_soc_dapm_widget *w,
-                                 struct snd_kcontrol *k, int  event)
-{
-       struct snd_soc_dapm_context *dapm = w->dapm;
-       struct snd_soc_card *card = dapm->card;
-       struct snd_soc_dai *codec_dai;
-       int ret = 0;
 
-       codec_dai = snd_soc_card_get_codec_dai(card, ACP5X_NUVOTON_CODEC_DAI);
-       if (!codec_dai) {
-               dev_err(card->dev, "Codec dai not found\n");
-               return -EIO;
-       }
 
-       if (SND_SOC_DAPM_EVENT_OFF(event)) {
-               ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL,
-                                            0, SND_SOC_CLOCK_IN);
-               if (ret < 0) {
-                       dev_err(card->dev, "set sysclk err = %d\n", ret);
-                       return -EIO;
-               }
-       } else {
-               ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
-                                            SND_SOC_CLOCK_IN);
-               if (ret < 0)
-                       dev_err(codec_dai->dev, "can't set BLK clock %d\n", ret);
-               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, ACP5X_NUVOTON_BCLK,
-                                         ACP5X_NAU8821_FREQ_OUT);
-               if (ret < 0)
-                       dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
-       }
-       return ret;
-}
-
-static const struct snd_kcontrol_new acp5x_8821_controls[] = {
-       SOC_DAPM_PIN_SWITCH("Headphone"),
-       SOC_DAPM_PIN_SWITCH("Headset Mic"),
-       SOC_DAPM_PIN_SWITCH("Int Mic"),
-};
-
-static const struct snd_soc_dapm_widget acp5x_8821_widgets[] = {
+static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_MIC("Int Mic", NULL),
        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
-                           platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+                           platform_clock_control,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 };
 
-static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = {
+static const struct snd_soc_dapm_route acp5x_8821_35l41_audio_route[] = {
        /* HP jack connectors - unknown if we have jack detection */
        { "Headphone", NULL, "HPOL" },
        { "Headphone", NULL, "HPOR" },
@@ -324,17 +316,17 @@ static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = {
        { "Int Mic", NULL, "Platform Clock" },
 };
 
-static struct snd_soc_card acp5x_card = {
+static struct snd_soc_card acp5x_8821_35l41_card = {
        .name = "acp5x",
        .owner = THIS_MODULE,
-       .dai_link = acp5x_dai,
-       .num_links = ARRAY_SIZE(acp5x_dai),
-       .dapm_widgets = acp5x_8821_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_widgets),
-       .dapm_routes = acp5x_8821_audio_route,
-       .num_dapm_routes = ARRAY_SIZE(acp5x_8821_audio_route),
-       .codec_conf = cs35l41_conf,
-       .num_configs = ARRAY_SIZE(cs35l41_conf),
+       .dai_link = acp5x_8821_35l41_dai,
+       .num_links = ARRAY_SIZE(acp5x_8821_35l41_dai),
+       .dapm_widgets = acp5x_8821_35l41_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_35l41_widgets),
+       .dapm_routes = acp5x_8821_35l41_audio_route,
+       .num_dapm_routes = ARRAY_SIZE(acp5x_8821_35l41_audio_route),
+       .codec_conf = acp5x_cs35l41_conf,
+       .num_configs = ARRAY_SIZE(acp5x_cs35l41_conf),
        .controls = acp5x_8821_controls,
        .num_controls = ARRAY_SIZE(acp5x_8821_controls),
 };
@@ -342,6 +334,7 @@ static struct snd_soc_card acp5x_card = {
 static int acp5x_vg_quirk_cb(const struct dmi_system_id *id)
 {
        acp5x_machine_id = VG_JUPITER;
+
        return 1;
 }
 
@@ -358,33 +351,31 @@ static const struct dmi_system_id acp5x_vg_quirk_table[] = {
 
 static int acp5x_probe(struct platform_device *pdev)
 {
-       int ret;
        struct acp5x_platform_info *machine;
+       struct device *dev = &pdev->dev;
        struct snd_soc_card *card;
+       int ret;
 
-       machine = devm_kzalloc(&pdev->dev, sizeof(struct acp5x_platform_info),
-                              GFP_KERNEL);
+       machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
        if (!machine)
                return -ENOMEM;
 
        dmi_check_system(acp5x_vg_quirk_table);
        switch (acp5x_machine_id) {
        case VG_JUPITER:
-               card = &acp5x_card;
-               acp5x_card.dev = &pdev->dev;
+               card = &acp5x_8821_35l41_card;
                break;
        default:
                return -ENODEV;
        }
+       card->dev = dev;
        platform_set_drvdata(pdev, card);
        snd_soc_card_set_drvdata(card, machine);
 
-       ret = devm_snd_soc_register_card(&pdev->dev, card);
-       if (ret) {
-               return dev_err_probe(&pdev->dev, ret,
-                                    "snd_soc_register_card(%s) failed\n",
-                                    acp5x_card.name);
-       }
+       ret = devm_snd_soc_register_card(dev, card);
+       if (ret)
+               return dev_err_probe(dev, ret, "Register card (%s) failed\n", card->name);
+
        return 0;
 }
 
index d36bb718370f6fe24b7e00badfc14fbcaada924a..29901ee4bfe387e64ed2cdd773a3a8a18d22687b 100644 (file)
@@ -415,10 +415,9 @@ static int acp5x_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int acp5x_audio_remove(struct platform_device *pdev)
+static void acp5x_audio_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static int __maybe_unused acp5x_pcm_resume(struct device *dev)
@@ -500,7 +499,7 @@ static const struct dev_pm_ops acp5x_pm_ops = {
 
 static struct platform_driver acp5x_dma_driver = {
        .probe = acp5x_audio_probe,
-       .remove = acp5x_audio_remove,
+       .remove_new = acp5x_audio_remove,
        .driver = {
                .name = "acp5x_i2s_dma",
                .pm = &acp5x_pm_ops,
index 0acdf0156f075d02a701868ce67690990302e452..b9958e55536744fdec7b1aa9455a70f09cad12a3 100644 (file)
@@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = {
 };
 
 static const struct dmi_system_id yc_acp_quirk_table[] = {
+       {
+               .driver_data = &acp6x_card,
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5525"),
+               }
+       },
        {
                .driver_data = &acp6x_card,
                .matches = {
@@ -178,6 +185,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "21EN"),
                }
        },
+       {
+               .driver_data = &acp6x_card,
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "21HY"),
+               }
+       },
        {
                .driver_data = &acp6x_card,
                .matches = {
@@ -262,6 +276,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"),
                }
        },
+       {
+               .driver_data = &acp6x_card,
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+                       DMI_MATCH(DMI_BOARD_NAME, "8A42"),
+               }
+       },
        {
                .driver_data = &acp6x_card,
                .matches = {
index 294dd7fb43c9be5ab1374f44ea5414cd75064ad2..d818eba48546dc5bfd441a14d9af4716cd9eb382 100644 (file)
@@ -388,10 +388,9 @@ static int acp6x_pdm_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int acp6x_pdm_audio_remove(struct platform_device *pdev)
+static void acp6x_pdm_audio_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static int __maybe_unused acp6x_pdm_resume(struct device *dev)
@@ -440,7 +439,7 @@ static const struct dev_pm_ops acp6x_pdm_pm_ops = {
 
 static struct platform_driver acp6x_pdm_dma_driver = {
        .probe = acp6x_pdm_audio_probe,
-       .remove = acp6x_pdm_audio_remove,
+       .remove_new = acp6x_pdm_audio_remove,
        .driver = {
                .name = "acp_yc_pdm_dma",
                .pm = &acp6x_pdm_pm_ops,
index 036207568c048fb15b0bfd034151a26762895d28..2de7d1edf00b728f8d6274e00b1dda643d35c86c 100644 (file)
@@ -105,3 +105,6 @@ static inline void acp6x_writel(u32 val, void __iomem *base_addr)
 {
        writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
 }
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
index 77c5fa1f7af14036e5d56c5c5eae1585ac6e2408..7af6a349b1d41fb60d9450a312927e4774889a34 100644 (file)
@@ -149,10 +149,16 @@ static int snd_acp6x_probe(struct pci_dev *pci,
        int index = 0;
        int val = 0x00;
        u32 addr;
-       unsigned int irqflags;
+       unsigned int irqflags, flag;
        int ret;
 
        irqflags = IRQF_SHARED;
+
+       /* Return if acp config flag is defined */
+       flag = snd_amd_acp_find_config(pci);
+       if (flag)
+               return -ENODEV;
+
        /* Yellow Carp device check */
        switch (pci->revision) {
        case 0x60:
index 64750db9b96396e2b8d2a78cf185a3cb12c128f6..ce77934f3eef313a556a05c8e139a8d1f4dda60b 100644 (file)
@@ -1159,13 +1159,12 @@ err_release:
        return ret;
 }
 
-static int apple_mca_remove(struct platform_device *pdev)
+static void apple_mca_remove(struct platform_device *pdev)
 {
        struct mca_data *mca = platform_get_drvdata(pdev);
 
        snd_soc_unregister_component(&pdev->dev);
        apple_mca_release(mca);
-       return 0;
 }
 
 static const struct of_device_id apple_mca_of_match[] = {
@@ -1180,7 +1179,7 @@ static struct platform_driver apple_mca_driver = {
                .of_match_table = apple_mca_of_match,
        },
        .probe = apple_mca_probe,
-       .remove = apple_mca_remove,
+       .remove_new = apple_mca_remove,
 };
 module_platform_driver(apple_mca_driver);
 
index 9883e6867fd12f12228d912ade968664bde8fc8d..007ab746973d28822ed0a1bcd8143b6d0db75b00 100644 (file)
@@ -473,24 +473,21 @@ static int atmel_classd_asoc_card_init(struct device *dev,
        if (!dai_link)
                return -ENOMEM;
 
-       comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
+       comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
        if (!comp)
                return -ENOMEM;
 
        dai_link->cpus          = &comp[0];
        dai_link->codecs        = &comp[1];
-       dai_link->platforms     = &comp[2];
 
        dai_link->num_cpus      = 1;
        dai_link->num_codecs    = 1;
-       dai_link->num_platforms = 1;
 
        dai_link->name                  = "CLASSD";
        dai_link->stream_name           = "CLASSD PCM";
        dai_link->codecs->dai_name      = "snd-soc-dummy-dai";
        dai_link->cpus->dai_name        = dev_name(dev);
        dai_link->codecs->name          = "snd-soc-dummy";
-       dai_link->platforms->name       = dev_name(dev);
 
        card->dai_link  = dai_link;
        card->num_links = 1;
index 425d66edbf8671fd6713c5b21f5cea20c2576514..49930baf5e4d6e8bb3d2e5183058e9c0bf2e0431 100644 (file)
@@ -717,13 +717,11 @@ static int atmel_i2s_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int atmel_i2s_remove(struct platform_device *pdev)
+static void atmel_i2s_remove(struct platform_device *pdev)
 {
        struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
 
        clk_disable_unprepare(dev->pclk);
-
-       return 0;
 }
 
 static struct platform_driver atmel_i2s_driver = {
@@ -732,7 +730,7 @@ static struct platform_driver atmel_i2s_driver = {
                .of_match_table = of_match_ptr(atmel_i2s_dt_ids),
        },
        .probe          = atmel_i2s_probe,
-       .remove         = atmel_i2s_remove,
+       .remove_new     = atmel_i2s_remove,
 };
 module_platform_driver(atmel_i2s_driver);
 
index 12cd40b156440bf37159afff33848fc589659ad8..00c7b3a34ef5197080b1d6939fe894015a368514 100644 (file)
@@ -496,24 +496,21 @@ static int atmel_pdmic_asoc_card_init(struct device *dev,
        if (!dai_link)
                return -ENOMEM;
 
-       comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
+       comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
        if (!comp)
                return -ENOMEM;
 
        dai_link->cpus          = &comp[0];
        dai_link->codecs        = &comp[1];
-       dai_link->platforms     = &comp[2];
 
        dai_link->num_cpus      = 1;
        dai_link->num_codecs    = 1;
-       dai_link->num_platforms = 1;
 
        dai_link->name                  = "PDMIC";
        dai_link->stream_name           = "PDMIC PCM";
        dai_link->codecs->dai_name      = "snd-soc-dummy-dai";
        dai_link->cpus->dai_name        = dev_name(dev);
        dai_link->codecs->name          = "snd-soc-dummy";
-       dai_link->platforms->name       = dev_name(dev);
 
        card->dai_link  = dai_link;
        card->num_links = 1;
index 9c974c4e187d2a8772a892a838b42f6ec1c0c61c..00e98136bec25d2445c431a10f73d379a6f7a24d 100644 (file)
@@ -161,7 +161,7 @@ err_set_audio:
        return ret;
 }
 
-static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
+static void atmel_asoc_wm8904_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
@@ -171,8 +171,6 @@ static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
        atmel_ssc_put_audio(id);
-
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -190,7 +188,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = {
                .pm             = &snd_soc_pm_ops,
        },
        .probe = atmel_asoc_wm8904_probe,
-       .remove = atmel_asoc_wm8904_remove,
+       .remove_new = atmel_asoc_wm8904_remove,
 };
 
 module_platform_driver(atmel_asoc_wm8904_driver);
index 6dfb96c576ff36747fc2ae991f3407cb2827ba17..7c83d48ca1a021a04ba456824f00a246ce6b4d86 100644 (file)
@@ -1088,13 +1088,11 @@ static int mchp_i2s_mcc_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int mchp_i2s_mcc_remove(struct platform_device *pdev)
+static void mchp_i2s_mcc_remove(struct platform_device *pdev)
 {
        struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
 
        clk_disable_unprepare(dev->pclk);
-
-       return 0;
 }
 
 static struct platform_driver mchp_i2s_mcc_driver = {
@@ -1103,7 +1101,7 @@ static struct platform_driver mchp_i2s_mcc_driver = {
                .of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
        },
        .probe          = mchp_i2s_mcc_probe,
-       .remove         = mchp_i2s_mcc_remove,
+       .remove_new     = mchp_i2s_mcc_remove,
 };
 module_platform_driver(mchp_i2s_mcc_driver);
 
index 1aed3baa9369708336378c760f6a2a3e6bec5373..da23855a0e40392e67e03b2636180718a5d8df7b 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <dt-bindings/sound/microchip,pdmc.h>
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -49,8 +50,6 @@
 #define MCHP_PDMC_MR_OSR256            (3 << 16)
 
 #define MCHP_PDMC_MR_SINCORDER_MASK    GENMASK(23, 20)
-#define MCHP_PDMC_MR_SINCORDER(order)  (((order) << 20) & \
-                                        MCHP_PDMC_MR_SINCORDER_MASK)
 
 #define MCHP_PDMC_MR_SINC_OSR_MASK     GENMASK(27, 24)
 #define MCHP_PDMC_MR_SINC_OSR_DIS      (0 << 24)
@@ -62,8 +61,6 @@
 #define MCHP_PDMC_MR_SINC_OSR_256      (6 << 24)
 
 #define MCHP_PDMC_MR_CHUNK_MASK                GENMASK(31, 28)
-#define MCHP_PDMC_MR_CHUNK(chunk)      (((chunk) << 28) & \
-                                        MCHP_PDMC_MR_CHUNK_MASK)
 
 /*
  * ---- Configuration Register (Read/Write) ----
@@ -617,10 +614,10 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
 
        mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr);
 
-       mr_val |= MCHP_PDMC_MR_SINCORDER(dd->sinc_order);
+       mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order);
 
        dd->addr.maxburst = mchp_pdmc_period_to_maxburst(snd_pcm_lib_period_bytes(substream));
-       mr_val |= MCHP_PDMC_MR_CHUNK(dd->addr.maxburst);
+       mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst);
        dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst);
 
        snd_soc_component_update_bits(comp, MCHP_PDMC_MR,
@@ -762,12 +759,10 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd,
        int ret;
 
        ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(dd->dev, "failed to add channel map controls: %d\n", ret);
-               return ret;
-       }
 
-       return 0;
+       return ret;
 }
 
 static struct snd_soc_dai_driver mchp_pdmc_dai = {
@@ -788,7 +783,7 @@ static struct snd_soc_dai_driver mchp_pdmc_dai = {
 /* PDMC interrupt handler */
 static irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id)
 {
-       struct mchp_pdmc *dd = (struct mchp_pdmc *)dev_id;
+       struct mchp_pdmc *dd = dev_id;
        u32 isr, msr, pending;
        irqreturn_t ret = IRQ_NONE;
 
@@ -1082,7 +1077,7 @@ static int mchp_pdmc_probe(struct platform_device *pdev)
        }
 
        ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0,
-                              dev_name(&pdev->dev), (void *)dd);
+                              dev_name(&pdev->dev), dd);
        if (ret < 0) {
                dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
                        irq, ret);
@@ -1134,7 +1129,7 @@ pm_runtime_suspend:
        return ret;
 }
 
-static int mchp_pdmc_remove(struct platform_device *pdev)
+static void mchp_pdmc_remove(struct platform_device *pdev)
 {
        struct mchp_pdmc *dd = platform_get_drvdata(pdev);
 
@@ -1142,8 +1137,6 @@ static int mchp_pdmc_remove(struct platform_device *pdev)
                mchp_pdmc_runtime_suspend(dd->dev);
 
        pm_runtime_disable(dd->dev);
-
-       return 0;
 }
 
 static const struct of_device_id mchp_pdmc_of_match[] = {
@@ -1168,7 +1161,7 @@ static struct platform_driver mchp_pdmc_driver = {
                .pm             = pm_ptr(&mchp_pdmc_pm_ops),
        },
        .probe  = mchp_pdmc_probe,
-       .remove = mchp_pdmc_remove,
+       .remove_new = mchp_pdmc_remove,
 };
 module_platform_driver(mchp_pdmc_driver);
 
index eb0c0ef4541e239b086ffa078f6016ee7d10360c..ff6aba143aeeadcee601b3e0cf9976ed452b36a3 100644 (file)
@@ -1183,20 +1183,18 @@ pm_runtime_disable:
        return err;
 }
 
-static int mchp_spdifrx_remove(struct platform_device *pdev)
+static void mchp_spdifrx_remove(struct platform_device *pdev)
 {
        struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev);
 
        pm_runtime_disable(dev->dev);
        if (!pm_runtime_status_suspended(dev->dev))
                mchp_spdifrx_runtime_suspend(dev->dev);
-
-       return 0;
 }
 
 static struct platform_driver mchp_spdifrx_driver = {
        .probe  = mchp_spdifrx_probe,
-       .remove = mchp_spdifrx_remove,
+       .remove_new = mchp_spdifrx_remove,
        .driver = {
                .name   = "mchp_spdifrx",
                .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids),
index 20d135c718b070f479f60f91b23c6a5cbf1dbc21..1d3e17119888d4686e7001a059d025e2cff48000 100644 (file)
 
 /* Valid Bits per Sample */
 #define SPDIFTX_MR_VBPS_MASK           GENMASK(13, 8)
-#define SPDIFTX_MR_VBPS(bps)           FIELD_PREP(SPDIFTX_MR_VBPS_MASK, bps)
 
 /* Chunk Size */
 #define SPDIFTX_MR_CHUNK_MASK          GENMASK(19, 16)
-#define SPDIFTX_MR_CHUNK(size)         FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, size)
 
 /* Validity Bits for Channels 1 and 2 */
 #define SPDIFTX_MR_VALID1                      BIT(24)
@@ -89,7 +87,6 @@
 
 /* Bytes per Sample */
 #define SPDIFTX_MR_BPS_MASK            GENMASK(29, 28)
-#define SPDIFTX_MR_BPS(bytes)          FIELD_PREP(SPDIFTX_MR_BPS_MASK, (bytes - 1))
 
 /*
  * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
@@ -309,15 +306,10 @@ static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
        struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
-       u32 mr;
-       int running;
        int ret;
 
        /* do not start/stop while channel status or user data is updated */
        spin_lock(&ctrl->lock);
-       regmap_read(dev->regmap, SPDIFTX_MR, &mr);
-       running = !!(mr & SPDIFTX_MR_TXEN_ENABLE);
-
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_START:
@@ -326,10 +318,8 @@ static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
                dev->suspend_irq = 0;
                fallthrough;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (!running) {
-                       mr &= ~SPDIFTX_MR_TXEN_MASK;
-                       mr |= SPDIFTX_MR_TXEN_ENABLE;
-               }
+               ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+                                        SPDIFTX_MR_TXEN_ENABLE);
                break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
                regmap_read(dev->regmap, SPDIFTX_IMR, &dev->suspend_irq);
@@ -339,20 +329,15 @@ static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
                             SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
                fallthrough;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (running) {
-                       mr &= ~SPDIFTX_MR_TXEN_MASK;
-                       mr |= SPDIFTX_MR_TXEN_DISABLE;
-               }
+               ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+                                        SPDIFTX_MR_TXEN_DISABLE);
                break;
        default:
-               spin_unlock(&ctrl->lock);
-               return -EINVAL;
+               ret = -EINVAL;
        }
-
-       ret = regmap_write(dev->regmap, SPDIFTX_MR, mr);
        spin_unlock(&ctrl->lock);
        if (ret)
-               dev_err(dev->dev, "unable to disable TX: %d\n", ret);
+               dev_err(dev->dev, "unable to start/stop TX: %d\n", ret);
 
        return ret;
 }
@@ -402,47 +387,47 @@ static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
                        params_channels(params));
                return -EINVAL;
        }
-       mr |= SPDIFTX_MR_CHUNK(dev->playback.maxburst);
+       mr |= FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, dev->playback.maxburst);
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
-               mr |= SPDIFTX_MR_VBPS(8);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 8);
                break;
        case SNDRV_PCM_FORMAT_S16_BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S16_LE:
-               mr |= SPDIFTX_MR_VBPS(16);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 16);
                break;
        case SNDRV_PCM_FORMAT_S18_3BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S18_3LE:
-               mr |= SPDIFTX_MR_VBPS(18);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 18);
                break;
        case SNDRV_PCM_FORMAT_S20_3BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S20_3LE:
-               mr |= SPDIFTX_MR_VBPS(20);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 20);
                break;
        case SNDRV_PCM_FORMAT_S24_3BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S24_3LE:
-               mr |= SPDIFTX_MR_VBPS(24);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
                break;
        case SNDRV_PCM_FORMAT_S24_BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S24_LE:
-               mr |= SPDIFTX_MR_VBPS(24);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
                break;
        case SNDRV_PCM_FORMAT_S32_BE:
                mr |= SPDIFTX_MR_ENDIAN_BIG;
                fallthrough;
        case SNDRV_PCM_FORMAT_S32_LE:
-               mr |= SPDIFTX_MR_VBPS(32);
+               mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 32);
                break;
        default:
                dev_err(dev->dev, "unsupported PCM format: %d\n",
@@ -450,7 +435,7 @@ static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       mr |= SPDIFTX_MR_BPS(bps);
+       mr |= FIELD_PREP(SPDIFTX_MR_BPS_MASK, bps - 1);
 
        switch (params_rate(params)) {
        case 22050:
@@ -891,7 +876,7 @@ pm_runtime_suspend:
        return err;
 }
 
-static int mchp_spdiftx_remove(struct platform_device *pdev)
+static void mchp_spdiftx_remove(struct platform_device *pdev)
 {
        struct mchp_spdiftx_dev *dev = platform_get_drvdata(pdev);
 
@@ -899,13 +884,11 @@ static int mchp_spdiftx_remove(struct platform_device *pdev)
                mchp_spdiftx_runtime_suspend(dev->dev);
 
        pm_runtime_disable(dev->dev);
-
-       return 0;
 }
 
 static struct platform_driver mchp_spdiftx_driver = {
        .probe  = mchp_spdiftx_probe,
-       .remove = mchp_spdiftx_remove,
+       .remove_new = mchp_spdiftx_remove,
        .driver = {
                .name   = "mchp_spdiftx",
                .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids),
index 954460719aa3d0b8ad225fad0470ba3acad8033e..30c87c2c1b0bd1a70b49bf3fe22e1f7aeec36472 100644 (file)
@@ -155,11 +155,9 @@ put_codec_node:
        return ret;
 }
 
-static int snd_proto_remove(struct platform_device *pdev)
+static void snd_proto_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_card(&snd_proto);
-
-       return 0;
 }
 
 static const struct of_device_id snd_proto_of_match[] = {
@@ -174,7 +172,7 @@ static struct platform_driver snd_proto_driver = {
                .of_match_table = snd_proto_of_match,
        },
        .probe    = snd_proto_probe,
-       .remove  = snd_proto_remove,
+       .remove_new      = snd_proto_remove,
 };
 
 module_platform_driver(snd_proto_driver);
index 785b9d01d8afeb9b34dad6fb7a320a82b5080461..baf38964b49151fae43572087de7ba6aa18c7388 100644 (file)
@@ -186,14 +186,12 @@ err:
        return ret;
 }
 
-static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
+static void at91sam9g20ek_audio_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        snd_soc_unregister_card(card);
        atmel_ssc_put_audio(0);
-
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -210,7 +208,7 @@ static struct platform_driver at91sam9g20ek_audio_driver = {
                .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
        },
        .probe  = at91sam9g20ek_audio_probe,
-       .remove = at91sam9g20ek_audio_remove,
+       .remove_new = at91sam9g20ek_audio_remove,
 };
 
 module_platform_driver(at91sam9g20ek_audio_driver);
index 99310e40e7a6203e3f0089d7a8957e16f379350c..cd1d59a90e0218ef2b1824fe7db188dc2911804c 100644 (file)
@@ -176,14 +176,12 @@ out:
        return ret;
 }
 
-static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+static void sam9x5_wm8731_driver_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct sam9x5_drvdata *priv = card->drvdata;
 
        atmel_ssc_put_audio(priv->ssc_id);
-
-       return 0;
 }
 
 static const struct of_device_id sam9x5_wm8731_of_match[] = {
@@ -198,7 +196,7 @@ static struct platform_driver sam9x5_wm8731_driver = {
                .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
        },
        .probe = sam9x5_wm8731_driver_probe,
-       .remove = sam9x5_wm8731_driver_remove,
+       .remove_new = sam9x5_wm8731_driver_remove,
 };
 module_platform_driver(sam9x5_wm8731_driver);
 
index ef537de7719c70f63e96985f428edaeda3960ea9..efead272d92b163f33db8262f99ce608657cbbcb 100644 (file)
@@ -412,15 +412,13 @@ err_disable_ana:
        return ret;
 }
 
-static int tse850_remove(struct platform_device *pdev)
+static void tse850_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
 
        snd_soc_unregister_card(card);
        regulator_disable(tse850->ana);
-
-       return 0;
 }
 
 static const struct of_device_id tse850_dt_ids[] = {
@@ -435,7 +433,7 @@ static struct platform_driver tse850_driver = {
                .of_match_table = of_match_ptr(tse850_dt_ids),
        },
        .probe = tse850_probe,
-       .remove = tse850_remove,
+       .remove_new = tse850_remove,
 };
 
 module_platform_driver(tse850_driver);
index b18512ca25781ab150f9f66a6a6a52a126100473..a11d6841afc2b41348b687fd85a5fab79298fc24 100644 (file)
@@ -285,7 +285,7 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
        return 0;
 }
 
-static int au1xac97c_drvremove(struct platform_device *pdev)
+static void au1xac97c_drvremove(struct platform_device *pdev)
 {
        struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
 
@@ -294,8 +294,6 @@ static int au1xac97c_drvremove(struct platform_device *pdev)
        WR(ctx, AC97_ENABLE, EN_D);     /* clock off, disable */
 
        ac97c_workdata = NULL;  /* MDEV */
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -338,7 +336,7 @@ static struct platform_driver au1xac97c_driver = {
                .pm     = AU1XPSCAC97_PMOPS,
        },
        .probe          = au1xac97c_drvprobe,
-       .remove         = au1xac97c_drvremove,
+       .remove_new     = au1xac97c_drvremove,
 };
 
 module_platform_driver(au1xac97c_driver);
index b15c8baa9ee454903017e9dfe3a05e83c2799ec4..064406080d72635c727b1f1de2a0dd5da32461ad 100644 (file)
@@ -270,15 +270,13 @@ static int au1xi2s_drvprobe(struct platform_device *pdev)
                                          &au1xi2s_dai_driver, 1);
 }
 
-static int au1xi2s_drvremove(struct platform_device *pdev)
+static void au1xi2s_drvremove(struct platform_device *pdev)
 {
        struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
 
        snd_soc_unregister_component(&pdev->dev);
 
        WR(ctx, I2S_ENABLE, EN_D);      /* clock off, disable */
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -315,7 +313,7 @@ static struct platform_driver au1xi2s_driver = {
                .pm     = AU1XI2SC_PMOPS,
        },
        .probe          = au1xi2s_drvprobe,
-       .remove         = au1xi2s_drvremove,
+       .remove_new     = au1xi2s_drvremove,
 };
 
 module_platform_driver(au1xi2s_driver);
index b536394b9ca08309f9c8391d67424107592741fe..9fd91aea7d1a8a9eda05e3f76d5ea006e53f8fa0 100644 (file)
@@ -421,7 +421,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
        return 0;
 }
 
-static int au1xpsc_ac97_drvremove(struct platform_device *pdev)
+static void au1xpsc_ac97_drvremove(struct platform_device *pdev)
 {
        struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
 
@@ -434,8 +434,6 @@ static int au1xpsc_ac97_drvremove(struct platform_device *pdev)
        wmb(); /* drain writebuffer */
 
        au1xpsc_ac97_workdata = NULL;   /* MDEV */
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -488,7 +486,7 @@ static struct platform_driver au1xpsc_ac97_driver = {
                .pm     = AU1XPSCAC97_PMOPS,
        },
        .probe          = au1xpsc_ac97_drvprobe,
-       .remove         = au1xpsc_ac97_drvremove,
+       .remove_new     = au1xpsc_ac97_drvremove,
 };
 
 module_platform_driver(au1xpsc_ac97_driver);
index 79b5ae4e494cb6410350cbc0e9331674193cbadc..52734dec8247215455bfb1adc4eb52f685e16944 100644 (file)
@@ -344,7 +344,7 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
                                &au1xpsc_i2s_component, &wd->dai_drv, 1);
 }
 
-static int au1xpsc_i2s_drvremove(struct platform_device *pdev)
+static void au1xpsc_i2s_drvremove(struct platform_device *pdev)
 {
        struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
 
@@ -352,8 +352,6 @@ static int au1xpsc_i2s_drvremove(struct platform_device *pdev)
        wmb(); /* drain writebuffer */
        __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        wmb(); /* drain writebuffer */
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -406,7 +404,7 @@ static struct platform_driver au1xpsc_i2s_driver = {
                .pm     = AU1XPSCI2S_PMOPS,
        },
        .probe          = au1xpsc_i2s_drvprobe,
-       .remove         = au1xpsc_i2s_drvremove,
+       .remove_new     = au1xpsc_i2s_drvremove,
 };
 
 module_platform_driver(au1xpsc_i2s_driver);
index 2da1384ffe911a586d659eaa999c2a01d72ff961..18c51dbbc8dc507d334652b7e4c2f0e2e7d18be4 100644 (file)
@@ -289,10 +289,9 @@ static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int bcm63xx_i2s_dev_remove(struct platform_device *pdev)
+static void bcm63xx_i2s_dev_remove(struct platform_device *pdev)
 {
        bcm63xx_soc_platform_remove(pdev);
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -308,7 +307,7 @@ static struct platform_driver bcm63xx_i2s_driver = {
                .of_match_table = of_match_ptr(snd_soc_bcm_audio_match),
        },
        .probe = bcm63xx_i2s_dev_probe,
-       .remove = bcm63xx_i2s_dev_remove,
+       .remove_new = bcm63xx_i2s_dev_remove,
 };
 
 module_platform_driver(bcm63xx_i2s_driver);
index 2a92e33e1fbf1d0b22a59d353fe7312793139683..8638bf22ef5c2e0f9a1c9bc3d96d469cefbbf2f9 100644 (file)
@@ -1377,11 +1377,9 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int cygnus_ssp_remove(struct platform_device *pdev)
+static void cygnus_ssp_remove(struct platform_device *pdev)
 {
        cygnus_soc_platform_unregister(&pdev->dev);
-
-       return 0;
 }
 
 static const struct of_device_id cygnus_ssp_of_match[] = {
@@ -1392,7 +1390,7 @@ MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
 
 static struct platform_driver cygnus_ssp_driver = {
        .probe          = cygnus_ssp_probe,
-       .remove         = cygnus_ssp_remove,
+       .remove_new     = cygnus_ssp_remove,
        .driver         = {
                .name   = "cygnus-ssp",
                .of_match_table = cygnus_ssp_of_match,
index 34870c2d0cbafce1c05ddc893d703369d7576196..38a83c4dcc2d6488728dac456b941f2bec0b9a8f 100644 (file)
@@ -8,7 +8,11 @@ config SND_EP93XX_SOC
          the EP93xx I2S or AC97 interfaces.
 
 config SND_EP93XX_SOC_I2S
-       tristate
+       tristate "I2S controller support for the Cirrus Logic EP93xx series"
+       depends on SND_EP93XX_SOC
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the EP93xx I2S interface.
 
 if SND_EP93XX_SOC_I2S
 
index 3852902029120ab344d5583b965ad5e2238564ae..f49caab21a256ba804a49edd2d4dcc9821ac5ea8 100644 (file)
@@ -93,14 +93,12 @@ static int edb93xx_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int edb93xx_remove(struct platform_device *pdev)
+static void edb93xx_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        snd_soc_unregister_card(card);
        ep93xx_i2s_release();
-
-       return 0;
 }
 
 static struct platform_driver edb93xx_driver = {
@@ -108,7 +106,7 @@ static struct platform_driver edb93xx_driver = {
                .name   = "edb93xx-audio",
        },
        .probe          = edb93xx_probe,
-       .remove         = edb93xx_remove,
+       .remove_new     = edb93xx_remove,
 };
 
 module_platform_driver(edb93xx_driver);
index 70840f27d4a797ac9e98600e6ca85ca05267a48f..afc6b5b570ea042f0f78d63a9ff0a4066ebaf5fa 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/of.h>
 
 #include <sound/core.h>
 #include <sound/dmaengine_pcm.h>
@@ -208,6 +209,16 @@ static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
        return 0;
 }
 
+static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+       ep93xx_i2s_enable(info, substream->stream);
+
+       return 0;
+}
+
 static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
@@ -348,7 +359,6 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
        if (err)
                return err;
 
-       ep93xx_i2s_enable(info, substream->stream);
        return 0;
 }
 
@@ -397,6 +407,7 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component)
 #endif
 
 static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+       .startup        = ep93xx_i2s_startup,
        .shutdown       = ep93xx_i2s_shutdown,
        .hw_params      = ep93xx_i2s_hw_params,
        .set_sysclk     = ep93xx_i2s_set_sysclk,
@@ -495,21 +506,27 @@ fail:
        return err;
 }
 
-static int ep93xx_i2s_remove(struct platform_device *pdev)
+static void ep93xx_i2s_remove(struct platform_device *pdev)
 {
        struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
 
        clk_put(info->lrclk);
        clk_put(info->sclk);
        clk_put(info->mclk);
-       return 0;
 }
 
+static const struct of_device_id ep93xx_i2s_of_ids[] = {
+       { .compatible = "cirrus,ep9301-i2s" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, ep93xx_i2s_of_ids);
+
 static struct platform_driver ep93xx_i2s_driver = {
        .probe  = ep93xx_i2s_probe,
-       .remove = ep93xx_i2s_remove,
+       .remove_new = ep93xx_i2s_remove,
        .driver = {
                .name   = "ep93xx-i2s",
+               .of_match_table = ep93xx_i2s_of_ids,
        },
 };
 
index 07747565c3b51c795e6472c560d44dcb333d4739..79d2362ad055ab1c4b4d1daac3f6f5023eeddab0 100644 (file)
@@ -68,6 +68,9 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_CS35L41_I2C
        imply SND_SOC_CS35L45_I2C
        imply SND_SOC_CS35L45_SPI
+       imply SND_SOC_CS35L56_I2C
+       imply SND_SOC_CS35L56_SPI
+       imply SND_SOC_CS35L56_SDW
        imply SND_SOC_CS42L42
        imply SND_SOC_CS42L42_SDW
        imply SND_SOC_CS42L51_I2C
@@ -130,6 +133,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_MAX98925
        imply SND_SOC_MAX98926
        imply SND_SOC_MAX98927
+       imply SND_SOC_MAX98363
        imply SND_SOC_MAX98373_I2C
        imply SND_SOC_MAX98373_SDW
        imply SND_SOC_MAX98390
@@ -200,6 +204,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_RT711_SDW
        imply SND_SOC_RT711_SDCA_SDW
        imply SND_SOC_RT712_SDCA_SDW
+       imply SND_SOC_RT712_SDCA_DMIC_SDW
        imply SND_SOC_RT715_SDW
        imply SND_SOC_RT715_SDCA_SDW
        imply SND_SOC_RT1308_SDW
@@ -364,6 +369,9 @@ config SND_SOC_WM_ADSP
        default y if SND_SOC_WM2200=y
        default y if SND_SOC_CS35L41_SPI=y
        default y if SND_SOC_CS35L41_I2C=y
+       default y if SND_SOC_CS35L45_SPI=y
+       default y if SND_SOC_CS35L45_I2C=y
+       default y if SND_SOC_CS35L56=y
        default m if SND_SOC_MADERA=m
        default m if SND_SOC_CS47L24=m
        default m if SND_SOC_WM5102=m
@@ -371,6 +379,9 @@ config SND_SOC_WM_ADSP
        default m if SND_SOC_WM2200=m
        default m if SND_SOC_CS35L41_SPI=m
        default m if SND_SOC_CS35L41_I2C=m
+       default m if SND_SOC_CS35L45_SPI=m
+       default m if SND_SOC_CS35L45_I2C=m
+       default m if SND_SOC_CS35L56=m
 
 config SND_SOC_AB8500_CODEC
        tristate
@@ -711,6 +722,41 @@ config SND_SOC_CS35L45_I2C
          Enable support for Cirrus Logic CS35L45 smart speaker amplifier
          with I2C control.
 
+config SND_SOC_CS35L56
+       tristate
+
+config SND_SOC_CS35L56_SHARED
+       tristate
+
+config SND_SOC_CS35L56_I2C
+       tristate "Cirrus Logic CS35L56 CODEC (I2C)"
+       depends on I2C
+       depends on SOUNDWIRE || !SOUNDWIRE
+       select REGMAP_I2C
+       select SND_SOC_CS35L56
+       select SND_SOC_CS35L56_SHARED
+       help
+         Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control
+
+config SND_SOC_CS35L56_SPI
+       tristate "Cirrus Logic CS35L56 CODEC (SPI)"
+       depends on SPI_MASTER
+       depends on SOUNDWIRE || !SOUNDWIRE
+       select REGMAP_SPI
+       select SND_SOC_CS35L56
+       select SND_SOC_CS35L56_SHARED
+       help
+         Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control
+
+config SND_SOC_CS35L56_SDW
+       tristate "Cirrus Logic CS35L56 CODEC (SDW)"
+       depends on SOUNDWIRE
+       select REGMAP
+       select SND_SOC_CS35L56
+       select SND_SOC_CS35L56_SHARED
+       help
+         Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control
+
 config SND_SOC_CS42L42_CORE
        tristate
 
@@ -1089,6 +1135,16 @@ config SND_SOC_MAX98520
 
          To compile this driver as a module, choose M here.
 
+config SND_SOC_MAX98363
+       tristate "Analog Devices MAX98363 Soundwire Speaker Amplifier"
+       depends on SOUNDWIRE
+       select REGMAP_SOUNDWIRE
+       help
+         Enable support for Analog Devices MAX98363 Soundwire
+         amplifier. MAX98363 supports the MIPI SoundWire v1.2
+         compatible interface for audio and control data.
+         This amplifier does not support I2C and I2S.
+
 config SND_SOC_MAX98373
        tristate
 
@@ -1477,6 +1533,12 @@ config SND_SOC_RT712_SDCA_SDW
        select REGMAP_SOUNDWIRE
        select REGMAP_SOUNDWIRE_MBQ
 
+config SND_SOC_RT712_SDCA_DMIC_SDW
+       tristate "Realtek RT712 SDCA DMIC Codec - SDW"
+       depends on SOUNDWIRE
+       select REGMAP_SOUNDWIRE
+       select REGMAP_SOUNDWIRE_MBQ
+
 config SND_SOC_RT715
        tristate
 
index f1ca18f7946cac604baf87fc4b61167c07fce796..5cdbae88e6e357f888b7485aab9fea8c83b07dda 100644 (file)
@@ -66,6 +66,11 @@ snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
 snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o
 snd-soc-cs35l45-spi-objs := cs35l45-spi.o
 snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
+snd-soc-cs35l56-objs := cs35l56.o
+snd-soc-cs35l56-shared-objs := cs35l56-shared.o
+snd-soc-cs35l56-i2c-objs := cs35l56-i2c.o
+snd-soc-cs35l56-spi-objs := cs35l56-spi.o
+snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o
 snd-soc-cs42l42-objs := cs42l42.o
 snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o
 snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o
@@ -143,6 +148,7 @@ snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
 snd-soc-max98927-objs := max98927.o
 snd-soc-max98520-objs := max98520.o
+snd-soc-max98363-objs := max98363.o
 snd-soc-max98373-objs := max98373.o
 snd-soc-max98373-i2c-objs := max98373-i2c.o
 snd-soc-max98373-sdw-objs := max98373-sdw.o
@@ -228,6 +234,7 @@ snd-soc-rt700-objs := rt700.o rt700-sdw.o
 snd-soc-rt711-objs := rt711.o rt711-sdw.o
 snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
 snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o
+snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o
 snd-soc-rt715-objs := rt715.o rt715-sdw.o
 snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
 snd-soc-rt9120-objs := rt9120.o
@@ -433,6 +440,11 @@ obj-$(CONFIG_SND_SOC_CS35L41_I2C)  += snd-soc-cs35l41-i2c.o
 obj-$(CONFIG_SND_SOC_CS35L45)  += snd-soc-cs35l45.o
 obj-$(CONFIG_SND_SOC_CS35L45_SPI)      += snd-soc-cs35l45-spi.o
 obj-$(CONFIG_SND_SOC_CS35L45_I2C)      += snd-soc-cs35l45-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56)  += snd-soc-cs35l56.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED)   += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_I2C)      += snd-soc-cs35l56-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56_SPI)      += snd-soc-cs35l56-spi.o
+obj-$(CONFIG_SND_SOC_CS35L56_SDW)      += snd-soc-cs35l56-sdw.o
 obj-$(CONFIG_SND_SOC_CS42L42_CORE)     += snd-soc-cs42l42.o
 obj-$(CONFIG_SND_SOC_CS42L42)  += snd-soc-cs42l42-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L42_SDW)      += snd-soc-cs42l42-sdw.o
@@ -505,6 +517,7 @@ obj-$(CONFIG_SND_SOC_MAX98925)      += snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
 obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o
+obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o
 obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o
 obj-$(CONFIG_SND_SOC_MAX98373_I2C)   += snd-soc-max98373-i2c.o
 obj-$(CONFIG_SND_SOC_MAX98373_SDW)   += snd-soc-max98373-sdw.o
@@ -591,6 +604,7 @@ obj-$(CONFIG_SND_SOC_RT700)     += snd-soc-rt700.o
 obj-$(CONFIG_SND_SOC_RT711)     += snd-soc-rt711.o
 obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW)     += snd-soc-rt711-sdca.o
 obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW)     += snd-soc-rt712-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW)     += snd-soc-rt712-sdca-dmic.o
 obj-$(CONFIG_SND_SOC_RT715)     += snd-soc-rt715.o
 obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW)     += snd-soc-rt715-sdca.o
 obj-$(CONFIG_SND_SOC_RT9120)   += snd-soc-rt9120.o
index 8370bec27a9cc750da947b6de5e88b3cfc37056b..207c5c95f35ae5a47e1a7973ef91f86dd9632400 100644 (file)
@@ -55,7 +55,7 @@ static const struct spi_device_id adau1977_spi_ids[] = {
 };
 MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
 
-static const struct of_device_id adau1977_spi_of_match[] = {
+static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
         { .compatible = "adi,adau1977" },
         { .compatible = "adi,adau1978" },
         { .compatible = "adi,adau1979" },
index 04be71435491e27089cc58e0276697707545db1f..8538e2871c5fa7b672d81ca828364f47c2c38821 100644 (file)
@@ -1114,12 +1114,31 @@ static const struct reg_sequence cs35l41_reset_to_safe[] = {
        { 0x00000040,                   0x00000033 },
 };
 
+static const struct reg_sequence cs35l41_actv_seq[] = {
+       /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */
+       {CS35L41_MDSYNC_EN,        0x00003000},
+       /* BST_CTL_SEL = MDSYNC */
+       {CS35L41_BSTCVRT_VCTRL2,    0x00000002},
+};
+
+static const struct reg_sequence cs35l41_pass_seq[] = {
+       /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */
+       {CS35L41_MDSYNC_EN,        0x00001000},
+       /* BST_EN = 0 */
+       {CS35L41_PWR_CTRL2,        0x00003300},
+       /* BST_CTL_SEL = MDSYNC */
+       {CS35L41_BSTCVRT_VCTRL2,    0x00000002},
+};
+
 int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
                       struct cs35l41_hw_cfg *hw_cfg)
 {
        int ret;
 
        switch (hw_cfg->bst_type) {
+       case CS35L41_SHD_BOOST_ACTV:
+               regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq));
+               fallthrough;
        case CS35L41_INT_BOOST:
                ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind,
                                           hw_cfg->bst_cap, hw_cfg->bst_ipk);
@@ -1138,6 +1157,10 @@ int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
                ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
                                         CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT);
                break;
+       case CS35L41_SHD_BOOST_PASS:
+               ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq,
+                                            ARRAY_SIZE(cs35l41_pass_seq));
+               break;
        default:
                dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type);
                ret = -EINVAL;
@@ -1165,11 +1188,59 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
 }
 EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
 
-int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable)
+int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
+                         struct completion *pll_lock)
 {
        int ret;
+       unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3;
+       struct reg_sequence cs35l41_mdsync_down_seq[] = {
+               {CS35L41_PWR_CTRL3,             0},
+               {CS35L41_GPIO_PAD_CONTROL,      0},
+               {CS35L41_PWR_CTRL1,             0, 3000},
+       };
+       struct reg_sequence cs35l41_mdsync_up_seq[] = {
+               {CS35L41_PWR_CTRL3,     0},
+               {CS35L41_PWR_CTRL1,     0x00000000, 3000},
+               {CS35L41_PWR_CTRL1,     0x00000001, 3000},
+       };
 
        switch (b_type) {
+       case CS35L41_SHD_BOOST_ACTV:
+       case CS35L41_SHD_BOOST_PASS:
+               regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+               regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control);
+
+               pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK;
+               pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT;
+
+               gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ;
+               gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT;
+
+               pad_control &= ~CS35L41_GPIO1_CTRL_MASK;
+               pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK;
+
+               cs35l41_mdsync_down_seq[0].def = pwr_ctrl3;
+               cs35l41_mdsync_down_seq[1].def = pad_control;
+               cs35l41_mdsync_down_seq[2].def = pwr_ctrl1;
+               ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq,
+                                            ARRAY_SIZE(cs35l41_mdsync_down_seq));
+               if (!enable)
+                       break;
+
+               if (!pll_lock)
+                       return -EINVAL;
+
+               ret = wait_for_completion_timeout(pll_lock, msecs_to_jiffies(1000));
+               if (ret == 0) {
+                       ret = -ETIMEDOUT;
+               } else {
+                       regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+                       pwr_ctrl3 |= CS35L41_SYNC_EN_MASK;
+                       cs35l41_mdsync_up_seq[0].def = pwr_ctrl3;
+                       ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq,
+                                                    ARRAY_SIZE(cs35l41_mdsync_up_seq));
+               }
+               break;
        case CS35L41_INT_BOOST:
                ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
                                         enable << CS35L41_GLOBAL_EN_SHIFT);
index c223d83e02cfb26eb769dcca3339cdfcfbf0937b..6ac501f008ecafdd82a2bde2704e432b4b39869d 100644 (file)
@@ -150,6 +150,7 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
        { 5644800,      16,     24 },
        { 6000000,      16,     24 },
        { 6144000,      16,     24 },
+       { 12288000,     0,      0 },
 };
 
 static int cs35l41_get_fs_mon_config_index(int freq)
@@ -356,6 +357,30 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
        WM_ADSP_FW_CONTROL("DSP1", 0),
 };
 
+static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable)
+{
+       switch (cs35l41->hw_cfg.bst_type) {
+       case CS35L41_INT_BOOST:
+       case CS35L41_SHD_BOOST_ACTV:
+               enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF;
+               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+                               enable << CS35L41_BST_EN_SHIFT);
+               break;
+       default:
+               break;
+       }
+}
+
+
+static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit,
+                                 unsigned int rel_err_bit)
+{
+       regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit);
+       regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0);
+}
+
 static irqreturn_t cs35l41_irq(int irq, void *data)
 {
        struct cs35l41_private *cs35l41 = data;
@@ -392,94 +417,49 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
         */
        if (status[0] & CS35L41_AMP_SHORT_ERR) {
                dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_AMP_SHORT_ERR);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_AMP_SHORT_ERR_RLS,
-                                  CS35L41_AMP_SHORT_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_AMP_SHORT_ERR_RLS, 0);
+               cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS);
                ret = IRQ_HANDLED;
        }
 
        if (status[0] & CS35L41_TEMP_WARN) {
                dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_TEMP_WARN);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_TEMP_WARN_ERR_RLS,
-                                  CS35L41_TEMP_WARN_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_TEMP_WARN_ERR_RLS, 0);
+               cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS);
                ret = IRQ_HANDLED;
        }
 
        if (status[0] & CS35L41_TEMP_ERR) {
                dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_TEMP_ERR);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_TEMP_ERR_RLS,
-                                  CS35L41_TEMP_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_TEMP_ERR_RLS, 0);
+               cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS);
                ret = IRQ_HANDLED;
        }
 
        if (status[0] & CS35L41_BST_OVP_ERR) {
                dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK, 0);
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_BST_OVP_ERR);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_OVP_ERR_RLS,
-                                  CS35L41_BST_OVP_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_OVP_ERR_RLS, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK,
-                                  CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+               cs35l41_boost_enable(cs35l41, 0);
+               cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS);
+               cs35l41_boost_enable(cs35l41, 1);
                ret = IRQ_HANDLED;
        }
 
        if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
                dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK, 0);
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_BST_DCM_UVP_ERR);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_UVP_ERR_RLS,
-                                  CS35L41_BST_UVP_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_UVP_ERR_RLS, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK,
-                                  CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+               cs35l41_boost_enable(cs35l41, 0);
+               cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS);
+               cs35l41_boost_enable(cs35l41, 1);
                ret = IRQ_HANDLED;
        }
 
        if (status[0] & CS35L41_BST_SHORT_ERR) {
                dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n");
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK, 0);
-               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
-                            CS35L41_BST_SHORT_ERR);
-               regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_SHORT_ERR_RLS,
-                                  CS35L41_BST_SHORT_ERR_RLS);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
-                                  CS35L41_BST_SHORT_ERR_RLS, 0);
-               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
-                                  CS35L41_BST_EN_MASK,
-                                  CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+               cs35l41_boost_enable(cs35l41, 0);
+               cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS);
+               cs35l41_boost_enable(cs35l41, 1);
+               ret = IRQ_HANDLED;
+       }
+
+       if (status[2] & CS35L41_PLL_LOCK) {
+               regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK);
+               complete(&cs35l41->pll_lock);
                ret = IRQ_HANDLED;
        }
 
@@ -520,10 +500,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
                                                cs35l41_pup_patch,
                                                ARRAY_SIZE(cs35l41_pup_patch));
 
-               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1);
+               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1,
+                                     &cs35l41->pll_lock);
                break;
        case SND_SOC_DAPM_POST_PMD:
-               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
+               cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
+                                     &cs35l41->pll_lock);
 
                ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
                                               val, val &  CS35L41_PDN_DONE_MASK,
@@ -830,6 +812,10 @@ static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
 static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
+       struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+       reinit_completion(&cs35l41->pll_lock);
+
        if (substream->runtime)
                return snd_pcm_hw_constraint_list(substream->runtime, 0,
                                                  SNDRV_PCM_HW_PARAM_RATE,
@@ -1037,9 +1023,21 @@ static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cf
        unsigned int val;
        int ret;
 
-       ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
-       if (ret >= 0)
-               hw_cfg->bst_type = val;
+       /* Some ACPI systems received the Shared Boost feature before the upstream driver,
+        * leaving those systems with deprecated _DSD properties.
+        * To correctly configure those systems add shared-boost-active and shared-boost-passive
+        * properties mapped to the correct value in boost-type.
+        * These two are not DT properties and should not be used in new systems designs.
+        */
+       if (device_property_read_bool(dev, "cirrus,shared-boost-active")) {
+               hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV;
+       } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) {
+               hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS;
+       } else {
+               ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+               if (ret >= 0)
+                       hw_cfg->bst_type = val;
+       }
 
        ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
        if (ret >= 0)
@@ -1280,6 +1278,10 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
        /* Set interrupt masks for critical errors */
        regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
                     CS35L41_INT1_MASK_DEFAULT);
+       if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+           cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+                                  0 << CS35L41_INT3_PLL_LOCK_SHIFT);
 
        ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
                                        IRQF_ONESHOT | IRQF_SHARED | irq_pol,
@@ -1303,6 +1305,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
        if (ret < 0)
                goto err;
 
+       init_completion(&cs35l41->pll_lock);
+
        pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
        pm_runtime_use_autosuspend(cs35l41->dev);
        pm_runtime_mark_last_busy(cs35l41->dev);
@@ -1345,6 +1349,10 @@ void cs35l41_remove(struct cs35l41_private *cs35l41)
        pm_runtime_disable(cs35l41->dev);
 
        regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+       if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+           cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+                                  1 << CS35L41_INT3_PLL_LOCK_SHIFT);
        kfree(cs35l41->dsp.system_name);
        wm_adsp2_remove(&cs35l41->dsp);
        cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
index c85cbc1dd333b7cc926443b23524eddeac7bd3f0..34d967d4372b28e850e5ad71de4dc3ec13984766 100644 (file)
@@ -33,6 +33,7 @@ struct cs35l41_private {
        int irq;
        /* GPIO for /RST */
        struct gpio_desc *reset_gpio;
+       struct completion pll_lock;
 };
 
 int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
index 1117df4b2f1187a2aa33719888146cff217cd2b1..562f73df7afa74053b29e64e3911b9acf956c5ad 100644 (file)
@@ -32,6 +32,9 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
        }
 
        cs35l45->dev = dev;
+       cs35l45->irq = client->irq;
+       cs35l45->bus_type = CONTROL_BUS_I2C;
+       cs35l45->i2c_addr = client->addr;
 
        return cs35l45_probe(cs35l45);
 }
index ffaca07fb26798627852db21176015a719477356..a00b23b4180c5af7c82383e3d76f499d6e5a5f4f 100644 (file)
@@ -23,6 +23,9 @@ static int cs35l45_spi_probe(struct spi_device *spi)
        if (cs35l45 == NULL)
                return -ENOMEM;
 
+       spi->max_speed_hz = CS35L45_SPI_MAX_FREQ;
+       spi_setup(spi);
+
        spi_set_drvdata(spi, cs35l45);
        cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
        if (IS_ERR(cs35l45->regmap)) {
@@ -32,6 +35,8 @@ static int cs35l45_spi_probe(struct spi_device *spi)
        }
 
        cs35l45->dev = dev;
+       cs35l45->irq = spi->irq;
+       cs35l45->bus_type = CONTROL_BUS_SPI;
 
        return cs35l45_probe(cs35l45);
 }
index 4b1320a2e6e97fda9bbab2f72726b1f838543853..46610e64e8188506991c152bb078e32bc3c8b709 100644 (file)
@@ -43,6 +43,12 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45);
 static const struct reg_default cs35l45_defaults[] = {
        { CS35L45_BLOCK_ENABLES,                0x00003323 },
        { CS35L45_BLOCK_ENABLES2,               0x00000010 },
+       { CS35L45_SYNC_GPIO1,                   0x00000007 },
+       { CS35L45_INTB_GPIO2_MCLK_REF,          0x00000005 },
+       { CS35L45_GPIO3,                        0x00000005 },
+       { CS35L45_PWRMGT_CTL,                   0x00000000 },
+       { CS35L45_WAKESRC_CTL,                  0x00000008 },
+       { CS35L45_WKI2C_CTL,                    0x00000030 },
        { CS35L45_REFCLK_INPUT,                 0x00000510 },
        { CS35L45_GLOBAL_SAMPLE_RATE,           0x00000003 },
        { CS35L45_ASP_ENABLES1,                 0x00000000 },
@@ -60,7 +66,53 @@ static const struct reg_default cs35l45_defaults[] = {
        { CS35L45_ASPTX3_INPUT,                 0x00000020 },
        { CS35L45_ASPTX4_INPUT,                 0x00000028 },
        { CS35L45_ASPTX5_INPUT,                 0x00000048 },
+       { CS35L45_DSP1_RX1_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX2_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX3_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX4_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX5_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX6_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX7_RATE,                0x00000001 },
+       { CS35L45_DSP1_RX8_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX1_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX2_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX3_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX4_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX5_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX6_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX7_RATE,                0x00000001 },
+       { CS35L45_DSP1_TX8_RATE,                0x00000001 },
+       { CS35L45_DSP1RX1_INPUT,                0x00000008 },
+       { CS35L45_DSP1RX2_INPUT,                0x00000009 },
+       { CS35L45_DSP1RX3_INPUT,                0x00000018 },
+       { CS35L45_DSP1RX4_INPUT,                0x00000019 },
+       { CS35L45_DSP1RX5_INPUT,                0x00000020 },
+       { CS35L45_DSP1RX6_INPUT,                0x00000028 },
+       { CS35L45_DSP1RX7_INPUT,                0x0000003A },
+       { CS35L45_DSP1RX8_INPUT,                0x00000028 },
        { CS35L45_AMP_PCM_CONTROL,              0x00100000 },
+       { CS35L45_IRQ1_CFG,                     0x00000000 },
+       { CS35L45_IRQ1_MASK_1,                  0xBFEFFFBF },
+       { CS35L45_IRQ1_MASK_2,                  0xFFFFFFFF },
+       { CS35L45_IRQ1_MASK_3,                  0xFFFF87FF },
+       { CS35L45_IRQ1_MASK_4,                  0xF8FFFFFF },
+       { CS35L45_IRQ1_MASK_5,                  0x0EF80000 },
+       { CS35L45_IRQ1_MASK_6,                  0x00000000 },
+       { CS35L45_IRQ1_MASK_7,                  0xFFFFFF78 },
+       { CS35L45_IRQ1_MASK_8,                  0x00003FFF },
+       { CS35L45_IRQ1_MASK_9,                  0x00000000 },
+       { CS35L45_IRQ1_MASK_10,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_11,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_12,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_13,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_14,                 0x00000001 },
+       { CS35L45_IRQ1_MASK_15,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_16,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_17,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_18,                 0x3FE5D0FF },
+       { CS35L45_GPIO1_CTRL1,                  0x81000001 },
+       { CS35L45_GPIO2_CTRL1,                  0x81000001 },
+       { CS35L45_GPIO3_CTRL1,                  0x81000001 },
 };
 
 static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
@@ -72,6 +124,13 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
        case CS35L45_BLOCK_ENABLES:
        case CS35L45_BLOCK_ENABLES2:
        case CS35L45_ERROR_RELEASE:
+       case CS35L45_SYNC_GPIO1:
+       case CS35L45_INTB_GPIO2_MCLK_REF:
+       case CS35L45_GPIO3:
+       case CS35L45_PWRMGT_CTL:
+       case CS35L45_WAKESRC_CTL:
+       case CS35L45_WKI2C_CTL:
+       case CS35L45_PWRMGT_STS:
        case CS35L45_REFCLK_INPUT:
        case CS35L45_GLOBAL_SAMPLE_RATE:
        case CS35L45_ASP_ENABLES1:
@@ -89,9 +148,59 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
        case CS35L45_ASPTX3_INPUT:
        case CS35L45_ASPTX4_INPUT:
        case CS35L45_ASPTX5_INPUT:
+       case CS35L45_DSP1RX1_INPUT:
+       case CS35L45_DSP1RX2_INPUT:
+       case CS35L45_DSP1RX3_INPUT:
+       case CS35L45_DSP1RX4_INPUT:
+       case CS35L45_DSP1RX5_INPUT:
+       case CS35L45_DSP1RX6_INPUT:
+       case CS35L45_DSP1RX7_INPUT:
+       case CS35L45_DSP1RX8_INPUT:
        case CS35L45_AMP_PCM_CONTROL:
        case CS35L45_AMP_PCM_HPF_TST:
-       case CS35L45_IRQ1_EINT_4:
+       case CS35L45_IRQ1_CFG:
+       case CS35L45_IRQ1_STATUS:
+       case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+       case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+       case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
+       case CS35L45_GPIO_STATUS1:
+       case CS35L45_GPIO1_CTRL1:
+       case CS35L45_GPIO2_CTRL1:
+       case CS35L45_GPIO3_CTRL1:
+       case CS35L45_DSP_MBOX_1:
+       case CS35L45_DSP_MBOX_2:
+       case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+       case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+       case CS35L45_DSP1_SYS_ID:
+       case CS35L45_DSP1_CLOCK_FREQ:
+       case CS35L45_DSP1_RX1_RATE:
+       case CS35L45_DSP1_RX2_RATE:
+       case CS35L45_DSP1_RX3_RATE:
+       case CS35L45_DSP1_RX4_RATE:
+       case CS35L45_DSP1_RX5_RATE:
+       case CS35L45_DSP1_RX6_RATE:
+       case CS35L45_DSP1_RX7_RATE:
+       case CS35L45_DSP1_RX8_RATE:
+       case CS35L45_DSP1_TX1_RATE:
+       case CS35L45_DSP1_TX2_RATE:
+       case CS35L45_DSP1_TX3_RATE:
+       case CS35L45_DSP1_TX4_RATE:
+       case CS35L45_DSP1_TX5_RATE:
+       case CS35L45_DSP1_TX6_RATE:
+       case CS35L45_DSP1_TX7_RATE:
+       case CS35L45_DSP1_TX8_RATE:
+       case CS35L45_DSP1_SCRATCH1:
+       case CS35L45_DSP1_SCRATCH2:
+       case CS35L45_DSP1_SCRATCH3:
+       case CS35L45_DSP1_SCRATCH4:
+       case CS35L45_DSP1_CCM_CORE_CONTROL:
+       case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+       case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+       case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+       case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+       case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+       case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+       case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
                return true;
        default:
                return false;
@@ -106,7 +215,29 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
        case CS35L45_GLOBAL_ENABLES:
        case CS35L45_ERROR_RELEASE:
        case CS35L45_AMP_PCM_HPF_TST:   /* not cachable */
-       case CS35L45_IRQ1_EINT_4:
+       case CS35L45_PWRMGT_STS:
+       case CS35L45_IRQ1_STATUS:
+       case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+       case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+       case CS35L45_GPIO_STATUS1:
+       case CS35L45_DSP_MBOX_1:
+       case CS35L45_DSP_MBOX_2:
+       case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+       case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+       case CS35L45_DSP1_SYS_ID:
+       case CS35L45_DSP1_CLOCK_FREQ:
+       case CS35L45_DSP1_SCRATCH1:
+       case CS35L45_DSP1_SCRATCH2:
+       case CS35L45_DSP1_SCRATCH3:
+       case CS35L45_DSP1_SCRATCH4:
+       case CS35L45_DSP1_CCM_CORE_CONTROL:
+       case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+       case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+       case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+       case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+       case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+       case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+       case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
                return true;
        default:
                return false;
index 855d9f13e6ff1711da627e6bc8bb0dfad37eae4b..c31597f6bfae199e9ea7f2d6d3579c89f16ba518 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/property.h>
+#include <linux/firmware.h>
 #include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 #include "cs35l45.h"
 
+static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
+                                       enum cs35l45_cspl_mboxstate sts)
+{
+       switch (cmd) {
+       case CSPL_MBOX_CMD_NONE:
+       case CSPL_MBOX_CMD_UNKNOWN_CMD:
+               return true;
+       case CSPL_MBOX_CMD_PAUSE:
+       case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+               return (sts == CSPL_MBOX_STS_PAUSED);
+       case CSPL_MBOX_CMD_RESUME:
+               return (sts == CSPL_MBOX_STS_RUNNING);
+       case CSPL_MBOX_CMD_REINIT:
+               return (sts == CSPL_MBOX_STS_RUNNING);
+       case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+               return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+       case CSPL_MBOX_CMD_HIBERNATE:
+               return (sts == CSPL_MBOX_STS_HIBERNATE);
+       default:
+               return false;
+       }
+}
+
+static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45,
+                                     struct regmap *regmap,
+                                     const enum cs35l45_cspl_mboxcmd cmd)
+{
+       unsigned int sts = 0, i;
+       int ret;
+
+       if (!cs35l45->dsp.cs_dsp.running) {
+               dev_err(cs35l45->dev, "DSP not running\n");
+               return -EPERM;
+       }
+
+       // Set mailbox cmd
+       ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd);
+       if (ret < 0) {
+               if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+                       dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret);
+               return ret;
+       }
+
+       // Read mailbox status and verify it is appropriate for the given cmd
+       for (i = 0; i < 5; i++) {
+               usleep_range(1000, 1100);
+
+               ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts);
+               if (ret < 0) {
+                       dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret);
+                       continue;
+               }
+
+               if (!cs35l45_check_cspl_mbox_sts(cmd, sts))
+                       dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+               else
+                       return 0;
+       }
+
+       if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+               dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+       return -ENOMSG;
+}
+
 static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
                                struct snd_kcontrol *kcontrol, int event)
 {
@@ -46,10 +112,68 @@ static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int cs35l45_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+       int ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (cs35l45->dsp.cs_dsp.booted)
+                       return 0;
+
+               return wm_adsp_early_event(w, kcontrol, event);
+       case SND_SOC_DAPM_POST_PMU:
+               if (cs35l45->dsp.cs_dsp.running)
+                       return 0;
+
+               regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL,
+                                  CS35L45_MEM_RDY_MASK);
+
+               return wm_adsp_event(w, kcontrol, event);
+       case SND_SOC_DAPM_PRE_PMD:
+               if (cs35l45->dsp.preloaded)
+                       return 0;
+
+               if (cs35l45->dsp.cs_dsp.running) {
+                       ret = wm_adsp_event(w, kcontrol, event);
+                       if (ret)
+                               return ret;
+               }
+
+               return wm_adsp_early_event(w, kcontrol, event);
+       default:
+               return 0;
+       }
+}
+
+static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+                                                CSPL_MBOX_CMD_RESUME);
+       case SND_SOC_DAPM_PRE_PMD:
+               return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+                                                CSPL_MBOX_CMD_PAUSE);
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const char * const cs35l45_asp_tx_txt[] = {
        "Zero", "ASP_RX1", "ASP_RX2",
        "VMON", "IMON", "ERR_VOL",
        "VDD_BATTMON", "VDD_BSTMON",
+       "DSP_TX1", "DSP_TX2",
        "Interpolator", "IL_TARGET",
 };
 
@@ -57,6 +181,7 @@ static const unsigned int cs35l45_asp_tx_val[] = {
        CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
        CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
        CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+       CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2,
        CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
 };
 
@@ -78,12 +203,54 @@ static const struct soc_enum cs35l45_asp_tx_enums[] = {
                              cs35l45_asp_tx_val),
 };
 
+static const char * const cs35l45_dsp_rx_txt[] = {
+       "Zero", "ASP_RX1", "ASP_RX2",
+       "VMON", "IMON", "ERR_VOL",
+       "CLASSH_TGT", "VDD_BATTMON",
+       "VDD_BSTMON", "TEMPMON",
+};
+
+static const unsigned int cs35l45_dsp_rx_val[] = {
+       CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+       CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+       CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON,
+       CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON,
+};
+
+static const struct soc_enum cs35l45_dsp_rx_enums[] = {
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+                             cs35l45_dsp_rx_val),
+};
+
 static const char * const cs35l45_dac_txt[] = {
-       "Zero", "ASP_RX1", "ASP_RX2"
+       "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2"
 };
 
 static const unsigned int cs35l45_dac_val[] = {
-       CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2
+       CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+       CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2
 };
 
 static const struct soc_enum cs35l45_dacpcm_enums[] = {
@@ -100,11 +267,29 @@ static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
        SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
 };
 
+static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = {
+       SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]),
+       SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]),
+       SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]),
+       SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]),
+       SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]),
+       SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]),
+       SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]),
+       SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]),
+};
+
 static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
        SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
 };
 
 static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+       SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+                               cs35l45_dsp_preload_ev,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+                               cs35l45_dsp_audio_ev,
+                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
        SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
                            cs35l45_global_en_ev,
                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -139,6 +324,15 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
        SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
 
+       SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]),
+       SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]),
+       SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]),
+       SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]),
+       SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]),
+       SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]),
+       SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]),
+       SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]),
+
        SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
 
        SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -149,6 +343,8 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
 #define CS35L45_ASP_MUX_ROUTE(name) \
        { name" Source", "ASP_RX1",      "ASP_RX1" }, \
        { name" Source", "ASP_RX2",      "ASP_RX2" }, \
+       { name" Source", "DSP_TX1",      "DSP1" }, \
+       { name" Source", "DSP_TX2",      "DSP1" }, \
        { name" Source", "VMON",         "VMON" }, \
        { name" Source", "IMON",         "IMON" }, \
        { name" Source", "ERR_VOL",      "ERR_VOL" }, \
@@ -157,10 +353,16 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
        { name" Source", "Interpolator", "AMP_INTP" }, \
        { name" Source", "IL_TARGET",    "IL_TARGET" }
 
-#define CS35L45_DAC_MUX_ROUTE(name) \
+#define CS35L45_DSP_MUX_ROUTE(name) \
        { name" Source", "ASP_RX1",     "ASP_RX1" }, \
        { name" Source", "ASP_RX2",     "ASP_RX2" }
 
+#define CS35L45_DAC_MUX_ROUTE(name) \
+       { name" Source", "ASP_RX1",     "ASP_RX1" }, \
+       { name" Source", "ASP_RX2",     "ASP_RX2" }, \
+       { name" Source", "DSP_TX1",     "DSP1" }, \
+       { name" Source", "DSP_TX2",     "DSP1" }
+
 static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
        /* Feedback */
        { "VMON", NULL, "VMON_SRC" },
@@ -204,6 +406,27 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
        { "AMP", NULL, "DACPCM1 Source"},
        { "AMP", NULL, "GLOBAL_EN"},
 
+       CS35L45_DSP_MUX_ROUTE("DSP_RX1"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX2"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX3"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX4"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX5"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX6"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX7"),
+       CS35L45_DSP_MUX_ROUTE("DSP_RX8"),
+
+       {"DSP1", NULL, "DSP_RX1 Source"},
+       {"DSP1", NULL, "DSP_RX2 Source"},
+       {"DSP1", NULL, "DSP_RX3 Source"},
+       {"DSP1", NULL, "DSP_RX4 Source"},
+       {"DSP1", NULL, "DSP_RX5 Source"},
+       {"DSP1", NULL, "DSP_RX6 Source"},
+       {"DSP1", NULL, "DSP_RX7 Source"},
+       {"DSP1", NULL, "DSP_RX8 Source"},
+
+       {"DSP1 Preload", NULL, "DSP1 Preloader"},
+       {"DSP1", NULL, "DSP1 Preloader"},
+
        CS35L45_DAC_MUX_ROUTE("DACPCM1"),
 
        { "SPK", NULL, "AMP"},
@@ -219,6 +442,8 @@ static const struct snd_kcontrol_new cs35l45_controls[] = {
                         -409, 48,
                         (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
                         0, cs35l45_dig_pcm_vol_tlv),
+       WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+       WM_ADSP_FW_CONTROL("DSP1", 0),
 };
 
 static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
@@ -489,7 +714,24 @@ static struct snd_soc_dai_driver cs35l45_dai[] = {
        },
 };
 
+static int cs35l45_component_probe(struct snd_soc_component *component)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+       return wm_adsp2_component_probe(&cs35l45->dsp, component);
+}
+
+static void cs35l45_component_remove(struct snd_soc_component *component)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+       wm_adsp2_component_remove(&cs35l45->dsp, component);
+}
+
 static const struct snd_soc_component_driver cs35l45_component = {
+       .probe = cs35l45_component_probe,
+       .remove = cs35l45_component_remove,
+
        .dapm_widgets = cs35l45_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
 
@@ -504,11 +746,81 @@ static const struct snd_soc_component_driver cs35l45_component = {
        .endianness = 1,
 };
 
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+       unsigned int wksrc;
+
+       if (cs35l45->bus_type == CONTROL_BUS_I2C)
+               wksrc = CS35L45_WKSRC_I2C;
+       else
+               wksrc = CS35L45_WKSRC_SPI;
+
+       regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+                          CS35L45_WKSRC_EN_MASK,
+                          wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+       regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+                          CS35L45_UPDT_WKCTL_MASK);
+
+       regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+                          CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+       regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+                          CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+       dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+       cs35l45_setup_hibernate(cs35l45);
+
+       // Don't wait for ACK since bus activity would wake the device
+       regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+       return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+       const int wake_retries = 20;
+       const int sleep_retries = 5;
+       int ret, i, j;
+
+       for (i = 0; i < sleep_retries; i++) {
+               dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+               for (j = 0; j < wake_retries; j++) {
+                       ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+                                         CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+                       if (!ret) {
+                               dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+                               return 0;
+                       }
+                       usleep_range(100, 200);
+               }
+
+               dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+               cs35l45_setup_hibernate(cs35l45);
+       }
+
+       dev_err(cs35l45->dev, "Timed out waking device\n");
+
+       return -ETIMEDOUT;
+}
+
 static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
 {
        struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
 
+       if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+               return 0;
+
+       cs35l45_enter_hibernate(cs35l45);
+
        regcache_cache_only(cs35l45->regmap, true);
+       regcache_mark_dirty(cs35l45->regmap);
 
        dev_dbg(cs35l45->dev, "Runtime suspended\n");
 
@@ -520,9 +832,17 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
        struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
        int ret;
 
+       if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+               return 0;
+
        dev_dbg(cs35l45->dev, "Runtime resume\n");
 
        regcache_cache_only(cs35l45->regmap, false);
+
+       ret = cs35l45_exit_hibernate(cs35l45);
+       if (ret)
+               return ret;
+
        ret = regcache_sync(cs35l45->regmap);
        if (ret != 0)
                dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
@@ -536,7 +856,66 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
 
 static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
 {
+       struct device_node *node = cs35l45->dev->of_node;
+       unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+                                   CS35L45_GPIO3_CTRL1};
+       unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+                                  CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+       struct device_node *child;
        unsigned int val;
+       char of_name[32];
+       int ret, i;
+
+       if (!node)
+               return 0;
+
+       for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+               sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+               child = of_get_child_by_name(node, of_name);
+               if (!child)
+                       continue;
+
+               ret = of_property_read_u32(child, "gpio-dir", &val);
+               if (!ret)
+                       regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+                                          CS35L45_GPIO_DIR_MASK,
+                                          val << CS35L45_GPIO_DIR_SHIFT);
+
+               ret = of_property_read_u32(child, "gpio-lvl", &val);
+               if (!ret)
+                       regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+                                          CS35L45_GPIO_LVL_MASK,
+                                          val << CS35L45_GPIO_LVL_SHIFT);
+
+               ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+               if (!ret)
+                       regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+                                          CS35L45_GPIO_OP_CFG_MASK,
+                                          val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+               ret = of_property_read_u32(child, "gpio-pol", &val);
+               if (!ret)
+                       regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+                                          CS35L45_GPIO_POL_MASK,
+                                          val << CS35L45_GPIO_POL_SHIFT);
+
+               ret = of_property_read_u32(child, "gpio-ctrl", &val);
+               if (!ret)
+                       regmap_update_bits(cs35l45->regmap, pad_regs[i],
+                                          CS35L45_GPIO_CTRL_MASK,
+                                          val << CS35L45_GPIO_CTRL_SHIFT);
+
+               ret = of_property_read_u32(child, "gpio-invert", &val);
+               if (!ret) {
+                       regmap_update_bits(cs35l45->regmap, pad_regs[i],
+                                          CS35L45_GPIO_INVERT_MASK,
+                                          val << CS35L45_GPIO_INVERT_SHIFT);
+                       if (i == 1)
+                               cs35l45->irq_invert = val;
+               }
+
+               of_node_put(child);
+       }
 
        if (device_property_read_u32(cs35l45->dev,
                                     "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
@@ -548,6 +927,134 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
        return 0;
 }
 
+static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45,
+                                             const unsigned int cmd,
+                                             unsigned int data)
+{
+       static char *speak_status = "Unknown";
+
+       switch (cmd) {
+       case EVENT_SPEAKER_STATUS:
+               switch (data) {
+               case 1:
+                       speak_status = "All Clear";
+                       break;
+               case 2:
+                       speak_status = "Open Circuit";
+                       break;
+               case 4:
+                       speak_status = "Short Circuit";
+                       break;
+               }
+
+               dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n",
+                        speak_status);
+               break;
+       case EVENT_BOOT_DONE:
+               dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n");
+               break;
+       default:
+               dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+       unsigned int mbox_val;
+       int ret = 0;
+
+       ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val);
+       if (!ret && mbox_val)
+               ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK,
+                               (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT);
+
+       /* Handle DSP trace log IRQ */
+       ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val);
+       if (!ret && mbox_val != 0) {
+               dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n");
+       }
+
+       return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+
+       dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+
+       dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+       CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+       CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+       CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+       CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+       int i;
+
+       i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+       dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+       return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+       CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+       CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+       CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+       .name = "cs35l45 IRQ1 Controller",
+       .main_status = CS35L45_IRQ1_STATUS,
+       .status_base = CS35L45_IRQ1_EINT_1,
+       .mask_base = CS35L45_IRQ1_MASK_1,
+       .ack_base = CS35L45_IRQ1_EINT_1,
+       .num_regs = 18,
+       .irqs = cs35l45_reg_irqs,
+       .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+       .runtime_pm = true,
+};
+
 static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 {
        struct device *dev = cs35l45->dev;
@@ -593,18 +1100,68 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
        if (ret < 0)
                return ret;
 
-       pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
-       pm_runtime_use_autosuspend(cs35l45->dev);
-       pm_runtime_set_active(cs35l45->dev);
-       pm_runtime_enable(cs35l45->dev);
-
        return 0;
 }
 
+static const struct reg_sequence cs35l45_fs_errata_patch[] = {
+       {0x02B80080,                    0x00000001},
+       {0x02B80088,                    0x00000001},
+       {0x02B80090,                    0x00000001},
+       {0x02B80098,                    0x00000001},
+       {0x02B800A0,                    0x00000001},
+       {0x02B800A8,                    0x00000001},
+       {0x02B800B0,                    0x00000001},
+       {0x02B800B8,                    0x00000001},
+       {0x02B80280,                    0x00000001},
+       {0x02B80288,                    0x00000001},
+       {0x02B80290,                    0x00000001},
+       {0x02B80298,                    0x00000001},
+       {0x02B802A0,                    0x00000001},
+       {0x02B802A8,                    0x00000001},
+       {0x02B802B0,                    0x00000001},
+       {0x02B802B8,                    0x00000001},
+};
+
+static const struct cs_dsp_region cs35l45_dsp1_regions[] = {
+       { .type = WMFW_HALO_PM_PACKED,  .base = CS35L45_DSP1_PMEM_0 },
+       { .type = WMFW_HALO_XM_PACKED,  .base = CS35L45_DSP1_XMEM_PACK_0 },
+       { .type = WMFW_HALO_YM_PACKED,  .base = CS35L45_DSP1_YMEM_PACK_0 },
+       {. type = WMFW_ADSP2_XM,        .base = CS35L45_DSP1_XMEM_UNPACK24_0},
+       {. type = WMFW_ADSP2_YM,        .base = CS35L45_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l45_dsp_init(struct cs35l45_private *cs35l45)
+{
+       struct wm_adsp *dsp = &cs35l45->dsp;
+       int ret;
+
+       dsp->part = "cs35l45";
+       dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+       dsp->toggle_preload = true;
+       dsp->cs_dsp.num = 1;
+       dsp->cs_dsp.type = WMFW_HALO;
+       dsp->cs_dsp.rev = 0;
+       dsp->cs_dsp.dev = cs35l45->dev;
+       dsp->cs_dsp.regmap = cs35l45->regmap;
+       dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ;
+       dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID;
+       dsp->cs_dsp.mem = cs35l45_dsp1_regions;
+       dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions);
+       dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+       ret = wm_halo_init(dsp);
+
+       regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch,
+                                                  ARRAY_SIZE(cs35l45_fs_errata_patch));
+
+       return ret;
+}
+
 int cs35l45_probe(struct cs35l45_private *cs35l45)
 {
        struct device *dev = cs35l45->dev;
-       int ret;
+       unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+       int ret, i, irq;
 
        cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
        if (IS_ERR(cs35l45->vdd_batt))
@@ -649,14 +1206,63 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
        if (ret < 0)
                goto err_reset;
 
+       ret = cs35l45_dsp_init(cs35l45);
+       if (ret < 0)
+               goto err_reset;
+
+       pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+       pm_runtime_use_autosuspend(cs35l45->dev);
+       pm_runtime_mark_last_busy(cs35l45->dev);
+       pm_runtime_set_active(cs35l45->dev);
+       pm_runtime_get_noresume(cs35l45->dev);
+       pm_runtime_enable(cs35l45->dev);
+
+       if (cs35l45->irq) {
+               if (cs35l45->irq_invert)
+                       irq_pol |= IRQF_TRIGGER_HIGH;
+               else
+                       irq_pol |= IRQF_TRIGGER_LOW;
+
+               ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+                                              &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+               if (ret) {
+                       dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+                       goto err_dsp;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+                       irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+                       if (irq < 0) {
+                               dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+                               ret = irq;
+                               goto err_dsp;
+                       }
+
+                       ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+                                                       irq_pol, cs35l45_irqs[i].name, cs35l45);
+                       if (ret) {
+                               dev_err(dev, "Failed to request IRQ %s: %d\n",
+                                       cs35l45_irqs[i].name, ret);
+                               goto err_dsp;
+                       }
+               }
+       }
+
        ret = devm_snd_soc_register_component(dev, &cs35l45_component,
                                              cs35l45_dai,
                                              ARRAY_SIZE(cs35l45_dai));
        if (ret < 0)
-               goto err_reset;
+               goto err_dsp;
+
+       pm_runtime_put_autosuspend(cs35l45->dev);
 
        return 0;
 
+err_dsp:
+       pm_runtime_disable(cs35l45->dev);
+       pm_runtime_put_noidle(cs35l45->dev);
+       wm_adsp2_remove(&cs35l45->dsp);
+
 err_reset:
        gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
 err:
@@ -669,9 +1275,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
 
 void cs35l45_remove(struct cs35l45_private *cs35l45)
 {
+       pm_runtime_get_sync(cs35l45->dev);
        pm_runtime_disable(cs35l45->dev);
+       wm_adsp2_remove(&cs35l45->dsp);
 
        gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+
+       pm_runtime_put_noidle(cs35l45->dev);
        regulator_disable(cs35l45->vdd_a);
        /* VDD_BATT must be the last to power-off */
        regulator_disable(cs35l45->vdd_batt);
index 53fe9d2b7b15f44d80c2063c97daacea6ccc6113..0da28439f628b0d593e43345cc14daf16898029a 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
+#include "wm_adsp.h"
 
 #define CS35L45_DEVID                          0x00000000
 #define CS35L45_REVID                          0x00000004
 #define CS35L45_BLOCK_ENABLES                  0x00002018
 #define CS35L45_BLOCK_ENABLES2                 0x0000201C
 #define CS35L45_ERROR_RELEASE                  0x00002034
+#define CS35L45_SYNC_GPIO1                     0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF            0x00002434
+#define CS35L45_GPIO3                          0x00002438
+#define CS35L45_PWRMGT_CTL                     0x00002900
+#define CS35L45_WAKESRC_CTL                    0x00002904
+#define CS35L45_WKI2C_CTL                      0x00002908
+#define CS35L45_PWRMGT_STS                     0x0000290C
 #define CS35L45_REFCLK_INPUT                   0x00002C04
 #define CS35L45_GLOBAL_SAMPLE_RATE             0x00002C0C
 #define CS35L45_BOOST_CCM_CFG                  0x00003808
 #define CS35L45_ASPTX3_INPUT                   0x00004C28
 #define CS35L45_ASPTX4_INPUT                   0x00004C2C
 #define CS35L45_ASPTX5_INPUT                   0x00004C30
+#define CS35L45_DSP1RX1_INPUT                  0x00004C40
+#define CS35L45_DSP1RX2_INPUT                  0x00004C44
+#define CS35L45_DSP1RX3_INPUT                  0x00004C48
+#define CS35L45_DSP1RX4_INPUT                  0x00004C4C
+#define CS35L45_DSP1RX5_INPUT                  0x00004C50
+#define CS35L45_DSP1RX6_INPUT                  0x00004C54
+#define CS35L45_DSP1RX7_INPUT                  0x00004C58
+#define CS35L45_DSP1RX8_INPUT                  0x00004C5C
 #define CS35L45_LDPM_CONFIG                    0x00006404
 #define CS35L45_AMP_PCM_CONTROL                        0x00007000
 #define CS35L45_AMP_PCM_HPF_TST                        0x00007004
+#define CS35L45_IRQ1_CFG                       0x0000E000
+#define CS35L45_IRQ1_STATUS                    0x0000E004
+#define CS35L45_IRQ1_EINT_1                    0x0000E010
+#define CS35L45_IRQ1_EINT_2                    0x0000E014
+#define CS35L45_IRQ1_EINT_3                    0x0000E018
 #define CS35L45_IRQ1_EINT_4                    0x0000E01C
-#define CS35L45_LASTREG                                0x0000E01C
+#define CS35L45_IRQ1_EINT_5                    0x0000E020
+#define CS35L45_IRQ1_EINT_7                    0x0000E028
+#define CS35L45_IRQ1_EINT_8                    0x0000E02C
+#define CS35L45_IRQ1_EINT_18                   0x0000E054
+#define CS35L45_IRQ1_STS_1                     0x0000E090
+#define CS35L45_IRQ1_STS_2                     0x0000E094
+#define CS35L45_IRQ1_STS_3                     0x0000E098
+#define CS35L45_IRQ1_STS_4                     0x0000E09C
+#define CS35L45_IRQ1_STS_5                     0x0000E0A0
+#define CS35L45_IRQ1_STS_7                     0x0000E0A8
+#define CS35L45_IRQ1_STS_8                     0x0000E0AC
+#define CS35L45_IRQ1_STS_18                    0x0000E0D4
+#define CS35L45_IRQ1_MASK_1                    0x0000E110
+#define CS35L45_IRQ1_MASK_2                    0x0000E114
+#define CS35L45_IRQ1_MASK_3                    0x0000E118
+#define CS35L45_IRQ1_MASK_4                    0x0000E11C
+#define CS35L45_IRQ1_MASK_5                    0x0000E120
+#define CS35L45_IRQ1_MASK_6                    0x0000E124
+#define CS35L45_IRQ1_MASK_7                    0x0000E128
+#define CS35L45_IRQ1_MASK_8                    0x0000E12C
+#define CS35L45_IRQ1_MASK_9                    0x0000E130
+#define CS35L45_IRQ1_MASK_10                   0x0000E134
+#define CS35L45_IRQ1_MASK_11                   0x0000E138
+#define CS35L45_IRQ1_MASK_12                   0x0000E13C
+#define CS35L45_IRQ1_MASK_13                   0x0000E140
+#define CS35L45_IRQ1_MASK_14                   0x0000E144
+#define CS35L45_IRQ1_MASK_15                   0x0000E148
+#define CS35L45_IRQ1_MASK_16                   0x0000E14C
+#define CS35L45_IRQ1_MASK_17                   0x0000E150
+#define CS35L45_IRQ1_MASK_18                   0x0000E154
+#define CS35L45_GPIO_STATUS1                   0x0000F000
+#define CS35L45_GPIO1_CTRL1                    0x0000F008
+#define CS35L45_GPIO2_CTRL1                    0x0000F00C
+#define CS35L45_GPIO3_CTRL1                    0x0000F010
+#define CS35L45_DSP_MBOX_1                     0x00011000
+#define CS35L45_DSP_MBOX_2                     0x00011004
+#define CS35L45_DSP_VIRT1_MBOX_1               0x00011020
+#define CS35L45_DSP_VIRT1_MBOX_2               0x00011024
+#define CS35L45_DSP_VIRT1_MBOX_3               0x00011028
+#define CS35L45_DSP_VIRT1_MBOX_4               0x0001102C
+#define CS35L45_DSP_VIRT2_MBOX_1               0x00011040
+#define CS35L45_DSP_VIRT2_MBOX_2               0x00011044
+#define CS35L45_DSP_VIRT2_MBOX_3               0x00011048
+#define CS35L45_DSP_VIRT2_MBOX_4               0x0001104C
+#define CS35L45_DSP1_XMEM_PACK_0               0x02000000
+#define CS35L45_DSP1_XMEM_PACK_4607            0x020047FC
+#define CS35L45_DSP1_XMEM_UNPACK32_0           0x02400000
+#define CS35L45_DSP1_XMEM_UNPACK32_3071        0x02402FFC
+#define CS35L45_DSP1_SYS_ID                    0x025E0000
+#define CS35L45_DSP1_XMEM_UNPACK24_0           0x02800000
+#define CS35L45_DSP1_XMEM_UNPACK24_6143        0x02805FFC
+#define CS35L45_DSP1_CLOCK_FREQ                0x02B80000
+#define CS35L45_DSP1_RX1_RATE                  0x02B80080
+#define CS35L45_DSP1_RX2_RATE                  0x02B80088
+#define CS35L45_DSP1_RX3_RATE                  0x02B80090
+#define CS35L45_DSP1_RX4_RATE                  0x02B80098
+#define CS35L45_DSP1_RX5_RATE                  0x02B800A0
+#define CS35L45_DSP1_RX6_RATE                  0x02B800A8
+#define CS35L45_DSP1_RX7_RATE                  0x02B800B0
+#define CS35L45_DSP1_RX8_RATE                  0x02B800B8
+#define CS35L45_DSP1_TX1_RATE                  0x02B80280
+#define CS35L45_DSP1_TX2_RATE                  0x02B80288
+#define CS35L45_DSP1_TX3_RATE                  0x02B80290
+#define CS35L45_DSP1_TX4_RATE                  0x02B80298
+#define CS35L45_DSP1_TX5_RATE                  0x02B802A0
+#define CS35L45_DSP1_TX6_RATE                  0x02B802A8
+#define CS35L45_DSP1_TX7_RATE                  0x02B802B0
+#define CS35L45_DSP1_TX8_RATE                  0x02B802B8
+#define CS35L45_DSP1_SCRATCH1                  0x02B805C0
+#define CS35L45_DSP1_SCRATCH2                  0x02B805C8
+#define CS35L45_DSP1_SCRATCH3                  0x02B805D0
+#define CS35L45_DSP1_SCRATCH4                  0x02B805D8
+#define CS35L45_DSP1_CCM_CORE_CONTROL          0x02BC1000
+#define CS35L45_DSP1_YMEM_PACK_0               0x02C00000
+#define CS35L45_DSP1_YMEM_PACK_1532            0x02C017F0
+#define CS35L45_DSP1_YMEM_UNPACK32_0           0x03000000
+#define CS35L45_DSP1_YMEM_UNPACK32_1022        0x03000FF8
+#define CS35L45_DSP1_YMEM_UNPACK24_0           0x03400000
+#define CS35L45_DSP1_YMEM_UNPACK24_2043        0x03401FEC
+#define CS35L45_DSP1_PMEM_0                    0x03800000
+#define CS35L45_DSP1_PMEM_3834                 0x03803BE8
+#define CS35L45_LASTREG                        0x03C6EFE8
 
 /* SFT_RESET */
 #define CS35L45_SOFT_RESET_TRIGGER             0x5A000000
 /* BLOCK_ENABLES2 */
 #define CS35L45_ASP_EN_SHIFT                   27
 
+#define CS35L45_MEM_RDY_SHIFT                  1
+#define CS35L45_MEM_RDY_MASK                   BIT(1)
+
 /* ERROR_RELEASE */
 #define CS35L45_GLOBAL_ERR_RLS_MASK            BIT(11)
 
+/* CCM_CORE */
+#define CS35L45_CCM_CORE_RESET_SHIFT           9
+#define CS35L45_CCM_CORE_RESET_MASK            BIT(9)
+#define CS35L45_CCM_PM_REMAP_SHIFT             7
+#define CS35L45_CCM_PM_REMAP_MASK              BIT(7)
+#define CS35L45_CCM_CORE_EN_SHIFT              0
+#define CS35L45_CCM_CORE_EN_MASK               BIT(0)
+
 /* REFCLK_INPUT */
 #define CS35L45_PLL_FORCE_EN_SHIFT             16
 #define CS35L45_PLL_FORCE_EN_MASK              BIT(16)
 #define CS35L45_OTP_BOOT_DONE_STS_MASK         BIT(1)
 #define CS35L45_OTP_BUSY_MASK                  BIT(0)
 
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT                 31
+#define CS35L45_GPIO_DIR_MASK                  BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT                 15
+#define CS35L45_GPIO_LVL_MASK                  BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT              14
+#define CS35L45_GPIO_OP_CFG_MASK               BIT(14)
+#define CS35L45_GPIO_POL_SHIFT                 12
+#define CS35L45_GPIO_POL_MASK                  BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT                20
+#define CS35L45_GPIO_CTRL_MASK                 GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT              19
+#define CS35L45_GPIO_INVERT_MASK               BIT(19)
+
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT              7
+#define CS35L45_BST_UVP_ERR_MASK               BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT            8
+#define CS35L45_BST_SHORT_ERR_MASK             BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT                 17
+#define CS35L45_TEMP_ERR_MASK                  BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT     22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK      BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK          BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT            31
+#define CS35L45_AMP_SHORT_ERR_MASK             BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT           4
+#define CS35L45_DSP_WDT_EXPIRE_MASK            BIT(4)
+#define CS35L45_DSP_VIRT2_MBOX_SHIFT           21
+#define CS35L45_DSP_VIRT2_MBOX_MASK            BIT(21)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT            1
+#define CS35L45_PLL_LOCK_FLAG_MASK             BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT     4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK      BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT              25
+#define CS35L45_AMP_CAL_ERR_MASK               BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT             15
+#define CS35L45_GLOBAL_ERROR_MASK              BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT           16
+#define CS35L45_UVLO_VDDLV_ERR_MASK            BIT(16)
+
 /* Mixer sources */
 #define CS35L45_PCM_SRC_MASK                   0x7F
 #define CS35L45_PCM_SRC_ZERO                   0x00
 #define CS35L45_PCM_SRC_CLASSH_TGT             0x21
 #define CS35L45_PCM_SRC_VDD_BATTMON            0x28
 #define CS35L45_PCM_SRC_VDD_BSTMON             0x29
+#define CS35L45_PCM_SRC_DSP_TX1                        0x32
+#define CS35L45_PCM_SRC_DSP_TX2                        0x33
 #define CS35L45_PCM_SRC_TEMPMON                        0x3A
 #define CS35L45_PCM_SRC_INTERPOLATOR           0x40
 #define CS35L45_PCM_SRC_IL_TARGET              0x48
 #define CS35L45_POST_GLOBAL_EN_US              5000
 #define CS35L45_PRE_GLOBAL_DIS_US              3000
 
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1               BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2                        BIT(1)
+#define CS35L45_WKSRC_GPIO3                    BIT(2)
+#define CS35L45_WKSRC_SPI                      BIT(3)
+#define CS35L45_WKSRC_I2C                      BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT               15
+#define CS35L45_UPDT_WKCTL_MASK                        BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT                 8
+#define CS35L45_WKSRC_EN_MASK                  GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT                        0
+#define CS35L45_WKSRC_POL_MASK                 GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT               15
+#define CS35L45_UPDT_WKI2C_MASK                        BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT               0
+#define CS35L45_WKI2C_ADDR_MASK                        GENMASK(6, 0)
+
+#define CS35L45_SPI_MAX_FREQ                   4000000
+
+enum cs35l45_cspl_mboxstate {
+       CSPL_MBOX_STS_RUNNING = 0,
+       CSPL_MBOX_STS_PAUSED = 1,
+       CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
+       CSPL_MBOX_STS_HIBERNATE = 3,
+};
+
+enum cs35l45_cspl_mboxcmd {
+       CSPL_MBOX_CMD_NONE = 0,
+       CSPL_MBOX_CMD_PAUSE = 1,
+       CSPL_MBOX_CMD_RESUME = 2,
+       CSPL_MBOX_CMD_REINIT = 3,
+       CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+       CSPL_MBOX_CMD_HIBERNATE = 5,
+       CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+       CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
+       CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
+};
+
+enum control_bus_type {
+       CONTROL_BUS_I2C = 0,
+       CONTROL_BUS_SPI = 1,
+};
+
 #define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
                         SNDRV_PCM_FMTBIT_S24_3LE| \
                         SNDRV_PCM_FMTBIT_S24_LE)
                       SNDRV_PCM_RATE_88200 | \
                       SNDRV_PCM_RATE_96000)
 
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand)                \
+       {                                       \
+               .irq = CS35L45_ ## _irq ## _IRQ,\
+               .name = _name,                  \
+               .handler = _hand,               \
+       }
+
+struct cs35l45_irq {
+       int irq;
+       const char *name;
+       irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq)                                    \
+       [CS35L45_ ## _irq ## _IRQ] = {                                  \
+               .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \
+               .mask = CS35L45_ ## _irq ## _MASK                       \
+       }
+
+enum cs35l45_irq_list {
+       CS35L45_AMP_SHORT_ERR_IRQ,
+       CS35L45_UVLO_VDDBATT_ERR_IRQ,
+       CS35L45_BST_SHORT_ERR_IRQ,
+       CS35L45_BST_UVP_ERR_IRQ,
+       CS35L45_TEMP_ERR_IRQ,
+       CS35L45_AMP_CAL_ERR_IRQ,
+       CS35L45_UVLO_VDDLV_ERR_IRQ,
+       CS35L45_GLOBAL_ERROR_IRQ,
+       CS35L45_DSP_WDT_EXPIRE_IRQ,
+       CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+       CS35L45_PLL_LOCK_FLAG_IRQ,
+       CS35L45_DSP_VIRT2_MBOX_IRQ,
+       CS35L45_NUM_IRQ
+};
+
+#define CS35L45_MBOX3_CMD_MASK         0xFF
+#define CS35L45_MBOX3_CMD_SHIFT                0
+#define CS35L45_MBOX3_DATA_MASK                0xFFFFFF00
+#define CS35L45_MBOX3_DATA_SHIFT       8
+
+enum mbox3_events {
+       EVENT_SPEAKER_STATUS = 0x66,
+       EVENT_BOOT_DONE = 0x67,
+};
+
 struct cs35l45_private {
+       struct wm_adsp dsp; /* needs to be first member */
        struct device *dev;
        struct regmap *regmap;
        struct gpio_desc *reset_gpio;
@@ -204,6 +464,11 @@ struct cs35l45_private {
        bool sysclk_set;
        u8 slot_width;
        u8 slot_count;
+       int irq_invert;
+       int irq;
+       unsigned int i2c_addr;
+       enum control_bus_type bus_type;
+       struct regmap_irq_chip_data *irq_data;
 };
 
 extern const struct dev_pm_ops cs35l45_pm_ops;
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
new file mode 100644 (file)
index 0000000..295caad
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_i2c_probe(struct i2c_client *client)
+{
+       struct cs35l56_private *cs35l56;
+       struct device *dev = &client->dev;
+       const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
+       int ret;
+
+       cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+       if (!cs35l56)
+               return -ENOMEM;
+
+       cs35l56->dev = dev;
+       cs35l56->can_hibernate = true;
+
+       i2c_set_clientdata(client, cs35l56);
+       cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config);
+       if (IS_ERR(cs35l56->regmap)) {
+               ret = PTR_ERR(cs35l56->regmap);
+               return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n");
+       }
+
+       ret = cs35l56_common_probe(cs35l56);
+       if (ret != 0)
+               return ret;
+
+       ret = cs35l56_init(cs35l56);
+       if (ret == 0)
+               ret = cs35l56_irq_request(cs35l56, client->irq);
+       if (ret < 0)
+               cs35l56_remove(cs35l56);
+
+       return ret;
+}
+
+static void cs35l56_i2c_remove(struct i2c_client *client)
+{
+       struct cs35l56_private *cs35l56 = i2c_get_clientdata(client);
+
+       cs35l56_remove(cs35l56);
+}
+
+static const struct i2c_device_id cs35l56_id_i2c[] = {
+       { "cs35l56", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+static struct i2c_driver cs35l56_i2c_driver = {
+       .driver = {
+               .name           = "cs35l56",
+               .pm = &cs35l56_pm_ops_i2c_spi,
+       },
+       .id_table       = cs35l56_id_i2c,
+       .probe_new      = cs35l56_i2c_probe,
+       .remove         = cs35l56_i2c_remove,
+};
+
+module_i2c_driver(cs35l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 I2C driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
new file mode 100644 (file)
index 0000000..2cde786
--- /dev/null
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SoundWire binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "cs35l56.h"
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS35L56_SDW_ADDR_OFFSET                0x8000
+
+static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
+{
+       int ret;
+
+       ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf);
+       if (ret != 0) {
+               dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret);
+               return ret;
+       }
+
+       swab32s((u32 *)buf);
+
+       return 0;
+}
+
+static int cs35l56_sdw_read(void *context, const void *reg_buf,
+                           const size_t reg_size, void *val_buf,
+                           size_t val_size)
+{
+       struct sdw_slave *peripheral = context;
+       u8 *buf8 = val_buf;
+       unsigned int reg, bytes;
+       int ret;
+
+       reg = le32_to_cpu(*(const __le32 *)reg_buf);
+       reg += CS35L56_SDW_ADDR_OFFSET;
+
+       if (val_size == 4)
+               return cs35l56_sdw_read_one(peripheral, reg, val_buf);
+
+       while (val_size) {
+               bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+               if (bytes > val_size)
+                       bytes = val_size;
+
+               ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8);
+               if (ret != 0) {
+                       dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n",
+                               reg, reg + bytes - 1, ret);
+                       return ret;
+               }
+
+               swab32_array((u32 *)buf8, bytes / 4);
+               val_size -= bytes;
+               reg += bytes;
+               buf8 += bytes;
+       }
+
+       return 0;
+}
+
+static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes)
+{
+       u32 *dest32 = dest;
+       const u32 *src32 = src;
+
+       for (; nbytes > 0; nbytes -= 4)
+               *dest32++ = swab32(*src32++);
+}
+
+static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf)
+{
+       u32 val_le = swab32(*(u32 *)buf);
+       int ret;
+
+       ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le);
+       if (ret != 0) {
+               dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cs35l56_sdw_gather_write(void *context,
+                                   const void *reg_buf, size_t reg_size,
+                                   const void *val_buf, size_t val_size)
+{
+       struct sdw_slave *peripheral = context;
+       const u8 *src_be = val_buf;
+       u32 val_le_buf[64];     /* Define u32 so it is 32-bit aligned */
+       unsigned int reg, bytes;
+       int ret;
+
+       reg = le32_to_cpu(*(const __le32 *)reg_buf);
+       reg += CS35L56_SDW_ADDR_OFFSET;
+
+       if (val_size == 4)
+               return cs35l56_sdw_write_one(peripheral, reg, src_be);
+
+       while (val_size) {
+               bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+               if (bytes > val_size)
+                       bytes = val_size;
+               if (bytes > sizeof(val_le_buf))
+                       bytes = sizeof(val_le_buf);
+
+               cs35l56_swab_copy(val_le_buf, src_be, bytes);
+
+               ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf);
+               if (ret != 0) {
+                       dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n",
+                               reg, reg + bytes - 1, ret);
+                       return ret;
+               }
+
+               val_size -= bytes;
+               reg += bytes;
+               src_be += bytes;
+       }
+
+       return 0;
+}
+
+static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size)
+{
+       const u8 *src_buf = val_buf;
+
+       /* First word of val_buf contains the destination address */
+       return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4);
+}
+
+/*
+ * Registers are big-endian on I2C and SPI but little-endian on SoundWire.
+ * Exported firmware controls are big-endian on I2C/SPI but little-endian on
+ * SoundWire. Firmware files are always big-endian and are opaque blobs.
+ * Present a big-endian regmap and hide the endianness swap, so that the ALSA
+ * byte controls always have the same byte order, and firmware file blobs
+ * can be written verbatim.
+ */
+static const struct regmap_bus cs35l56_regmap_bus_sdw = {
+       .read = cs35l56_sdw_read,
+       .write = cs35l56_sdw_write,
+       .gather_write = cs35l56_sdw_gather_write,
+       .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+       .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void cs35l56_sdw_init(struct sdw_slave *peripheral)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+       int ret;
+
+       pm_runtime_get_noresume(cs35l56->dev);
+
+       regcache_cache_only(cs35l56->regmap, false);
+
+       ret = cs35l56_init(cs35l56);
+       if (ret < 0) {
+               regcache_cache_only(cs35l56->regmap, true);
+               goto out;
+       }
+
+       /*
+        * cs35l56_init can return with !init_done if it triggered
+        * a soft reset.
+        */
+       if (cs35l56->init_done) {
+               /* Enable SoundWire interrupts */
+               sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+                               CS35L56_SDW_INT_MASK_CODEC_IRQ);
+       }
+
+out:
+       pm_runtime_mark_last_busy(cs35l56->dev);
+       pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
+                                struct sdw_slave_intr_status *status)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+       /* SoundWire core holds our pm_runtime when calling this function. */
+
+       dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port);
+
+       if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0)
+               return 0;
+
+       /*
+        * Prevent bus manager suspending and possibly issuing a
+        * bus-reset before the queued work has run.
+        */
+       pm_runtime_get_noresume(cs35l56->dev);
+
+       /*
+        * Mask and clear until it has been handled. The read of GEN_INT_STAT_1
+        * is required as per the SoundWire spec for interrupt status bits
+        * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+        * None of the interrupts are time-critical so use the
+        * power-efficient queue.
+        */
+       sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+       sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+       sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+       queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work);
+
+       return 0;
+}
+
+static void cs35l56_sdw_irq_work(struct work_struct *work)
+{
+       struct cs35l56_private *cs35l56 = container_of(work,
+                                                      struct cs35l56_private,
+                                                      sdw_irq_work);
+
+       cs35l56_irq(-1, cs35l56);
+
+       /* unmask interrupts */
+       if (!cs35l56->sdw_irq_no_unmask)
+               sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+                               CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+       pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+       struct sdw_slave_prop *prop = &peripheral->prop;
+       struct sdw_dpn_prop *ports;
+
+       ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL);
+       if (!ports)
+               return -ENOMEM;
+
+       prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT);
+       prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT);
+       prop->paging_support = true;
+       prop->clk_stop_mode1 = false;
+       prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+       prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF;
+
+       /* DP1 - playback */
+       ports[0].num = CS35L56_SDW1_PLAYBACK_PORT;
+       ports[0].type = SDW_DPN_FULL;
+       ports[0].ch_prep_timeout = 10;
+       prop->sink_dpn_prop = &ports[0];
+
+       /* DP3 - capture */
+       ports[1].num = CS35L56_SDW1_CAPTURE_PORT;
+       ports[1].type = SDW_DPN_FULL;
+       ports[1].ch_prep_timeout = 10;
+       prop->src_dpn_prop = &ports[1];
+
+       return 0;
+}
+
+static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
+                                    enum sdw_slave_status status)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+       switch (status) {
+       case SDW_SLAVE_ATTACHED:
+               dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__);
+               if (cs35l56->sdw_attached)
+                       break;
+
+               if (!cs35l56->init_done || cs35l56->soft_resetting)
+                       cs35l56_sdw_init(peripheral);
+
+               cs35l56->sdw_attached = true;
+               break;
+       case SDW_SLAVE_UNATTACHED:
+               dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__);
+               cs35l56->sdw_attached = false;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
+                                  struct sdw_slave *peripheral)
+{
+       unsigned int curr_scale_reg, next_scale_reg;
+       int curr_scale, next_scale, ret;
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       if (peripheral->bus->params.curr_bank) {
+               curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+               next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+       } else {
+               curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+               next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+       }
+
+       /*
+        * Current clock scale value must be different to new value.
+        * Modify current to guarantee this. If next still has the dummy
+        * value we wrote when it was current, the core code has not set
+        * a new scale so restore its original good value
+        */
+       curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
+       if (curr_scale < 0) {
+               dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale);
+               return curr_scale;
+       }
+
+       next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
+       if (next_scale < 0) {
+               dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale);
+               return next_scale;
+       }
+
+       if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
+               next_scale = cs35l56->old_sdw_clock_scale;
+               ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
+               if (ret < 0) {
+                       dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       cs35l56->old_sdw_clock_scale = curr_scale;
+       ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
+       if (ret < 0) {
+               dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+               return ret;
+       }
+
+       dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale);
+
+       return 0;
+}
+
+static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
+                                 struct sdw_bus_params *params)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+       int sclk;
+
+       sclk = params->curr_dr_freq / 2;
+       dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row);
+
+       if (cs35l56->rev < 0xb0)
+               return cs35l56_a1_kick_divider(cs35l56, peripheral);
+
+       return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
+                                              enum sdw_clk_stop_mode mode,
+                                              enum sdw_clk_stop_type type)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+       dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type);
+
+       return 0;
+}
+
+static const struct sdw_slave_ops cs35l56_sdw_ops = {
+       .read_prop = cs35l56_sdw_read_prop,
+       .interrupt_callback = cs35l56_sdw_interrupt,
+       .update_status = cs35l56_sdw_update_status,
+       .bus_config = cs35l56_sdw_bus_config,
+#ifdef DEBUG
+       .clk_stop = cs35l56_sdw_clk_stop,
+#endif
+};
+
+static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
+{
+       struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
+
+       if (peripheral->unattach_request) {
+               /* Cannot access registers until bus is re-initialized. */
+               dev_dbg(cs35l56->dev, "Wait for initialization_complete\n");
+               if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+                                                msecs_to_jiffies(5000))) {
+                       dev_err(cs35l56->dev, "initialization_complete timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               peripheral->unattach_request = 0;
+
+               /*
+                * Don't call regcache_mark_dirty(), we can't be sure that the
+                * Manager really did issue a Bus Reset.
+                */
+       }
+
+       return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       return cs35l56_runtime_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "Runtime resume\n");
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       ret = cs35l56_sdw_handle_unattach(cs35l56);
+       if (ret < 0)
+               return ret;
+
+       ret = cs35l56_runtime_resume_common(cs35l56);
+       if (ret)
+               return ret;
+
+       /* Re-enable SoundWire interrupts */
+       sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+                       CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+       return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       /*
+        * Disable SoundWire interrupts.
+        * Flush - don't cancel because that could leave an unbalanced pm_runtime_get.
+        */
+       cs35l56->sdw_irq_no_unmask = true;
+       flush_work(&cs35l56->sdw_irq_work);
+
+       /* Mask interrupts and flush in case sdw_irq_work was queued again */
+       sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+       sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+       sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+       flush_work(&cs35l56->sdw_irq_work);
+
+       return cs35l56_system_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       cs35l56->sdw_irq_no_unmask = false;
+       /* runtime_resume re-enables the interrupt */
+
+       return cs35l56_system_resume(dev);
+}
+
+static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+       struct device *dev = &peripheral->dev;
+       struct cs35l56_private *cs35l56;
+       int ret;
+
+       cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+       if (!cs35l56)
+               return -ENOMEM;
+
+       cs35l56->dev = dev;
+       cs35l56->sdw_peripheral = peripheral;
+       INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+       dev_set_drvdata(dev, cs35l56);
+
+       cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+                                          peripheral, &cs35l56_regmap_sdw);
+       if (IS_ERR(cs35l56->regmap)) {
+               ret = PTR_ERR(cs35l56->regmap);
+               return dev_err_probe(dev, ret, "Failed to allocate register map\n");
+       }
+
+       /* Start in cache-only until device is enumerated */
+       regcache_cache_only(cs35l56->regmap, true);
+
+       ret = cs35l56_common_probe(cs35l56);
+       if (ret != 0)
+               return ret;
+
+       return 0;
+}
+
+static int cs35l56_sdw_remove(struct sdw_slave *peripheral)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+       /* Disable SoundWire interrupts */
+       cs35l56->sdw_irq_no_unmask = true;
+       cancel_work_sync(&cs35l56->sdw_irq_work);
+       sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+       sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+       sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+
+       cs35l56_remove(cs35l56);
+
+       return 0;
+}
+
+static const struct dev_pm_ops cs35l56_sdw_pm = {
+       SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL)
+       SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume)
+       LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+       /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */
+};
+
+static const struct sdw_device_id cs35l56_sdw_id[] = {
+       SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
+       {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+       .driver = {
+               .name = "cs35l56",
+               .pm = &cs35l56_sdw_pm,
+       },
+       .probe = cs35l56_sdw_probe,
+       .remove = cs35l56_sdw_remove,
+       .ops = &cs35l56_sdw_ops,
+       .id_table = cs35l56_sdw_id,
+};
+
+module_sdw_driver(cs35l56_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
new file mode 100644 (file)
index 0000000..60da8c7
--- /dev/null
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Components shared between ASoC and HDA CS35L56 drivers
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+       { CS35L56_ASP1_ENABLES1,                0x00000000 },
+       { CS35L56_ASP1_CONTROL1,                0x00000028 },
+       { CS35L56_ASP1_CONTROL2,                0x18180200 },
+       { CS35L56_ASP1_CONTROL3,                0x00000002 },
+       { CS35L56_ASP1_FRAME_CONTROL1,          0x03020100 },
+       { CS35L56_ASP1_FRAME_CONTROL5,          0x00020100 },
+       { CS35L56_ASP1_DATA_CONTROL1,           0x00000018 },
+       { CS35L56_ASP1_DATA_CONTROL5,           0x00000018 },
+       { CS35L56_ASP1TX1_INPUT,                0x00000018 },
+       { CS35L56_ASP1TX2_INPUT,                0x00000019 },
+       { CS35L56_ASP1TX3_INPUT,                0x00000020 },
+       { CS35L56_ASP1TX4_INPUT,                0x00000028 },
+       { CS35L56_SWIRE_DP3_CH1_INPUT,          0x00000018 },
+       { CS35L56_SWIRE_DP3_CH2_INPUT,          0x00000019 },
+       { CS35L56_SWIRE_DP3_CH3_INPUT,          0x00000029 },
+       { CS35L56_SWIRE_DP3_CH4_INPUT,          0x00000028 },
+       { CS35L56_IRQ1_CFG,                     0x00000000 },
+       { CS35L56_IRQ1_MASK_1,                  0x83ffffff },
+       { CS35L56_IRQ1_MASK_2,                  0xffff7fff },
+       { CS35L56_IRQ1_MASK_4,                  0xe0ffffff },
+       { CS35L56_IRQ1_MASK_8,                  0xfc000fff },
+       { CS35L56_IRQ1_MASK_18,                 0x1f7df0ff },
+       { CS35L56_IRQ1_MASK_20,                 0x15c00000 },
+       /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default    */
+       /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default  */
+       /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default      */
+};
+
+static bool cs35l56_is_dsp_memory(unsigned int reg)
+{
+       switch (reg) {
+       case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+       case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095:
+       case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191:
+       case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+       case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070:
+       case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141:
+       case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L56_DEVID:
+       case CS35L56_REVID:
+       case CS35L56_RELID:
+       case CS35L56_OTPID:
+       case CS35L56_SFT_RESET:
+       case CS35L56_GLOBAL_ENABLES:
+       case CS35L56_BLOCK_ENABLES:
+       case CS35L56_BLOCK_ENABLES2:
+       case CS35L56_REFCLK_INPUT:
+       case CS35L56_GLOBAL_SAMPLE_RATE:
+       case CS35L56_ASP1_ENABLES1:
+       case CS35L56_ASP1_CONTROL1:
+       case CS35L56_ASP1_CONTROL2:
+       case CS35L56_ASP1_CONTROL3:
+       case CS35L56_ASP1_FRAME_CONTROL1:
+       case CS35L56_ASP1_FRAME_CONTROL5:
+       case CS35L56_ASP1_DATA_CONTROL1:
+       case CS35L56_ASP1_DATA_CONTROL5:
+       case CS35L56_DACPCM1_INPUT:
+       case CS35L56_DACPCM2_INPUT:
+       case CS35L56_ASP1TX1_INPUT:
+       case CS35L56_ASP1TX2_INPUT:
+       case CS35L56_ASP1TX3_INPUT:
+       case CS35L56_ASP1TX4_INPUT:
+       case CS35L56_DSP1RX1_INPUT:
+       case CS35L56_DSP1RX2_INPUT:
+       case CS35L56_SWIRE_DP3_CH1_INPUT:
+       case CS35L56_SWIRE_DP3_CH2_INPUT:
+       case CS35L56_SWIRE_DP3_CH3_INPUT:
+       case CS35L56_SWIRE_DP3_CH4_INPUT:
+       case CS35L56_IRQ1_CFG:
+       case CS35L56_IRQ1_STATUS:
+       case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+       case CS35L56_IRQ1_EINT_18:
+       case CS35L56_IRQ1_EINT_20:
+       case CS35L56_IRQ1_MASK_1:
+       case CS35L56_IRQ1_MASK_2:
+       case CS35L56_IRQ1_MASK_4:
+       case CS35L56_IRQ1_MASK_8:
+       case CS35L56_IRQ1_MASK_18:
+       case CS35L56_IRQ1_MASK_20:
+       case CS35L56_DSP_VIRTUAL1_MBOX_1:
+       case CS35L56_DSP_VIRTUAL1_MBOX_2:
+       case CS35L56_DSP_VIRTUAL1_MBOX_3:
+       case CS35L56_DSP_VIRTUAL1_MBOX_4:
+       case CS35L56_DSP_VIRTUAL1_MBOX_5:
+       case CS35L56_DSP_VIRTUAL1_MBOX_6:
+       case CS35L56_DSP_VIRTUAL1_MBOX_7:
+       case CS35L56_DSP_VIRTUAL1_MBOX_8:
+       case CS35L56_DSP_RESTRICT_STS1:
+       case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+       case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+       case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+       case CS35L56_DSP1_SCRATCH1:
+       case CS35L56_DSP1_SCRATCH2:
+       case CS35L56_DSP1_SCRATCH3:
+       case CS35L56_DSP1_SCRATCH4:
+               return true;
+       default:
+               return cs35l56_is_dsp_memory(reg);
+       }
+}
+
+static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+       case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+       case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L56_DEVID:
+       case CS35L56_REVID:
+       case CS35L56_RELID:
+       case CS35L56_OTPID:
+       case CS35L56_SFT_RESET:
+       case CS35L56_GLOBAL_ENABLES:               /* owned by firmware */
+       case CS35L56_BLOCK_ENABLES:                /* owned by firmware */
+       case CS35L56_BLOCK_ENABLES2:               /* owned by firmware */
+       case CS35L56_REFCLK_INPUT:                 /* owned by firmware */
+       case CS35L56_GLOBAL_SAMPLE_RATE:           /* owned by firmware */
+       case CS35L56_DACPCM1_INPUT:                /* owned by firmware */
+       case CS35L56_DACPCM2_INPUT:                /* owned by firmware */
+       case CS35L56_DSP1RX1_INPUT:                /* owned by firmware */
+       case CS35L56_DSP1RX2_INPUT:                /* owned by firmware */
+       case CS35L56_IRQ1_STATUS:
+       case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+       case CS35L56_IRQ1_EINT_18:
+       case CS35L56_IRQ1_EINT_20:
+       case CS35L56_DSP_VIRTUAL1_MBOX_1:
+       case CS35L56_DSP_VIRTUAL1_MBOX_2:
+       case CS35L56_DSP_VIRTUAL1_MBOX_3:
+       case CS35L56_DSP_VIRTUAL1_MBOX_4:
+       case CS35L56_DSP_VIRTUAL1_MBOX_5:
+       case CS35L56_DSP_VIRTUAL1_MBOX_6:
+       case CS35L56_DSP_VIRTUAL1_MBOX_7:
+       case CS35L56_DSP_VIRTUAL1_MBOX_8:
+       case CS35L56_DSP_RESTRICT_STS1:
+       case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+       case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+       case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+       case CS35L56_DSP1_SCRATCH1:
+       case CS35L56_DSP1_SCRATCH2:
+       case CS35L56_DSP1_SCRATCH3:
+       case CS35L56_DSP1_SCRATCH4:
+               return true;
+       case CS35L56_MAIN_RENDER_USER_MUTE:
+       case CS35L56_MAIN_RENDER_USER_VOLUME:
+       case CS35L56_MAIN_POSTURE_NUMBER:
+               return false;
+       default:
+               return cs35l56_is_dsp_memory(reg);
+       }
+}
+
+static const u32 cs35l56_firmware_registers[] = {
+       CS35L56_MAIN_RENDER_USER_MUTE,
+       CS35L56_MAIN_RENDER_USER_VOLUME,
+       CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap)
+{
+       int i;
+       unsigned int val;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) {
+               regmap_read(regmap, cs35l56_firmware_registers[i], &val);
+               dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__,
+                       i, cs35l56_firmware_registers[i], val);
+       }
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED);
+
+const struct cs_dsp_region cs35l56_dsp1_regions[] = {
+       { .type = WMFW_HALO_PM_PACKED,  .base = CS35L56_DSP1_PMEM_0 },
+       { .type = WMFW_HALO_XM_PACKED,  .base = CS35L56_DSP1_XMEM_PACKED_0 },
+       { .type = WMFW_HALO_YM_PACKED,  .base = CS35L56_DSP1_YMEM_PACKED_0 },
+       { .type = WMFW_ADSP2_XM,        .base = CS35L56_DSP1_XMEM_UNPACKED24_0 },
+       { .type = WMFW_ADSP2_YM,        .base = CS35L56_DSP1_YMEM_UNPACKED24_0 },
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED);
+
+static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
+       [0x0C] = 128000,
+       [0x0F] = 256000,
+       [0x11] = 384000,
+       [0x12] = 512000,
+       [0x15] = 768000,
+       [0x17] = 1024000,
+       [0x1A] = 1500000,
+       [0x1B] = 1536000,
+       [0x1C] = 2000000,
+       [0x1D] = 2048000,
+       [0x1E] = 2400000,
+       [0x20] = 3000000,
+       [0x21] = 3072000,
+       [0x23] = 4000000,
+       [0x24] = 4096000,
+       [0x25] = 4800000,
+       [0x27] = 6000000,
+       [0x28] = 6144000,
+       [0x29] = 6250000,
+       [0x2A] = 6400000,
+       [0x2E] = 8000000,
+       [0x2F] = 8192000,
+       [0x30] = 9600000,
+       [0x32] = 12000000,
+       [0x33] = 12288000,
+       [0x37] = 13500000,
+       [0x38] = 19200000,
+       [0x39] = 22579200,
+       [0x3B] = 24576000,
+};
+
+int cs35l56_get_bclk_freq_id(unsigned int freq)
+{
+       int i;
+
+       if (freq == 0)
+               return -EINVAL;
+
+       /* The BCLK frequency must be a valid PLL REFCLK */
+       for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) {
+               if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, SND_SOC_CS35L56_SHARED);
+
+static const char * const cs35l56_supplies[/* auto-sized */] = {
+       "VDD_P",
+       "VDD_IO",
+       "VDD_A",
+};
+
+void cs35l56_fill_supply_names(struct regulator_bulk_data *data)
+{
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES);
+       for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++)
+               data[i].supply = cs35l56_supplies[i];
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, SND_SOC_CS35L56_SHARED);
+
+const char * const cs35l56_tx_input_texts[] = {
+       "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH",
+       "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4",
+       "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON",
+       "INTERPOLATOR", "SDW1RX1", "SDW1RX2",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED);
+
+const unsigned int cs35l56_tx_input_values[] = {
+       CS35L56_INPUT_SRC_NONE,
+       CS35L56_INPUT_SRC_ASP1RX1,
+       CS35L56_INPUT_SRC_ASP1RX2,
+       CS35L56_INPUT_SRC_VMON,
+       CS35L56_INPUT_SRC_IMON,
+       CS35L56_INPUT_SRC_ERR_VOL,
+       CS35L56_INPUT_SRC_CLASSH,
+       CS35L56_INPUT_SRC_VDDBMON,
+       CS35L56_INPUT_SRC_VBSTMON,
+       CS35L56_INPUT_SRC_DSP1TX1,
+       CS35L56_INPUT_SRC_DSP1TX2,
+       CS35L56_INPUT_SRC_DSP1TX3,
+       CS35L56_INPUT_SRC_DSP1TX4,
+       CS35L56_INPUT_SRC_DSP1TX5,
+       CS35L56_INPUT_SRC_DSP1TX6,
+       CS35L56_INPUT_SRC_DSP1TX7,
+       CS35L56_INPUT_SRC_DSP1TX8,
+       CS35L56_INPUT_SRC_TEMPMON,
+       CS35L56_INPUT_SRC_INTERPOLATOR,
+       CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1,
+       CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_i2c = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L56_DSP1_PMEM_5114,
+       .reg_defaults = cs35l56_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+       .volatile_reg = cs35l56_volatile_reg,
+       .readable_reg = cs35l56_readable_reg,
+       .precious_reg = cs35l56_precious_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_spi = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .pad_bits = 16,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L56_DSP1_PMEM_5114,
+       .reg_defaults = cs35l56_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+       .volatile_reg = cs35l56_volatile_reg,
+       .readable_reg = cs35l56_readable_reg,
+       .precious_reg = cs35l56_precious_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_sdw = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L56_DSP1_PMEM_5114,
+       .reg_defaults = cs35l56_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+       .volatile_reg = cs35l56_volatile_reg,
+       .readable_reg = cs35l56_readable_reg,
+       .precious_reg = cs35l56_precious_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED);
+
+MODULE_DESCRIPTION("ASoC CS35L56 Shared");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644 (file)
index 0000000..996aab1
--- /dev/null
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_spi_probe(struct spi_device *spi)
+{
+       const struct regmap_config *regmap_config = &cs35l56_regmap_spi;
+       struct cs35l56_private *cs35l56;
+       int ret;
+
+       cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+       if (!cs35l56)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, cs35l56);
+       cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config);
+       if (IS_ERR(cs35l56->regmap)) {
+               ret = PTR_ERR(cs35l56->regmap);
+               return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
+       }
+
+       cs35l56->dev = &spi->dev;
+
+       ret = cs35l56_common_probe(cs35l56);
+       if (ret != 0)
+               return ret;
+
+       ret = cs35l56_init(cs35l56);
+       if (ret == 0)
+               ret = cs35l56_irq_request(cs35l56, spi->irq);
+       if (ret < 0)
+               cs35l56_remove(cs35l56);
+
+       return ret;
+}
+
+static void cs35l56_spi_remove(struct spi_device *spi)
+{
+       struct cs35l56_private *cs35l56 = spi_get_drvdata(spi);
+
+       cs35l56_remove(cs35l56);
+}
+
+static const struct spi_device_id cs35l56_id_spi[] = {
+       { "cs35l56", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l56_id_spi);
+
+static struct spi_driver cs35l56_spi_driver = {
+       .driver = {
+               .name           = "cs35l56",
+               .pm = &cs35l56_pm_ops_i2c_spi,
+       },
+       .id_table       = cs35l56_id_spi,
+       .probe          = cs35l56_spi_probe,
+       .remove         = cs35l56_spi_remove,
+};
+
+module_spi_driver(cs35l56_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SPI driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
new file mode 100644 (file)
index 0000000..46762f7
--- /dev/null
@@ -0,0 +1,1601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "wm_adsp.h"
+#include "cs35l56.h"
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event);
+
+static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command)
+{
+       unsigned int val;
+       int ret;
+
+       regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+       ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+                                      val, (val == 0),
+                                      CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+       if (ret) {
+               dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56)
+{
+       /* Wait for patching to complete */
+       flush_work(&cs35l56->dsp_work);
+}
+
+static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+       cs35l56_wait_dsp_ready(cs35l56);
+       return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+       cs35l56_wait_dsp_ready(cs35l56);
+       return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
+
+static const struct snd_kcontrol_new cs35l56_controls[] = {
+       SOC_SINGLE_EXT("Speaker Switch",
+                      CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+                      cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+       SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+                            CS35L56_MAIN_RENDER_USER_VOLUME,
+                            6, -400, 400, 9, 0,
+                            cs35l56_dspwait_get_volsw,
+                            cs35l56_dspwait_put_volsw,
+                            vol_tlv),
+       SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER,
+                      0, 255, 0,
+                      cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
+                                 CS35L56_ASP1TX1_INPUT,
+                                 0, CS35L56_ASP_TXn_SRC_MASK,
+                                 cs35l56_tx_input_texts,
+                                 cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx1_mux =
+       SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
+                                 CS35L56_ASP1TX2_INPUT,
+                                 0, CS35L56_ASP_TXn_SRC_MASK,
+                                 cs35l56_tx_input_texts,
+                                 cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx2_mux =
+       SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
+                                 CS35L56_ASP1TX3_INPUT,
+                                 0, CS35L56_ASP_TXn_SRC_MASK,
+                                 cs35l56_tx_input_texts,
+                                 cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx3_mux =
+       SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
+                                 CS35L56_ASP1TX4_INPUT,
+                                 0, CS35L56_ASP_TXn_SRC_MASK,
+                                 cs35l56_tx_input_texts,
+                                 cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx4_mux =
+       SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
+                               CS35L56_SWIRE_DP3_CH1_INPUT,
+                               0, CS35L56_SWIRETXn_SRC_MASK,
+                               cs35l56_tx_input_texts,
+                               cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx1_mux =
+       SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum,
+                               CS35L56_SWIRE_DP3_CH2_INPUT,
+                               0, CS35L56_SWIRETXn_SRC_MASK,
+                               cs35l56_tx_input_texts,
+                               cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx2_mux =
+       SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum,
+                               CS35L56_SWIRE_DP3_CH3_INPUT,
+                               0, CS35L56_SWIRETXn_SRC_MASK,
+                               cs35l56_tx_input_texts,
+                               cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx3_mux =
+       SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
+                               CS35L56_SWIRE_DP3_CH4_INPUT,
+                               0, CS35L56_SWIRETXn_SRC_MASK,
+                               cs35l56_tx_input_texts,
+                               cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx4_mux =
+       SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
+
+static int cs35l56_play_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 cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+       unsigned int val;
+       int ret;
+
+       dev_dbg(cs35l56->dev, "play: %d\n", event);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* Don't wait for ACK, we check in POST_PMU that it completed */
+               return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+                                   CS35L56_MBOX_CMD_AUDIO_PLAY);
+       case SND_SOC_DAPM_POST_PMU:
+               /* Wait for firmware to enter PS0 power state */
+               ret = regmap_read_poll_timeout(cs35l56->regmap,
+                                              CS35L56_TRANSDUCER_ACTUAL_PS,
+                                              val, (val == CS35L56_PS0),
+                                              CS35L56_PS0_POLL_US,
+                                              CS35L56_PS0_TIMEOUT_US);
+               if (ret)
+                       dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret);
+               return ret;
+       case SND_SOC_DAPM_POST_PMD:
+               return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+       default:
+               return 0;
+       }
+}
+
+static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
+       SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
+       SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
+
+       SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("SPK"),
+
+       SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+                           CS35L56_ASP_RX1_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+                           CS35L56_ASP_RX2_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+                            CS35L56_ASP_TX1_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+                            CS35L56_ASP_TX2_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
+                            CS35L56_ASP_TX3_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
+                            CS35L56_ASP_TX4_EN_SHIFT, 0),
+
+       SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux),
+       SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux),
+       SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux),
+       SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux),
+
+       SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux),
+       SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux),
+       SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux),
+       SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux),
+
+       SND_SOC_DAPM_SIGGEN("VMON ADC"),
+       SND_SOC_DAPM_SIGGEN("IMON ADC"),
+       SND_SOC_DAPM_SIGGEN("ERRVOL ADC"),
+       SND_SOC_DAPM_SIGGEN("CLASSH ADC"),
+       SND_SOC_DAPM_SIGGEN("VDDBMON ADC"),
+       SND_SOC_DAPM_SIGGEN("VBSTMON ADC"),
+       SND_SOC_DAPM_SIGGEN("TEMPMON ADC"),
+};
+
+#define CS35L56_SRC_ROUTE(name) \
+       { name" Source", "ASP1RX1", "ASP1RX1" }, \
+       { name" Source", "ASP1RX2", "ASP1RX2" }, \
+       { name" Source", "VMON", "VMON ADC" }, \
+       { name" Source", "IMON", "IMON ADC" }, \
+       { name" Source", "ERRVOL", "ERRVOL ADC" },   \
+       { name" Source", "CLASSH", "CLASSH ADC" },   \
+       { name" Source", "VDDBMON", "VDDBMON ADC" }, \
+       { name" Source", "VBSTMON", "VBSTMON ADC" }, \
+       { name" Source", "DSP1TX1", "DSP1" }, \
+       { name" Source", "DSP1TX2", "DSP1" }, \
+       { name" Source", "DSP1TX3", "DSP1" }, \
+       { name" Source", "DSP1TX4", "DSP1" }, \
+       { name" Source", "DSP1TX5", "DSP1" }, \
+       { name" Source", "DSP1TX6", "DSP1" }, \
+       { name" Source", "DSP1TX7", "DSP1" }, \
+       { name" Source", "DSP1TX8", "DSP1" }, \
+       { name" Source", "TEMPMON", "TEMPMON ADC" }, \
+       { name" Source", "INTERPOLATOR", "AMP" }, \
+       { name" Source", "SDW1RX1", "SDW1 Playback" }, \
+       { name" Source", "SDW1RX2", "SDW1 Playback" },
+
+static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
+       { "AMP", NULL, "VDD_B" },
+       { "AMP", NULL, "VDD_AMP" },
+
+       { "ASP1 Playback", NULL, "PLAY" },
+       { "SDW1 Playback", NULL, "PLAY" },
+
+       { "ASP1RX1", NULL, "ASP1 Playback" },
+       { "ASP1RX2", NULL, "ASP1 Playback" },
+       { "DSP1", NULL, "ASP1RX1" },
+       { "DSP1", NULL, "ASP1RX2" },
+       { "DSP1", NULL, "SDW1 Playback" },
+       { "AMP", NULL, "DSP1" },
+       { "SPK", NULL, "AMP" },
+
+       CS35L56_SRC_ROUTE("ASP1 TX1")
+       CS35L56_SRC_ROUTE("ASP1 TX2")
+       CS35L56_SRC_ROUTE("ASP1 TX3")
+       CS35L56_SRC_ROUTE("ASP1 TX4")
+
+       { "ASP1TX1", NULL, "ASP1 TX1 Source" },
+       { "ASP1TX2", NULL, "ASP1 TX2 Source" },
+       { "ASP1TX3", NULL, "ASP1 TX3 Source" },
+       { "ASP1TX4", NULL, "ASP1 TX4 Source" },
+       { "ASP1 Capture", NULL, "ASP1TX1" },
+       { "ASP1 Capture", NULL, "ASP1TX2" },
+       { "ASP1 Capture", NULL, "ASP1TX3" },
+       { "ASP1 Capture", NULL, "ASP1TX4" },
+
+       CS35L56_SRC_ROUTE("SDW1 TX1")
+       CS35L56_SRC_ROUTE("SDW1 TX2")
+       CS35L56_SRC_ROUTE("SDW1 TX3")
+       CS35L56_SRC_ROUTE("SDW1 TX4")
+       { "SDW1 Capture", NULL, "SDW1 TX1 Source" },
+       { "SDW1 Capture", NULL, "SDW1 TX2 Source" },
+       { "SDW1 Capture", NULL, "SDW1 TX3 Source" },
+       { "SDW1 Capture", NULL, "SDW1 TX4 Source" },
+};
+
+static int cs35l56_dsp_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 cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+       dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event);
+
+       return wm_adsp_event(w, kcontrol, event);
+}
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+       struct cs35l56_private *cs35l56 = data;
+       unsigned int status1 = 0, status8 = 0, status20 = 0;
+       unsigned int mask1, mask8, mask20;
+       unsigned int val;
+       int rv;
+
+       irqreturn_t ret = IRQ_NONE;
+
+       if (!cs35l56->init_done)
+               return IRQ_NONE;
+
+       mutex_lock(&cs35l56->irq_lock);
+
+       rv = pm_runtime_resume_and_get(cs35l56->dev);
+       if (rv < 0) {
+               dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv);
+               goto err_unlock;
+       }
+
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val);
+       if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+               dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n");
+               goto err;
+       }
+
+       /* Ack interrupts */
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1);
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+       status1 &= ~mask1;
+       regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8);
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+       status8 &= ~mask8;
+       regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20);
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+       status20 &= ~mask20;
+       /* We don't want EINT20 but they default to unmasked: force mask */
+       regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+       dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8);
+
+       /* Check to see if unmasked bits are active */
+       if (!status1 && !status8 && !status20)
+               goto err;
+
+       if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
+               dev_crit(cs35l56->dev, "Amp short error\n");
+
+       if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+               dev_crit(cs35l56->dev, "Overtemp error\n");
+
+       ret = IRQ_HANDLED;
+
+err:
+       pm_runtime_put(cs35l56->dev);
+err_unlock:
+       mutex_unlock(&cs35l56->irq_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE);
+
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq)
+{
+       int ret;
+
+       if (!irq)
+               return 0;
+
+       ret = devm_request_threaded_irq(cs35l56->dev, irq, NULL, cs35l56_irq,
+                                       IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+                                       "cs35l56", cs35l56);
+       if (!ret)
+               cs35l56->irq = irq;
+       else
+               dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
+       unsigned int val;
+
+       dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt);
+
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+       case SND_SOC_DAIFMT_CBC_CFC:
+               break;
+       default:
+               dev_err(cs35l56->dev, "Unsupported clock source mode\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT;
+               cs35l56->tdm_mode = true;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT;
+               cs35l56->tdm_mode = false;
+               break;
+       default:
+               dev_err(cs35l56->dev, "Unsupported DAI format\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_IF:
+               val |= CS35L56_ASP_FSYNC_INV_MASK;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               val |= CS35L56_ASP_BCLK_INV_MASK;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       default:
+               dev_err(cs35l56->dev, "Invalid clock invert\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l56->regmap,
+                          CS35L56_ASP1_CONTROL2,
+                          CS35L56_ASP_FMT_MASK |
+                          CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK,
+                          val);
+
+       /* Hi-Z DOUT in unused slots and when all TX are disabled */
+       regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3,
+                          CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
+                          CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
+
+       return 0;
+}
+
+static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
+                                          unsigned int reg, unsigned long mask)
+{
+       unsigned int reg_val, channel_shift;
+       int bit_num;
+
+       /* Init all slots to 63 */
+       switch (reg) {
+       case CS35L56_ASP1_FRAME_CONTROL1:
+               reg_val = 0x3f3f3f3f;
+               break;
+       case CS35L56_ASP1_FRAME_CONTROL5:
+               reg_val = 0x3f3f3f;
+               break;
+       }
+
+       /* Enable consecutive TX1..TXn for each of the slots set in mask */
+       channel_shift = 0;
+       for_each_set_bit(bit_num, &mask, 32) {
+               reg_val &= ~(0x3f << channel_shift);
+               reg_val |= bit_num << channel_shift;
+               channel_shift += 8;
+       }
+
+       regmap_write(cs35l56->regmap, reg, reg_val);
+}
+
+static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                                       unsigned int rx_mask, int slots, int slot_width)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+       if ((slots == 0) || (slot_width == 0)) {
+               dev_dbg(cs35l56->dev, "tdm config cleared\n");
+               cs35l56->asp_slot_width = 0;
+               cs35l56->asp_slot_count = 0;
+               return 0;
+       }
+
+       if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) {
+               dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width);
+               return -EINVAL;
+       }
+
+       /* More than 32 slots would give an unsupportable BCLK frequency */
+       if (slots > 32) {
+               dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots);
+               return -EINVAL;
+       }
+
+       cs35l56->asp_slot_width = (u8)slot_width;
+       cs35l56->asp_slot_count = (u8)slots;
+
+       // Note: rx/tx is from point of view of the CPU end
+       if (tx_mask == 0)
+               tx_mask = 0x3;  // ASPRX1/RX2 in slots 0 and 1
+
+       if (rx_mask == 0)
+               rx_mask = 0xf;  // ASPTX1..TX4 in slots 0..3
+
+       cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask);
+       cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask);
+
+       dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
+               cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask);
+
+       return 0;
+}
+
+static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *params,
+                                    struct snd_soc_dai *dai)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+       unsigned int rate = params_rate(params);
+       u8 asp_width, asp_wl;
+
+       asp_wl = params_width(params);
+       if (cs35l56->asp_slot_width)
+               asp_width = cs35l56->asp_slot_width;
+       else
+               asp_width = asp_wl;
+
+       dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate);
+
+       if (!cs35l56->sysclk_set) {
+               unsigned int slots = cs35l56->asp_slot_count;
+               unsigned int bclk_freq;
+               int freq_id;
+
+               if (slots == 0) {
+                       slots = params_channels(params);
+
+                       /* I2S always has an even number of slots */
+                       if (!cs35l56->tdm_mode)
+                               slots = round_up(slots, 2);
+               }
+
+               bclk_freq = asp_width * slots * rate;
+               freq_id = cs35l56_get_bclk_freq_id(bclk_freq);
+               if (freq_id < 0) {
+                       dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+                       return -EINVAL;
+               }
+
+               regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+                                  CS35L56_ASP_BCLK_FREQ_MASK,
+                                  freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+                                  CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
+                                  CS35L56_ASP_RX_WIDTH_SHIFT);
+               regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5,
+                                  CS35L56_ASP_RX_WL_MASK, asp_wl);
+       } else {
+               regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+                                  CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
+                                  CS35L56_ASP_TX_WIDTH_SHIFT);
+               regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1,
+                                  CS35L56_ASP_TX_WL_MASK, asp_wl);
+       }
+
+       return 0;
+}
+
+static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
+                                     int clk_id, unsigned int freq, int dir)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+       int freq_id;
+
+       if (freq == 0) {
+               cs35l56->sysclk_set = false;
+               return 0;
+       }
+
+       freq_id = cs35l56_get_bclk_freq_id(freq);
+       if (freq_id < 0)
+               return freq_id;
+
+       regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+                          CS35L56_ASP_BCLK_FREQ_MASK,
+                          freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+       cs35l56->sysclk_set = true;
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_ops = {
+       .set_fmt = cs35l56_asp_dai_set_fmt,
+       .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
+       .hw_params = cs35l56_asp_dai_hw_params,
+       .set_sysclk = cs35l56_asp_dai_set_sysclk,
+};
+
+static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+                                    struct snd_soc_dai *dai)
+{
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                                       unsigned int rx_mask, int slots, int slot_width)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+       /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */
+       cs35l56->rx_mask = tx_mask;
+       cs35l56->tx_mask = rx_mask;
+
+       return 0;
+}
+
+static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *params,
+                                    struct snd_soc_dai *dai)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+       struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+       struct sdw_stream_config sconfig;
+       struct sdw_port_config pconfig;
+       int ret;
+
+       dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params));
+
+       if (!cs35l56->init_done)
+               return -ENODEV;
+
+       if (!sdw_stream)
+               return -EINVAL;
+
+       memset(&sconfig, 0, sizeof(sconfig));
+       memset(&pconfig, 0, sizeof(pconfig));
+
+       sconfig.frame_rate = params_rate(params);
+       sconfig.bps = snd_pcm_format_width(params_format(params));
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               sconfig.direction = SDW_DATA_DIR_RX;
+               pconfig.num = CS35L56_SDW1_PLAYBACK_PORT;
+               pconfig.ch_mask = cs35l56->rx_mask;
+       } else {
+               sconfig.direction = SDW_DATA_DIR_TX;
+               pconfig.num = CS35L56_SDW1_CAPTURE_PORT;
+               pconfig.ch_mask = cs35l56->tx_mask;
+       }
+
+       if (pconfig.ch_mask == 0) {
+               sconfig.ch_count = params_channels(params);
+               pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0);
+       } else {
+               sconfig.ch_count = hweight32(pconfig.ch_mask);
+       }
+
+       ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig,
+                                  1, sdw_stream);
+       if (ret) {
+               dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+       struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+       if (!cs35l56->sdw_peripheral)
+               return -EINVAL;
+
+       sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream);
+
+       return 0;
+}
+
+static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai,
+                                     void *sdw_stream, int direction)
+{
+       if (!sdw_stream)
+               return 0;
+
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = {
+       .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot,
+       .shutdown = cs35l56_sdw_dai_shutdown,
+       .hw_params = cs35l56_sdw_dai_hw_params,
+       .hw_free = cs35l56_sdw_dai_hw_free,
+       .set_stream = cs35l56_sdw_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver cs35l56_dai[] = {
+       {
+               .name = "cs35l56-asp1",
+               .id = 0,
+               .playback = {
+                       .stream_name = "ASP1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = CS35L56_RATES,
+                       .formats = CS35L56_RX_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "ASP1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = CS35L56_RATES,
+                       .formats = CS35L56_TX_FORMATS,
+               },
+               .ops = &cs35l56_ops,
+               .symmetric_rate = 1,
+               .symmetric_sample_bits = 1,
+       },
+       {
+               .name = "cs35l56-sdw1",
+               .id = 1,
+               .playback = {
+                       .stream_name = "SDW1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = CS35L56_RATES,
+                       .formats = CS35L56_RX_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "SDW1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = CS35L56_RATES,
+                       .formats = CS35L56_TX_FORMATS,
+               },
+               .symmetric_rate = 1,
+               .ops = &cs35l56_sdw_dai_ops,
+       }
+};
+
+static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56)
+{
+       unsigned int reg;
+       unsigned int val;
+       int ret;
+
+       if (cs35l56->rev < CS35L56_REVID_B0)
+               reg = CS35L56_DSP1_HALO_STATE_A1;
+       else
+               reg = CS35L56_DSP1_HALO_STATE;
+
+       ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+                                      val,
+                                      (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+                                      CS35L56_HALO_STATE_POLL_US,
+                                      CS35L56_HALO_STATE_TIMEOUT_US);
+
+       if ((ret < 0) && (ret != -ETIMEDOUT)) {
+               dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret);
+               return ret;
+       }
+
+       if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
+               dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static inline void cs35l56_wait_min_reset_pulse(void)
+{
+       /* Satisfy minimum reset pulse width spec */
+       usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+       REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static void cs35l56_system_reset(struct cs35l56_private *cs35l56)
+{
+       cs35l56->soft_resetting = true;
+
+       /*
+        * Must enter cache-only first so there can't be any more register
+        * accesses other than the controlled system reset sequence below.
+        */
+       regcache_cache_only(cs35l56->regmap, true);
+       regmap_multi_reg_write_bypassed(cs35l56->regmap,
+                                       cs35l56_system_reset_seq,
+                                       ARRAY_SIZE(cs35l56_system_reset_seq));
+
+       /* On SoundWire the registers won't be accessible until it re-enumerates. */
+       if (cs35l56->sdw_peripheral)
+               return;
+
+       usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400);
+       regcache_cache_only(cs35l56->regmap, false);
+}
+
+static void cs35l56_dsp_work(struct work_struct *work)
+{
+       struct cs35l56_private *cs35l56 = container_of(work,
+                                                      struct cs35l56_private,
+                                                      dsp_work);
+       unsigned int reg;
+       unsigned int val;
+       int ret = 0;
+
+       if (!cs35l56->init_done)
+               return;
+
+       cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x",
+                                          cs35l56->secured ? "s" : "", cs35l56->rev);
+
+       if (!cs35l56->dsp.part)
+               return;
+
+       pm_runtime_get_sync(cs35l56->dev);
+
+       /*
+        * Disable SoundWire interrupts to prevent race with IRQ work.
+        * Setting sdw_irq_no_unmask prevents the handler re-enabling
+        * the SoundWire interrupt.
+        */
+       if (cs35l56->sdw_peripheral) {
+               cs35l56->sdw_irq_no_unmask = true;
+               cancel_work_sync(&cs35l56->sdw_irq_work);
+               sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+               sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+               sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+       }
+
+       ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN);
+       if (ret)
+               goto err;
+
+       if (cs35l56->rev < CS35L56_REVID_B0)
+               reg = CS35L56_DSP1_PM_CUR_STATE_A1;
+       else
+               reg = CS35L56_DSP1_PM_CUR_STATE;
+
+       ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+                                      val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+                                      CS35L56_HALO_STATE_POLL_US,
+                                      CS35L56_HALO_STATE_TIMEOUT_US);
+       if (ret < 0)
+               dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+                       val, ret);
+
+       /* Use wm_adsp to load and apply the firmware patch and coefficient files */
+       ret = wm_adsp_power_up(&cs35l56->dsp);
+       if (ret) {
+               dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+               goto err;
+       }
+
+       mutex_lock(&cs35l56->irq_lock);
+
+       init_completion(&cs35l56->init_completion);
+
+       cs35l56_system_reset(cs35l56);
+
+       if (cs35l56->sdw_peripheral) {
+               /*
+                * The system-reset causes the CS35L56 to detach from the bus.
+                * Wait for the manager to re-enumerate the CS35L56 and
+                * cs35l56_init() to run again.
+                */
+               if (!wait_for_completion_timeout(&cs35l56->init_completion,
+                                                msecs_to_jiffies(5000))) {
+                       dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__);
+                       goto err_unlock;
+               }
+       } else if (cs35l56_init(cs35l56)) {
+               goto err_unlock;
+       }
+
+       regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING);
+       cs35l56->fw_patched = true;
+
+err_unlock:
+       mutex_unlock(&cs35l56->irq_lock);
+err:
+       pm_runtime_mark_last_busy(cs35l56->dev);
+       pm_runtime_put_autosuspend(cs35l56->dev);
+
+       /* Re-enable SoundWire interrupts */
+       if (cs35l56->sdw_peripheral) {
+               cs35l56->sdw_irq_no_unmask = false;
+               sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+                               CS35L56_SDW_INT_MASK_CODEC_IRQ);
+       }
+}
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+       struct dentry *debugfs_root = component->debugfs_root;
+
+       BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
+       if (!wait_for_completion_timeout(&cs35l56->init_completion,
+                                        msecs_to_jiffies(5000))) {
+               dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__);
+               return -ENODEV;
+       }
+
+       cs35l56->component = component;
+       wm_adsp2_component_probe(&cs35l56->dsp, component);
+
+       debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done);
+       debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate);
+       debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched);
+
+       queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+       return 0;
+}
+
+static void cs35l56_component_remove(struct snd_soc_component *component)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+       cancel_work_sync(&cs35l56->dsp_work);
+}
+
+static int cs35l56_set_bias_level(struct snd_soc_component *component,
+                                 enum snd_soc_bias_level level)
+{
+       struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+       switch (level) {
+       case SND_SOC_BIAS_STANDBY:
+               /*
+                * Wait for patching to complete when transitioning from
+                * BIAS_OFF to BIAS_STANDBY
+                */
+               if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+                       cs35l56_wait_dsp_ready(cs35l56);
+
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
+       .probe = cs35l56_component_probe,
+       .remove = cs35l56_component_remove,
+
+       .dapm_widgets = cs35l56_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
+       .dapm_routes = cs35l56_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
+       .controls = cs35l56_controls,
+       .num_controls = ARRAY_SIZE(cs35l56_controls),
+
+       .set_bias_level = cs35l56_set_bias_level,
+
+       .suspend_bias_off = 1, /* see cs35l56_system_resume() */
+};
+
+static const struct reg_sequence cs35l56_hibernate_seq[] = {
+       /* This must be the last register access */
+       REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW),
+};
+
+static const struct reg_sequence cs35l56_hibernate_wake_seq[] = {
+       REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP),
+};
+
+int cs35l56_runtime_suspend(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+       unsigned int val;
+       int ret;
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       /* Firmware must have entered a power-save state */
+       ret = regmap_read_poll_timeout(cs35l56->regmap,
+                                      CS35L56_TRANSDUCER_ACTUAL_PS,
+                                      val, (val >= CS35L56_PS3),
+                                      CS35L56_PS3_POLL_US,
+                                      CS35L56_PS3_TIMEOUT_US);
+       if (ret)
+               dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret);
+
+       /* Clear BOOT_DONE so it can be used to detect a reboot */
+       regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+       if (!cs35l56->can_hibernate) {
+               regcache_cache_only(cs35l56->regmap, true);
+               dev_dbg(dev, "Suspended: no hibernate");
+
+               return 0;
+       }
+
+       /*
+        * Enable auto-hibernate. If it is woken by some other wake source
+        * it will automatically return to hibernate.
+        */
+       cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+
+       /*
+        * Must enter cache-only first so there can't be any more register
+        * accesses other than the controlled hibernate sequence below.
+        */
+       regcache_cache_only(cs35l56->regmap, true);
+
+       regmap_multi_reg_write_bypassed(cs35l56->regmap,
+                                       cs35l56_hibernate_seq,
+                                       ARRAY_SIZE(cs35l56_hibernate_seq));
+
+       dev_dbg(dev, "Suspended: hibernate");
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE);
+
+static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       if (!cs35l56->init_done)
+               return 0;
+
+       return cs35l56_runtime_resume_common(cs35l56);
+}
+
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56)
+{
+       unsigned int val;
+       int ret;
+
+       if (!cs35l56->can_hibernate)
+               goto out_sync;
+
+       if (!cs35l56->sdw_peripheral) {
+               /*
+                * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C.
+                * Must be done before releasing cache-only.
+                */
+               regmap_multi_reg_write_bypassed(cs35l56->regmap,
+                                               cs35l56_hibernate_wake_seq,
+                                               ARRAY_SIZE(cs35l56_hibernate_wake_seq));
+
+               usleep_range(CS35L56_CONTROL_PORT_READY_US,
+                            CS35L56_CONTROL_PORT_READY_US + 400);
+       }
+
+out_sync:
+       regcache_cache_only(cs35l56->regmap, false);
+
+       ret = cs35l56_wait_for_firmware_boot(cs35l56);
+       if (ret) {
+               dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret);
+               goto err;
+       }
+
+       ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+       if (ret)
+               goto err;
+
+       /* BOOT_DONE will be 1 if the amp reset */
+       regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val);
+       if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+               dev_dbg(cs35l56->dev, "Registers reset in suspend\n");
+               regcache_mark_dirty(cs35l56->regmap);
+       }
+
+       regcache_sync(cs35l56->regmap);
+
+       dev_dbg(cs35l56->dev, "Resumed");
+
+       return 0;
+
+err:
+       regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+                    CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+       regcache_cache_only(cs35l56->regmap, true);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56)
+{
+       unsigned int val;
+       int ret;
+
+       /* Nothing to re-patch if we haven't done any patching yet. */
+       if (!cs35l56->fw_patched)
+               return false;
+
+       /*
+        * If we have control of RESET we will have asserted it so the firmware
+        * will need re-patching.
+        */
+       if (cs35l56->reset_gpio)
+               return true;
+
+       /*
+        * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+        * can't be used here to test for memory retention.
+        * Assume that tuning must be re-loaded.
+        */
+       if (cs35l56->secured)
+               return true;
+
+       ret = pm_runtime_resume_and_get(cs35l56->dev);
+       if (ret) {
+               dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret);
+               return ret;
+       }
+
+       ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val);
+       if (ret)
+               dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+       else
+               ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+       pm_runtime_put_autosuspend(cs35l56->dev);
+
+       return ret;
+}
+
+int cs35l56_system_suspend(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "system_suspend\n");
+
+       if (cs35l56->component)
+               flush_work(&cs35l56->dsp_work);
+
+       /*
+        * The interrupt line is normally shared, but after we start suspending
+        * we can't check if our device is the source of an interrupt, and can't
+        * clear it. Prevent this race by temporarily disabling the parent irq
+        * until we reach _no_irq.
+        */
+       if (cs35l56->irq)
+               disable_irq(cs35l56->irq);
+
+       return pm_runtime_force_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
+
+int cs35l56_system_suspend_late(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "system_suspend_late\n");
+
+       /*
+        * Assert RESET before removing supplies.
+        * RESET is usually shared by all amps so it must not be asserted until
+        * all driver instances have done their suspend() stage.
+        */
+       if (cs35l56->reset_gpio) {
+               gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+               cs35l56_wait_min_reset_pulse();
+       }
+
+       regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late);
+
+int cs35l56_system_suspend_no_irq(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "system_suspend_no_irq\n");
+
+       /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+       if (cs35l56->irq)
+               enable_irq(cs35l56->irq);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq);
+
+int cs35l56_system_resume_no_irq(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "system_resume_no_irq\n");
+
+       /*
+        * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+        * spurious interrupts, and the interrupt line is normally shared.
+        * We can't check if our device is the source of an interrupt, and can't
+        * clear it, until it has fully resumed. Prevent this race by temporarily
+        * disabling the parent irq until we complete resume().
+        */
+       if (cs35l56->irq)
+               disable_irq(cs35l56->irq);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq);
+
+int cs35l56_system_resume_early(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "system_resume_early\n");
+
+       /* Ensure a spec-compliant RESET pulse. */
+       if (cs35l56->reset_gpio) {
+               gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+               cs35l56_wait_min_reset_pulse();
+       }
+
+       /* Enable supplies before releasing RESET. */
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+       if (ret) {
+               dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       /* Release shared RESET before drivers start resume(). */
+       gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_early);
+
+int cs35l56_system_resume(struct device *dev)
+{
+       struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "system_resume\n");
+
+       /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+       ret = pm_runtime_force_resume(dev);
+       if (cs35l56->irq)
+               enable_irq(cs35l56->irq);
+
+       if (ret)
+               return ret;
+
+       /* Firmware won't have been loaded if the component hasn't probed */
+       if (!cs35l56->component)
+               return 0;
+
+       ret = cs35l56_is_fw_reload_needed(cs35l56);
+       dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret);
+       if (ret < 1)
+               return ret;
+
+       cs35l56->fw_patched = false;
+       queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+       /*
+        * suspend_bias_off ensures we are now in BIAS_OFF so there will be
+        * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching.
+        */
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume);
+
+static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
+{
+       struct wm_adsp *dsp;
+       int ret;
+
+       cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp");
+       if (!cs35l56->dsp_wq)
+               return -ENOMEM;
+
+       INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
+
+       dsp = &cs35l56->dsp;
+       dsp->part = "cs35l56";
+       dsp->cs_dsp.num = 1;
+       dsp->cs_dsp.type = WMFW_HALO;
+       dsp->cs_dsp.rev = 0;
+       dsp->fw = 12;
+       dsp->cs_dsp.dev = cs35l56->dev;
+       dsp->cs_dsp.regmap = cs35l56->regmap;
+       dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE;
+       dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+       dsp->cs_dsp.mem = cs35l56_dsp1_regions;
+       dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+       dsp->cs_dsp.no_core_startstop = true;
+       dsp->wmfw_optional = true;
+
+       dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name);
+
+       ret = wm_halo_init(dsp);
+       if (ret != 0) {
+               dev_err(cs35l56->dev, "wm_halo_init failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56)
+{
+       acpi_handle handle = ACPI_HANDLE(cs35l56->dev);
+       const char *sub;
+
+       /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */
+       if (!handle)
+               return 0;
+
+       sub = acpi_get_subsystem_id(handle);
+       if (IS_ERR(sub)) {
+               /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */
+               if (PTR_ERR(sub) == -ENODATA)
+                       return 0;
+               else
+                       return PTR_ERR(sub);
+       }
+
+       cs35l56->dsp.system_name = sub;
+       dev_dbg(cs35l56->dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name);
+
+       return 0;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+       int ret;
+
+       init_completion(&cs35l56->init_completion);
+       mutex_init(&cs35l56->irq_lock);
+
+       dev_set_drvdata(cs35l56->dev, cs35l56);
+
+       cs35l56_fill_supply_names(cs35l56->supplies);
+       ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies),
+                                     cs35l56->supplies);
+       if (ret != 0)
+               return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n");
+
+       /* Reset could be controlled by the BIOS or shared by multiple amps */
+       cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(cs35l56->reset_gpio)) {
+               ret = PTR_ERR(cs35l56->reset_gpio);
+               /*
+                * If RESET is shared the first amp to probe will grab the reset
+                * line and reset all the amps
+                */
+               if (ret != -EBUSY)
+                       return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n");
+
+               dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n");
+               cs35l56->reset_gpio = NULL;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+       if (ret != 0)
+               return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n");
+
+       if (cs35l56->reset_gpio) {
+               cs35l56_wait_min_reset_pulse();
+               gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+       }
+
+       ret = cs35l56_acpi_get_name(cs35l56);
+       if (ret != 0)
+               goto err;
+
+       ret = cs35l56_dsp_init(cs35l56);
+       if (ret < 0) {
+               dev_err_probe(cs35l56->dev, ret, "DSP init failed\n");
+               goto err;
+       }
+
+       ret = devm_snd_soc_register_component(cs35l56->dev,
+                                             &soc_component_dev_cs35l56,
+                                             cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
+       if (ret < 0) {
+               dev_err_probe(cs35l56->dev, ret, "Register codec failed\n");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+       regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE);
+
+int cs35l56_init(struct cs35l56_private *cs35l56)
+{
+       int ret;
+       unsigned int devid, revid, otpid, secured;
+
+       /*
+        * Check whether the actions associated with soft reset or one time
+        * init need to be performed.
+        */
+       if (cs35l56->soft_resetting)
+               goto post_soft_reset;
+
+       if (cs35l56->init_done)
+               return 0;
+
+       pm_runtime_set_autosuspend_delay(cs35l56->dev, 100);
+       pm_runtime_use_autosuspend(cs35l56->dev);
+       pm_runtime_set_active(cs35l56->dev);
+       pm_runtime_enable(cs35l56->dev);
+
+       /*
+        * If the system is not using a reset_gpio then issue a
+        * dummy read to force a wakeup.
+        */
+       if (!cs35l56->reset_gpio)
+               regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid);
+
+       /* Wait for control port to be ready (datasheet tIRS). */
+       usleep_range(CS35L56_CONTROL_PORT_READY_US,
+                    CS35L56_CONTROL_PORT_READY_US + 400);
+
+       /*
+        * The HALO_STATE register is in different locations on Ax and B0
+        * devices so the REVID needs to be determined before waiting for the
+        * firmware to boot.
+        */
+       ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid);
+       if (ret < 0) {
+               dev_err(cs35l56->dev, "Get Revision ID failed\n");
+               return ret;
+       }
+       cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+
+       ret = cs35l56_wait_for_firmware_boot(cs35l56);
+       if (ret)
+               return ret;
+
+       ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid);
+       if (ret < 0) {
+               dev_err(cs35l56->dev, "Get Device ID failed\n");
+               return ret;
+       }
+       devid &= CS35L56_DEVID_MASK;
+
+       switch (devid) {
+       case 0x35A56:
+               break;
+       default:
+               dev_err(cs35l56->dev, "Unknown device %x\n", devid);
+               return ret;
+       }
+
+       ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+       if (ret) {
+               dev_err(cs35l56->dev, "Get Secure status failed\n");
+               return ret;
+       }
+
+       /* When any bus is restricted treat the device as secured */
+       if (secured & CS35L56_RESTRICTED_MASK)
+               cs35l56->secured = true;
+
+       ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid);
+       if (ret < 0) {
+               dev_err(cs35l56->dev, "Get OTP ID failed\n");
+               return ret;
+       }
+
+       dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
+                cs35l56->secured ? "s" : "", cs35l56->rev, otpid);
+
+       /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
+       regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+       regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1,
+                          CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+                          0);
+       regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8,
+                          CS35L56_TEMP_ERR_EINT1_MASK,
+                          0);
+
+       if (!cs35l56->reset_gpio) {
+               dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n");
+               cs35l56_system_reset(cs35l56);
+               if (cs35l56->sdw_peripheral) {
+                       /* Keep alive while we wait for re-enumeration */
+                       pm_runtime_get_noresume(cs35l56->dev);
+                       return 0;
+               }
+       }
+
+post_soft_reset:
+       if (cs35l56->soft_resetting) {
+               cs35l56->soft_resetting = false;
+
+               /* Done re-enumerating after one-time init so release the keep-alive */
+               if (cs35l56->sdw_peripheral && !cs35l56->init_done)
+                       pm_runtime_put_noidle(cs35l56->dev);
+
+               regcache_mark_dirty(cs35l56->regmap);
+               ret = cs35l56_wait_for_firmware_boot(cs35l56);
+               if (ret)
+                       return ret;
+
+               dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n");
+       }
+
+       /* Disable auto-hibernate so that runtime_pm has control */
+       ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+       if (ret)
+               return ret;
+
+       /* Populate soft registers in the regmap cache */
+       cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap);
+
+       /* Registers could be dirty after soft reset or SoundWire enumeration */
+       regcache_sync(cs35l56->regmap);
+
+       cs35l56->init_done = true;
+       complete(&cs35l56->init_completion);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE);
+
+void cs35l56_remove(struct cs35l56_private *cs35l56)
+{
+       cs35l56->init_done = false;
+
+       /*
+        * WAKE IRQs unmask if CS35L56 hibernates so free the handler to
+        * prevent it racing with remove().
+        */
+       if (cs35l56->irq)
+               devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56);
+
+       flush_workqueue(cs35l56->dsp_wq);
+       destroy_workqueue(cs35l56->dsp_wq);
+
+       pm_runtime_suspend(cs35l56->dev);
+       pm_runtime_disable(cs35l56->dev);
+
+       regcache_cache_only(cs35l56->regmap, true);
+
+       kfree(cs35l56->dsp.system_name);
+
+       gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+       regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE);
+
+const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = {
+       SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL)
+       SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume)
+       LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+       NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE);
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
new file mode 100644 (file)
index 0000000..1f78946
--- /dev/null
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS35L56_H
+#define CS35L56_H
+
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+#include "wm_adsp.h"
+
+#define CS35L56_SDW_GEN_INT_STAT_1     0xc0
+#define CS35L56_SDW_GEN_INT_MASK_1     0xc1
+#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0)
+
+#define CS35L56_SDW_INVALID_BUS_SCALE  0xf
+
+#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+                           | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS35L56_RATES (SNDRV_PCM_RATE_48000)
+
+struct sdw_slave;
+
+struct cs35l56_private {
+       struct wm_adsp dsp; /* must be first member */
+       struct work_struct dsp_work;
+       struct workqueue_struct *dsp_wq;
+       struct mutex irq_lock;
+       struct snd_soc_component *component;
+       struct device *dev;
+       struct regmap *regmap;
+       struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
+       int irq;
+       struct sdw_slave *sdw_peripheral;
+       u8 rev;
+       struct work_struct sdw_irq_work;
+       bool secured;
+       bool sdw_irq_no_unmask;
+       bool soft_resetting;
+       bool init_done;
+       bool sdw_attached;
+       bool fw_patched;
+       bool can_hibernate;
+       struct completion init_completion;
+       struct gpio_desc *reset_gpio;
+
+       u32 rx_mask;
+       u32 tx_mask;
+       u8 asp_slot_width;
+       u8 asp_slot_count;
+       bool tdm_mode;
+       bool sysclk_set;
+       u8 old_sdw_clock_scale;
+};
+
+extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+
+int cs35l56_runtime_suspend(struct device *dev);
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56);
+int cs35l56_system_suspend(struct device *dev);
+int cs35l56_system_suspend_late(struct device *dev);
+int cs35l56_system_suspend_no_irq(struct device *dev);
+int cs35l56_system_resume_no_irq(struct device *dev);
+int cs35l56_system_resume_early(struct device *dev);
+int cs35l56_system_resume(struct device *dev);
+irqreturn_t cs35l56_irq(int irq, void *data);
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq);
+int cs35l56_common_probe(struct cs35l56_private *cs35l56);
+int cs35l56_init(struct cs35l56_private *cs35l56);
+void cs35l56_remove(struct cs35l56_private *cs35l56);
+
+#endif /* ifndef CS35L56_H */
index 0e8a7cf0da50570b8b501533bda003f06bbb366b..4033be1c3bc1b63fb565a40022f25bfa174e1998 100644 (file)
@@ -17,7 +17,6 @@ static int cs4271_i2c_probe(struct i2c_client *client)
 
        config = cs4271_regmap_config;
        config.reg_bits = 8;
-       config.val_bits = 8;
 
        return cs4271_probe(&client->dev,
                            devm_regmap_init_i2c(client, &config));
index 7ef0a66b777893dd2eaeb9bf741f8863e4714052..4feb80436bd99b4c83e2084ebdd52bb1916c06dc 100644 (file)
@@ -17,7 +17,6 @@ static int cs4271_spi_probe(struct spi_device *spi)
 
        config = cs4271_regmap_config;
        config.reg_bits = 16;
-       config.val_bits = 8;
        config.read_flag_mask = 0x21;
        config.write_flag_mask = 0x20;
 
index 2021cf4426061ef7d8ced6f394c311dfcf1724b8..188b8b43c524f21fc9758877c459d8e149c8b7f9 100644 (file)
@@ -689,8 +689,8 @@ const struct regmap_config cs4271_regmap_config = {
 
        .reg_defaults = cs4271_reg_defaults,
        .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
-       .cache_type = REGCACHE_RBTREE,
-
+       .cache_type = REGCACHE_FLAT,
+       .val_bits = 8,
        .volatile_reg = cs4271_volatile_reg,
 };
 EXPORT_SYMBOL_GPL(cs4271_regmap_config);
index 7b539ee55499e8412d7c374abe3258207b69dab5..eeab07c850f9582e918815b44c2698fb5d3995f1 100644 (file)
@@ -152,9 +152,6 @@ static int cs42l42_sdw_port_prep(struct sdw_slave *slave,
 static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                          int direction)
 {
-       if (!sdw_stream)
-               return 0;
-
        snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
index 06c4214382e33a7fadef311c62b873bfeea5ce3a..a6538dab663977c32fa97dc99440b5602771886d 100644 (file)
@@ -1468,7 +1468,7 @@ error_core:
        return ret;
 }
 
-static int cs47l15_remove(struct platform_device *pdev)
+static void cs47l15_remove(struct platform_device *pdev)
 {
        struct cs47l15 *cs47l15 = platform_get_drvdata(pdev);
 
@@ -1482,8 +1482,6 @@ static int cs47l15_remove(struct platform_device *pdev)
        madera_free_irq(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
        madera_free_overheat(&cs47l15->core);
        madera_core_free(&cs47l15->core);
-
-       return 0;
 }
 
 static struct platform_driver cs47l15_codec_driver = {
@@ -1491,7 +1489,7 @@ static struct platform_driver cs47l15_codec_driver = {
                .name = "cs47l15-codec",
        },
        .probe = &cs47l15_probe,
-       .remove = &cs47l15_remove,
+       .remove_new = cs47l15_remove,
 };
 
 module_platform_driver(cs47l15_codec_driver);
index f9a2b865d71761cac4c31167b5734962b1f29e8c..a07b621d463ef2ddec4905d8a3e11d9e843fb9f4 100644 (file)
@@ -1319,7 +1319,7 @@ err_dsp_irq:
        return ret;
 }
 
-static int cs47l24_remove(struct platform_device *pdev)
+static void cs47l24_remove(struct platform_device *pdev)
 {
        struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
        struct arizona *arizona = cs47l24->core.arizona;
@@ -1333,8 +1333,6 @@ static int cs47l24_remove(struct platform_device *pdev)
 
        arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
        arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
-
-       return 0;
 }
 
 static struct platform_driver cs47l24_codec_driver = {
@@ -1342,7 +1340,7 @@ static struct platform_driver cs47l24_codec_driver = {
                .name = "cs47l24-codec",
        },
        .probe = cs47l24_probe,
-       .remove = cs47l24_remove,
+       .remove_new = cs47l24_remove,
 };
 
 module_platform_driver(cs47l24_codec_driver);
index c1032d6c9143f9664ea4a7aedf6a15c5c9ca5d1f..c05c80c16c84da3aaa6c7b5a2b1b17c73d6d29d2 100644 (file)
@@ -1744,7 +1744,7 @@ error_core:
        return ret;
 }
 
-static int cs47l35_remove(struct platform_device *pdev)
+static void cs47l35_remove(struct platform_device *pdev)
 {
        struct cs47l35 *cs47l35 = platform_get_drvdata(pdev);
        int i;
@@ -1758,8 +1758,6 @@ static int cs47l35_remove(struct platform_device *pdev)
        madera_free_irq(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
        madera_free_overheat(&cs47l35->core);
        madera_core_free(&cs47l35->core);
-
-       return 0;
 }
 
 static struct platform_driver cs47l35_codec_driver = {
@@ -1767,7 +1765,7 @@ static struct platform_driver cs47l35_codec_driver = {
                .name = "cs47l35-codec",
        },
        .probe = &cs47l35_probe,
-       .remove = &cs47l35_remove,
+       .remove_new = cs47l35_remove,
 };
 
 module_platform_driver(cs47l35_codec_driver);
index 215d8211aa59b77961534214db5494a8275436b9..dd7997a53e70157887f49418a0dc3497939ccc8b 100644 (file)
@@ -2695,7 +2695,7 @@ error_core:
        return ret;
 }
 
-static int cs47l85_remove(struct platform_device *pdev)
+static void cs47l85_remove(struct platform_device *pdev)
 {
        struct cs47l85 *cs47l85 = platform_get_drvdata(pdev);
        int i;
@@ -2709,8 +2709,6 @@ static int cs47l85_remove(struct platform_device *pdev)
        madera_free_irq(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
        madera_free_overheat(&cs47l85->core);
        madera_core_free(&cs47l85->core);
-
-       return 0;
 }
 
 static struct platform_driver cs47l85_codec_driver = {
@@ -2718,7 +2716,7 @@ static struct platform_driver cs47l85_codec_driver = {
                .name = "cs47l85-codec",
        },
        .probe = &cs47l85_probe,
-       .remove = &cs47l85_remove,
+       .remove_new = cs47l85_remove,
 };
 
 module_platform_driver(cs47l85_codec_driver);
index 1ad6526c78717e11e0ec3ff6fe8e42318ad96b73..cdd5e7e20b5d5f8f47eca9246f99df7b8f51cef5 100644 (file)
@@ -2618,7 +2618,7 @@ error_core:
        return ret;
 }
 
-static int cs47l90_remove(struct platform_device *pdev)
+static void cs47l90_remove(struct platform_device *pdev)
 {
        struct cs47l90 *cs47l90 = platform_get_drvdata(pdev);
        int i;
@@ -2633,8 +2633,6 @@ static int cs47l90_remove(struct platform_device *pdev)
        madera_set_irq_wake(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
        madera_free_irq(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
        madera_core_free(&cs47l90->core);
-
-       return 0;
 }
 
 static struct platform_driver cs47l90_codec_driver = {
@@ -2642,7 +2640,7 @@ static struct platform_driver cs47l90_codec_driver = {
                .name = "cs47l90-codec",
        },
        .probe = &cs47l90_probe,
-       .remove = &cs47l90_remove,
+       .remove_new = cs47l90_remove,
 };
 
 module_platform_driver(cs47l90_codec_driver);
index fe576d64e0893de4b4a581a20e44a2ccac6b45c6..bc4d311d47782c37aa7e0eb5b418be613328d6a3 100644 (file)
@@ -2068,7 +2068,7 @@ error_core:
        return ret;
 }
 
-static int cs47l92_remove(struct platform_device *pdev)
+static void cs47l92_remove(struct platform_device *pdev)
 {
        struct cs47l92 *cs47l92 = platform_get_drvdata(pdev);
 
@@ -2081,8 +2081,6 @@ static int cs47l92_remove(struct platform_device *pdev)
        madera_free_irq(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
 
        madera_core_free(&cs47l92->core);
-
-       return 0;
 }
 
 static struct platform_driver cs47l92_codec_driver = {
@@ -2090,7 +2088,7 @@ static struct platform_driver cs47l92_codec_driver = {
                .name = "cs47l92-codec",
        },
        .probe = &cs47l92_probe,
-       .remove = &cs47l92_remove,
+       .remove_new = cs47l92_remove,
 };
 
 module_platform_driver(cs47l92_codec_driver);
index 91372909d184b46597f304f2fe6f2e8c4f47d806..d9c28e701613a1f5941bd55dccb6d8c5c51db970 100644 (file)
@@ -2893,14 +2893,10 @@ static int da7218_probe(struct snd_soc_component *component)
        da7218_handle_pdata(component);
 
        /* Check if MCLK provided, if not the clock is NULL */
-       da7218->mclk = devm_clk_get(component->dev, "mclk");
+       da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
        if (IS_ERR(da7218->mclk)) {
-               if (PTR_ERR(da7218->mclk) != -ENOENT) {
-                       ret = PTR_ERR(da7218->mclk);
-                       goto err_disable_reg;
-               } else {
-                       da7218->mclk = NULL;
-               }
+               ret = PTR_ERR(da7218->mclk);
+               goto err_disable_reg;
        }
 
        /* Default PC to free-running */
index e3d398b8f54e4f0cd343e2bc56d5cf9295073eae..993a0d00bc48dd353cc82befa08f8b4a897fed3f 100644 (file)
@@ -342,36 +342,17 @@ static void da7219_aad_hptest_work(struct work_struct *work)
 static void da7219_aad_jack_det_work(struct work_struct *work)
 {
        struct da7219_aad_priv *da7219_aad =
-               container_of(work, struct da7219_aad_priv, jack_det_work);
+               container_of(work, struct da7219_aad_priv, jack_det_work.work);
        struct snd_soc_component *component = da7219_aad->component;
-       u8 srm_st;
 
-       mutex_lock(&da7219_aad->jack_det_mutex);
-
-       srm_st = snd_soc_component_read(component, DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
-       msleep(da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 4);
        /* Enable ground switch */
        snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
-
-       mutex_unlock(&da7219_aad->jack_det_mutex);
 }
 
-
 /*
  * IRQ
  */
 
-static irqreturn_t da7219_aad_pre_irq_thread(int irq, void *data)
-{
-
-       struct da7219_aad_priv *da7219_aad = data;
-
-       if (!da7219_aad->jack_inserted)
-               schedule_work(&da7219_aad->jack_det_work);
-
-       return IRQ_WAKE_THREAD;
-}
-
 static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
 {
        struct da7219_aad_priv *da7219_aad = data;
@@ -392,6 +373,18 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
        /* Read status register for jack insertion & type status */
        statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
 
+       if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) {
+               u8 srm_st;
+               int delay = 0;
+
+               srm_st = snd_soc_component_read(component,
+                                       DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
+               delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2);
+               queue_delayed_work(da7219_aad->aad_wq,
+                                                       &da7219_aad->jack_det_work,
+                                                       msecs_to_jiffies(delay));
+       }
+
        /* Clear events */
        regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
                          events, DA7219_AAD_IRQ_REG_MAX);
@@ -400,9 +393,6 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
                events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
                statusa);
 
-       if (!da7219_aad->jack_inserted)
-               cancel_work_sync(&da7219_aad->jack_det_work);
-
        if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
                /* Jack Insertion */
                if (events[DA7219_AAD_IRQ_REG_A] &
@@ -430,9 +420,9 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
                        if (statusa & DA7219_JACK_TYPE_STS_MASK) {
                                report |= SND_JACK_HEADSET;
                                mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
-                               schedule_work(&da7219_aad->btn_det_work);
+                               queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work);
                        } else {
-                               schedule_work(&da7219_aad->hptest_work);
+                               queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work);
                        }
                }
 
@@ -465,6 +455,7 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
                        da7219_aad->jack_inserted = false;
 
                        /* Cancel any pending work */
+                       cancel_delayed_work_sync(&da7219_aad->jack_det_work);
                        cancel_work_sync(&da7219_aad->btn_det_work);
                        cancel_work_sync(&da7219_aad->hptest_work);
 
@@ -964,13 +955,19 @@ int da7219_aad_init(struct snd_soc_component *component)
        snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
                            DA7219_BUTTON_CONFIG_MASK, 0);
 
+       da7219_aad_handle_gnd_switch_time(component);
+
+       da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad");
+       if (!da7219_aad->aad_wq) {
+               dev_err(component->dev, "Failed to create aad workqueue\n");
+               return -ENOMEM;
+       }
+
+       INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
        INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
        INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
-       INIT_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
-
-       mutex_init(&da7219_aad->jack_det_mutex);
 
-       ret = request_threaded_irq(da7219_aad->irq, da7219_aad_pre_irq_thread,
+       ret = request_threaded_irq(da7219_aad->irq, NULL,
                                   da7219_aad_irq_thread,
                                   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                   "da7219-aad", da7219_aad);
@@ -984,8 +981,6 @@ int da7219_aad_init(struct snd_soc_component *component)
        regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
                          &mask, DA7219_AAD_IRQ_REG_MAX);
 
-       da7219_aad_handle_gnd_switch_time(component);
-
        return 0;
 }
 
@@ -1002,8 +997,10 @@ void da7219_aad_exit(struct snd_soc_component *component)
 
        free_irq(da7219_aad->irq, da7219_aad);
 
+       cancel_delayed_work_sync(&da7219_aad->jack_det_work);
        cancel_work_sync(&da7219_aad->btn_det_work);
        cancel_work_sync(&da7219_aad->hptest_work);
+       destroy_workqueue(da7219_aad->aad_wq);
 }
 
 /*
@@ -1031,4 +1028,5 @@ int da7219_aad_probe(struct i2c_client *i2c)
 
 MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
 MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
 MODULE_LICENSE("GPL");
index be87ee47edde13953250c8cc4d7d22c474a425e2..fbfbf3e67918de5a2bec48de068063569a3cdb7d 100644 (file)
@@ -197,9 +197,8 @@ struct da7219_aad_priv {
 
        struct work_struct btn_det_work;
        struct work_struct hptest_work;
-       struct work_struct jack_det_work;
-
-       struct mutex  jack_det_mutex;
+       struct delayed_work jack_det_work;
+       struct workqueue_struct *aad_wq;
 
        struct snd_soc_jack *jack;
        bool micbias_resume_enable;
index 056c3082fe02caedb944a70f0f74d564cc760dbe..a27d809564593aaffefe4e9647065a248657ae90 100644 (file)
@@ -803,14 +803,15 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
        .endianness             = 1,
 };
 
-static const struct regmap_range es8316_volatile_ranges[] = {
-       regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG),
-};
-
-static const struct regmap_access_table es8316_volatile_table = {
-       .yes_ranges     = es8316_volatile_ranges,
-       .n_yes_ranges   = ARRAY_SIZE(es8316_volatile_ranges),
-};
+static bool es8316_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ES8316_GPIO_FLAG:
+               return true;
+       default:
+               return false;
+       }
+}
 
 static const struct regmap_config es8316_regmap = {
        .reg_bits = 8,
@@ -818,7 +819,7 @@ static const struct regmap_config es8316_regmap = {
        .use_single_read = true,
        .use_single_write = true,
        .max_register = 0x53,
-       .volatile_table = &es8316_volatile_table,
+       .volatile_reg = es8316_volatile_reg,
        .cache_type = REGCACHE_RBTREE,
 };
 
@@ -842,12 +843,14 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client)
        es8316->irq = i2c_client->irq;
        mutex_init(&es8316->lock);
 
-       ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
-                                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
-                                       "es8316", es8316);
-       if (ret) {
-               dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
-               es8316->irq = -ENXIO;
+       if (es8316->irq > 0) {
+               ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
+                                               IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                               "es8316", es8316);
+               if (ret) {
+                       dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
+                       es8316->irq = -ENXIO;
+               }
        }
 
        return devm_snd_soc_register_component(&i2c_client->dev,
index 8222cde6e3b90ec7771484b0311385120c2c9a63..11320423c69c85d73b2a63da4f36fc9f4f69a5cc 100644 (file)
@@ -457,13 +457,11 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int rk3036_codec_platform_remove(struct platform_device *pdev)
+static void rk3036_codec_platform_remove(struct platform_device *pdev)
 {
        struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
 
        clk_disable_unprepare(priv->pclk);
-
-       return 0;
 }
 
 static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
@@ -478,7 +476,7 @@ static struct platform_driver rk3036_codec_platform_driver = {
                .of_match_table = of_match_ptr(rk3036_codec_of_match),
        },
        .probe = rk3036_codec_platform_probe,
-       .remove = rk3036_codec_platform_remove,
+       .remove_new = rk3036_codec_platform_remove,
 };
 
 module_platform_driver(rk3036_codec_platform_driver);
index 1b9082d237c13989e7d5ecac96505f401a346151..f54baaad54d4b4c2c5b3e73bfc2c2d868df01cc3 100644 (file)
@@ -16,7 +16,7 @@ struct lpass_macro *lpass_macro_pds_init(struct device *dev)
        struct lpass_macro *l_pds;
        int ret;
 
-       if (!of_find_property(dev->of_node, "power-domains", NULL))
+       if (!of_property_present(dev->of_node, "power-domains"))
                return NULL;
 
        l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL);
index f2cbf9fe2c6ef96c759771ba2c6bd6ded71effb1..4eb886565ea3677634f9978df976ee712da10356 100644 (file)
@@ -6,6 +6,9 @@
 #ifndef __LPASS_MACRO_COMMON_H__
 #define __LPASS_MACRO_COMMON_H__
 
+/* NPL clock is expected */
+#define LPASS_MACRO_FLAG_HAS_NPL_CLOCK         BIT(0)
+
 struct lpass_macro {
        struct device *macro_pd;
        struct device *dcodec_pd;
index faba4237bd3da84050326b98de2f9682e79c5571..685ca95ef4a91495415314d10db94c3545f5362f 100644 (file)
@@ -3491,7 +3491,10 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx)
        struct clk_init_data init;
        int ret;
 
-       parent_clk_name = __clk_get_name(rx->npl);
+       if (rx->npl)
+               parent_clk_name = __clk_get_name(rx->npl);
+       else
+               parent_clk_name = __clk_get_name(rx->mclk);
 
        init.name = clk_name;
        init.ops = &swclk_gate_ops;
@@ -3521,10 +3524,13 @@ static const struct snd_soc_component_driver rx_macro_component_drv = {
 static int rx_macro_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       kernel_ulong_t flags;
        struct rx_macro *rx;
        void __iomem *base;
        int ret;
 
+       flags = (kernel_ulong_t)device_get_match_data(dev);
+
        rx = devm_kzalloc(dev, sizeof(*rx), GFP_KERNEL);
        if (!rx)
                return -ENOMEM;
@@ -3541,9 +3547,11 @@ static int rx_macro_probe(struct platform_device *pdev)
        if (IS_ERR(rx->mclk))
                return PTR_ERR(rx->mclk);
 
-       rx->npl = devm_clk_get(dev, "npl");
-       if (IS_ERR(rx->npl))
-               return PTR_ERR(rx->npl);
+       if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+               rx->npl = devm_clk_get(dev, "npl");
+               if (IS_ERR(rx->npl))
+                       return PTR_ERR(rx->npl);
+       }
 
        rx->fsgen = devm_clk_get(dev, "fsgen");
        if (IS_ERR(rx->fsgen))
@@ -3639,7 +3647,7 @@ err:
        return ret;
 }
 
-static int rx_macro_remove(struct platform_device *pdev)
+static void rx_macro_remove(struct platform_device *pdev)
 {
        struct rx_macro *rx = dev_get_drvdata(&pdev->dev);
 
@@ -3650,15 +3658,25 @@ static int rx_macro_remove(struct platform_device *pdev)
        clk_disable_unprepare(rx->dcodec);
 
        lpass_macro_pds_exit(rx->pds);
-
-       return 0;
 }
 
 static const struct of_device_id rx_macro_dt_match[] = {
-       { .compatible = "qcom,sc7280-lpass-rx-macro" },
-       { .compatible = "qcom,sm8250-lpass-rx-macro" },
-       { .compatible = "qcom,sm8450-lpass-rx-macro" },
-       { .compatible = "qcom,sc8280xp-lpass-rx-macro" },
+       {
+               .compatible = "qcom,sc7280-lpass-rx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+
+       }, {
+               .compatible = "qcom,sm8250-lpass-rx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8450-lpass-rx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8550-lpass-rx-macro",
+       }, {
+               .compatible = "qcom,sc8280xp-lpass-rx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
@@ -3723,7 +3741,7 @@ static struct platform_driver rx_macro_driver = {
                .pm = &rx_macro_pm_ops,
        },
        .probe = rx_macro_probe,
-       .remove = rx_macro_remove,
+       .remove_new = rx_macro_remove,
 };
 
 module_platform_driver(rx_macro_driver);
index 589c490a8c48798fd0d9365bcc54eb135eb63728..da6fcf7f0991aec10e8adf10ad34f09c93b9185a 100644 (file)
@@ -1915,7 +1915,10 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx)
        struct clk_init_data init;
        int ret;
 
-       parent_clk_name = __clk_get_name(tx->npl);
+       if (tx->npl)
+               parent_clk_name = __clk_get_name(tx->npl);
+       else
+               parent_clk_name = __clk_get_name(tx->mclk);
 
        init.name = clk_name;
        init.ops = &swclk_gate_ops;
@@ -1946,10 +1949,13 @@ 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;
@@ -1966,9 +1972,11 @@ static int tx_macro_probe(struct platform_device *pdev)
        if (IS_ERR(tx->mclk))
                return PTR_ERR(tx->mclk);
 
-       tx->npl = devm_clk_get(dev, "npl");
-       if (IS_ERR(tx->npl))
-               return PTR_ERR(tx->npl);
+       if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+               tx->npl = devm_clk_get(dev, "npl");
+               if (IS_ERR(tx->npl))
+                       return PTR_ERR(tx->npl);
+       }
 
        tx->fsgen = devm_clk_get(dev, "fsgen");
        if (IS_ERR(tx->fsgen))
@@ -2076,7 +2084,7 @@ err:
        return ret;
 }
 
-static int tx_macro_remove(struct platform_device *pdev)
+static void tx_macro_remove(struct platform_device *pdev)
 {
        struct tx_macro *tx = dev_get_drvdata(&pdev->dev);
 
@@ -2087,8 +2095,6 @@ static int tx_macro_remove(struct platform_device *pdev)
        clk_disable_unprepare(tx->fsgen);
 
        lpass_macro_pds_exit(tx->pds);
-
-       return 0;
 }
 
 static int __maybe_unused tx_macro_runtime_suspend(struct device *dev)
@@ -2145,10 +2151,21 @@ static const struct dev_pm_ops tx_macro_pm_ops = {
 };
 
 static const struct of_device_id tx_macro_dt_match[] = {
-       { .compatible = "qcom,sc7280-lpass-tx-macro" },
-       { .compatible = "qcom,sm8250-lpass-tx-macro" },
-       { .compatible = "qcom,sm8450-lpass-tx-macro" },
-       { .compatible = "qcom,sc8280xp-lpass-tx-macro" },
+       {
+               .compatible = "qcom,sc7280-lpass-tx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8250-lpass-tx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8450-lpass-tx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8550-lpass-tx-macro",
+       }, {
+               .compatible = "qcom,sc8280xp-lpass-tx-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
@@ -2160,7 +2177,7 @@ static struct platform_driver tx_macro_driver = {
                .pm = &tx_macro_pm_ops,
        },
        .probe = tx_macro_probe,
-       .remove = tx_macro_remove,
+       .remove_new = tx_macro_remove,
 };
 
 module_platform_driver(tx_macro_driver);
index fd62817d29a0a5768f4d6d4ba267565de16cb3e0..74724448da50bea65537fc52d0685725c8c627ad 100644 (file)
@@ -1575,7 +1575,7 @@ err:
        return ret;
 }
 
-static int va_macro_remove(struct platform_device *pdev)
+static void va_macro_remove(struct platform_device *pdev)
 {
        struct va_macro *va = dev_get_drvdata(&pdev->dev);
 
@@ -1584,8 +1584,6 @@ static int va_macro_remove(struct platform_device *pdev)
        clk_disable_unprepare(va->macro);
 
        lpass_macro_pds_exit(va->pds);
-
-       return 0;
 }
 
 static int __maybe_unused va_macro_runtime_suspend(struct device *dev)
@@ -1639,7 +1637,7 @@ static struct platform_driver va_macro_driver = {
                .pm = &va_macro_pm_ops,
        },
        .probe = va_macro_probe,
-       .remove = va_macro_remove,
+       .remove_new = va_macro_remove,
 };
 
 module_platform_driver(va_macro_driver);
index 3f6f1bdd4e030af22bc363c38933c3f60affc23e..8ba7dc89daaaa80bf8853101da0d930dac6de80e 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/of_platform.h>
 #include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
 #include "lpass-wsa-macro.h"
 
 #define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL      (0x0000)
@@ -2346,7 +2348,10 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
        struct clk_init_data init;
        int ret;
 
-       parent_clk_name = __clk_get_name(wsa->npl);
+       if (wsa->npl)
+               parent_clk_name = __clk_get_name(wsa->npl);
+       else
+               parent_clk_name = __clk_get_name(wsa->mclk);
 
        init.name = "mclk";
        of_property_read_string(dev_of_node(dev), "clock-output-names",
@@ -2379,9 +2384,12 @@ static int wsa_macro_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct wsa_macro *wsa;
+       kernel_ulong_t flags;
        void __iomem *base;
        int ret;
 
+       flags = (kernel_ulong_t)device_get_match_data(dev);
+
        wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL);
        if (!wsa)
                return -ENOMEM;
@@ -2398,9 +2406,11 @@ static int wsa_macro_probe(struct platform_device *pdev)
        if (IS_ERR(wsa->mclk))
                return PTR_ERR(wsa->mclk);
 
-       wsa->npl = devm_clk_get(dev, "npl");
-       if (IS_ERR(wsa->npl))
-               return PTR_ERR(wsa->npl);
+       if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+               wsa->npl = devm_clk_get(dev, "npl");
+               if (IS_ERR(wsa->npl))
+                       return PTR_ERR(wsa->npl);
+       }
 
        wsa->fsgen = devm_clk_get(dev, "fsgen");
        if (IS_ERR(wsa->fsgen))
@@ -2486,7 +2496,7 @@ err:
 
 }
 
-static int wsa_macro_remove(struct platform_device *pdev)
+static void wsa_macro_remove(struct platform_device *pdev)
 {
        struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
 
@@ -2495,8 +2505,6 @@ static int wsa_macro_remove(struct platform_device *pdev)
        clk_disable_unprepare(wsa->mclk);
        clk_disable_unprepare(wsa->npl);
        clk_disable_unprepare(wsa->fsgen);
-
-       return 0;
 }
 
 static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev)
@@ -2553,10 +2561,21 @@ static const struct dev_pm_ops wsa_macro_pm_ops = {
 };
 
 static const struct of_device_id wsa_macro_dt_match[] = {
-       {.compatible = "qcom,sc7280-lpass-wsa-macro"},
-       {.compatible = "qcom,sm8250-lpass-wsa-macro"},
-       {.compatible = "qcom,sm8450-lpass-wsa-macro"},
-       {.compatible = "qcom,sc8280xp-lpass-wsa-macro" },
+       {
+               .compatible = "qcom,sc7280-lpass-wsa-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8250-lpass-wsa-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8450-lpass-wsa-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       }, {
+               .compatible = "qcom,sm8550-lpass-wsa-macro",
+       }, {
+               .compatible = "qcom,sc8280xp-lpass-wsa-macro",
+               .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, wsa_macro_dt_match);
@@ -2568,7 +2587,7 @@ static struct platform_driver wsa_macro_driver = {
                .pm = &wsa_macro_pm_ops,
        },
        .probe = wsa_macro_probe,
-       .remove = wsa_macro_remove,
+       .remove_new = wsa_macro_remove,
 };
 
 module_platform_driver(wsa_macro_driver);
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644 (file)
index 0000000..dcce06b
--- /dev/null
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98363.h"
+
+static struct reg_default max98363_reg[] = {
+       {MAX98363_R2001_INTR_RAW, 0x0},
+       {MAX98363_R2003_INTR_STATE, 0x0},
+       {MAX98363_R2005_INTR_FALG, 0x0},
+       {MAX98363_R2007_INTR_EN, 0x0},
+       {MAX98363_R2009_INTR_CLR, 0x0},
+       {MAX98363_R2021_ERR_MON_CTRL, 0x0},
+       {MAX98363_R2022_SPK_MON_THRESH, 0x0},
+       {MAX98363_R2023_SPK_MON_DURATION, 0x0},
+       {MAX98363_R2030_TONE_GEN_CFG, 0x0},
+       {MAX98363_R203F_TONE_GEN_EN, 0x0},
+       {MAX98363_R2040_AMP_VOL, 0x0},
+       {MAX98363_R2041_AMP_GAIN, 0x5},
+       {MAX98363_R2042_DSP_CFG, 0x0},
+       {MAX98363_R21FF_REV_ID, 0x0},
+};
+
+static bool max98363_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98363_R2001_INTR_RAW:
+       case MAX98363_R2003_INTR_STATE:
+       case MAX98363_R2005_INTR_FALG:
+       case MAX98363_R2007_INTR_EN:
+       case MAX98363_R2009_INTR_CLR:
+       case MAX98363_R2021_ERR_MON_CTRL ... MAX98363_R2023_SPK_MON_DURATION:
+       case MAX98363_R2030_TONE_GEN_CFG:
+       case MAX98363_R203F_TONE_GEN_EN:
+       case MAX98363_R2040_AMP_VOL:
+       case MAX98363_R2041_AMP_GAIN:
+       case MAX98363_R2042_DSP_CFG:
+       case MAX98363_R21FF_REV_ID:
+               return true;
+       default:
+               return false;
+       }
+};
+
+static bool max98363_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98363_R2001_INTR_RAW:
+       case MAX98363_R2003_INTR_STATE:
+       case MAX98363_R2005_INTR_FALG:
+       case MAX98363_R2007_INTR_EN:
+       case MAX98363_R2009_INTR_CLR:
+       case MAX98363_R21FF_REV_ID:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config max98363_sdw_regmap = {
+       .reg_bits = 32,
+       .val_bits = 8,
+       .max_register = MAX98363_R21FF_REV_ID,
+       .reg_defaults  = max98363_reg,
+       .num_reg_defaults = ARRAY_SIZE(max98363_reg),
+       .readable_reg = max98363_readable_register,
+       .volatile_reg = max98363_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
+       .use_single_read = true,
+       .use_single_write = true,
+};
+
+static int max98363_suspend(struct device *dev)
+{
+       struct max98363_priv *max98363 = dev_get_drvdata(dev);
+
+       regcache_cache_only(max98363->regmap, true);
+       regcache_mark_dirty(max98363->regmap);
+
+       return 0;
+}
+
+#define MAX98363_PROBE_TIMEOUT 5000
+
+static int max98363_resume(struct device *dev)
+{
+       struct sdw_slave *slave = dev_to_sdw_dev(dev);
+       struct max98363_priv *max98363 = dev_get_drvdata(dev);
+       unsigned long time;
+
+       if (!max98363->first_hw_init)
+               return 0;
+
+       if (!slave->unattach_request)
+               goto regmap_sync;
+
+       time = wait_for_completion_timeout(&slave->initialization_complete,
+                                          msecs_to_jiffies(MAX98363_PROBE_TIMEOUT));
+       if (!time) {
+               dev_err(dev, "Initialization not complete, timed out\n");
+               return -ETIMEDOUT;
+       }
+
+regmap_sync:
+
+       slave->unattach_request = 0;
+       regcache_cache_only(max98363->regmap, false);
+       regcache_sync(max98363->regmap);
+
+       return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(max98363_pm, max98363_suspend, max98363_resume, NULL);
+
+static int max98363_read_prop(struct sdw_slave *slave)
+{
+       struct sdw_slave_prop *prop = &slave->prop;
+       int nval, i;
+       u32 bit;
+       unsigned long addr;
+       struct sdw_dpn_prop *dpn;
+
+       prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+       /* BITMAP: 00000010  Dataport 1 is active */
+       prop->sink_ports = BIT(1);
+       prop->paging_support = true;
+       prop->clk_stop_timeout = 20;
+       prop->simple_clk_stop_capable = true;
+       prop->clock_reg_supported = true;
+
+       nval = hweight32(prop->sink_ports);
+       prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+                                          sizeof(*prop->sink_dpn_prop),
+                                          GFP_KERNEL);
+       if (!prop->sink_dpn_prop)
+               return -ENOMEM;
+
+       i = 0;
+       dpn = prop->sink_dpn_prop;
+       addr = prop->sink_ports;
+       for_each_set_bit(bit, &addr, 32) {
+               dpn[i].num = bit;
+               dpn[i].type = SDW_DPN_FULL;
+               dpn[i].simple_ch_prep_sm = true;
+               dpn[i].ch_prep_timeout = 10;
+               i++;
+       }
+
+       return 0;
+}
+
+static int max98363_io_init(struct sdw_slave *slave)
+{
+       struct device *dev = &slave->dev;
+       struct max98363_priv *max98363 = dev_get_drvdata(dev);
+       int ret, reg;
+
+       if (max98363->first_hw_init) {
+               regcache_cache_only(max98363->regmap, false);
+               regcache_cache_bypass(max98363->regmap, true);
+       }
+
+       /*
+        * PM runtime is only enabled when a Slave reports as Attached
+        */
+       if (!max98363->first_hw_init) {
+               /* set autosuspend parameters */
+               pm_runtime_set_autosuspend_delay(dev, 3000);
+               pm_runtime_use_autosuspend(dev);
+
+               /* update count of parent 'active' children */
+               pm_runtime_set_active(dev);
+
+               /* make sure the device does not suspend immediately */
+               pm_runtime_mark_last_busy(dev);
+
+               pm_runtime_enable(dev);
+       }
+
+       pm_runtime_get_noresume(dev);
+
+       ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, &reg);
+       if (!ret) {
+               dev_info(dev, "Revision ID: %X\n", reg);
+               return ret;
+       }
+
+       if (max98363->first_hw_init) {
+               regcache_cache_bypass(max98363->regmap, false);
+               regcache_mark_dirty(max98363->regmap);
+       }
+
+       max98363->first_hw_init = true;
+       max98363->hw_init = true;
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return 0;
+}
+
+#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000
+#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params,
+                                     struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct max98363_priv *max98363 =
+               snd_soc_component_get_drvdata(component);
+
+       struct sdw_stream_config stream_config;
+       struct sdw_port_config port_config;
+       enum sdw_data_direction direction;
+       struct sdw_stream_runtime *stream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       int ret;
+
+       stream = snd_soc_dai_get_dma_data(dai, substream);
+
+       if (!stream)
+               return -EINVAL;
+
+       if (!max98363->slave)
+               return -EINVAL;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -EINVAL;
+
+       direction = SDW_DATA_DIR_RX;
+       port_config.num = 1;
+
+       stream_config.frame_rate = params_rate(params);
+       stream_config.bps = snd_pcm_format_width(params_format(params));
+       stream_config.direction = direction;
+       stream_config.ch_count = params_channels(params);
+
+       if (stream_config.ch_count > runtime->hw.channels_max) {
+               stream_config.ch_count = runtime->hw.channels_max;
+               dev_info(dai->dev, "Number of channels: %d (requested: %d)\n",
+                        stream_config.ch_count, params_channels(params));
+       }
+       port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+
+       ret = sdw_stream_add_slave(max98363->slave, &stream_config,
+                                  &port_config, 1, stream);
+       if (ret) {
+               dev_err(dai->dev, "Unable to configure port\n");
+               return ret;
+       }
+
+       dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+       return 0;
+}
+
+static int max98363_pcm_hw_free(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct max98363_priv *max98363 =
+               snd_soc_component_get_drvdata(component);
+       struct sdw_stream_runtime *stream =
+               snd_soc_dai_get_dma_data(dai, substream);
+
+       if (!max98363->slave)
+               return -EINVAL;
+
+       sdw_stream_remove_slave(max98363->slave, stream);
+
+       return 0;
+}
+
+static int max98363_set_sdw_stream(struct snd_soc_dai *dai,
+                                  void *sdw_stream, int direction)
+{
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops max98363_dai_sdw_ops = {
+       .hw_params = max98363_sdw_dai_hw_params,
+       .hw_free = max98363_pcm_hw_free,
+       .set_stream = max98363_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver max98363_dai[] = {
+       {
+               .name = "max98363-aif1",
+               .playback = {
+                       .stream_name = "HiFi Playback",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = MAX98363_RATES,
+                       .formats = MAX98363_FORMATS,
+               },
+               .ops = &max98363_dai_sdw_ops,
+       }
+};
+
+static int max98363_update_status(struct sdw_slave *slave,
+                                 enum sdw_slave_status status)
+{
+       struct max98363_priv *max98363 = dev_get_drvdata(&slave->dev);
+
+       if (status == SDW_SLAVE_UNATTACHED)
+               max98363->hw_init = false;
+
+       /*
+        * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+        */
+       if (max98363->hw_init || status != SDW_SLAVE_ATTACHED)
+               return 0;
+
+       /* perform I/O transfers required for Slave initialization */
+       return max98363_io_init(slave);
+}
+
+static struct sdw_slave_ops max98363_slave_ops = {
+       .read_prop = max98363_read_prop,
+       .update_status = max98363_update_status,
+};
+
+static DECLARE_TLV_DB_SCALE(max98363_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98363_spk_tlv,
+       0, 5, TLV_DB_SCALE_ITEM(-300, 300, 0),
+);
+
+static const char * const max98363_tone_cfg_text[] = {
+       "Reserved", "0", "+FS/2", "-FS/2", "1KHz",
+       "12KHz", "8KHz", "6KHz", "4KHz", "3KHz",
+       "2KHz", "1.5KHz", "Reserved", "500Hz", "250Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_tone_cfg_enum,
+                           MAX98363_R2030_TONE_GEN_CFG, 0,
+                           max98363_tone_cfg_text);
+
+static const char * const max98363_spkmon_duration_text[] = {
+       "8ms", "20ms", "40ms", "60ms",
+       "80ms", "160ms", "240ms", "320ms",
+       "400ms", "480ms", "560ms", "640ms",
+       "720ms", "800ms", "880ms", "960ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_spkmon_duration_enum,
+                           MAX98363_R2023_SPK_MON_DURATION, 0,
+                           max98363_spkmon_duration_text);
+
+static const struct snd_kcontrol_new max98363_snd_controls[] = {
+       SOC_SINGLE_TLV("Digital Volume", MAX98363_R2040_AMP_VOL,
+                      0, 0x7F, 1, max98363_digital_tlv),
+       SOC_SINGLE_TLV("Speaker Volume", MAX98363_R2041_AMP_GAIN,
+                      0, 10, 0, max98363_spk_tlv),
+       SOC_SINGLE("Tone Generator Switch", MAX98363_R203F_TONE_GEN_EN,
+                  0, 1, 0),
+       SOC_ENUM("Tone Config", max98363_tone_cfg_enum),
+       SOC_SINGLE("Ramp Switch", MAX98363_R2042_DSP_CFG,
+                  MAX98363_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+       SOC_SINGLE("CLK Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+                  MAX98363_CLOCK_MON_SHIFT, 1, 0),
+       SOC_SINGLE("SPKMON Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+                  MAX98363_SPKMON_SHIFT, 1, 0),
+       SOC_SINGLE("SPKMON Thresh", MAX98363_R2022_SPK_MON_THRESH, 0, 0xFF, 0),
+       SOC_ENUM("SPKMON Duration", max98363_spkmon_duration_enum),
+};
+
+static const struct snd_soc_dapm_widget max98363_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98363_audio_map[] = {
+       /* Plabyack */
+       {"BE_OUT", NULL, "AIFIN"},
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98363 = {
+       .controls               = max98363_snd_controls,
+       .num_controls           = ARRAY_SIZE(max98363_snd_controls),
+       .dapm_widgets           = max98363_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(max98363_dapm_widgets),
+       .dapm_routes            = max98363_audio_map,
+       .num_dapm_routes        = ARRAY_SIZE(max98363_audio_map),
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+};
+
+static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+       struct max98363_priv *max98363;
+       int ret;
+       struct device *dev = &slave->dev;
+
+       /*  Allocate and assign private driver data structure  */
+       max98363 = devm_kzalloc(dev, sizeof(*max98363), GFP_KERNEL);
+       if (!max98363)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, max98363);
+       max98363->regmap = regmap;
+       max98363->slave = slave;
+
+       max98363->hw_init = false;
+       max98363->first_hw_init = false;
+
+       /* codec registration  */
+       ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363,
+                                             max98363_dai,
+                                             ARRAY_SIZE(max98363_dai));
+       if (ret < 0)
+               dev_err(dev, "Failed to register codec: %d\n", ret);
+
+       return ret;
+}
+
+static int max98363_sdw_probe(struct sdw_slave *slave,
+                             const struct sdw_device_id *id)
+{
+       struct regmap *regmap;
+
+       /* Regmap Initialization */
+       regmap = devm_regmap_init_sdw(slave, &max98363_sdw_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return max98363_init(slave, regmap);
+}
+
+static const struct sdw_device_id max98363_id[] = {
+       SDW_SLAVE_ENTRY(0x019F, 0x8363, 0),
+       {},
+};
+MODULE_DEVICE_TABLE(sdw, max98363_id);
+
+static struct sdw_driver max98363_sdw_driver = {
+       .driver = {
+               .name = "max98363",
+               .pm = pm_ptr(&max98363_pm),
+       },
+       .probe = max98363_sdw_probe,
+       .ops = &max98363_slave_ops,
+       .id_table = max98363_id,
+};
+
+module_sdw_driver(max98363_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98363 driver SDW");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h
new file mode 100644 (file)
index 0000000..2b6743d
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Analog Devices Inc. */
+
+#ifndef _MAX98363_H
+#define _MAX98363_H
+
+#define MAX98363_R2000_SW_RESET                        0x2000
+#define MAX98363_R2001_INTR_RAW                        0x2001
+#define MAX98363_R2003_INTR_STATE              0x2003
+#define MAX98363_R2005_INTR_FALG               0x2005
+#define MAX98363_R2007_INTR_EN                 0x2007
+#define MAX98363_R2009_INTR_CLR                        0x2009
+#define MAX98363_R2021_ERR_MON_CTRL            0x2021
+#define MAX98363_R2022_SPK_MON_THRESH          0x2022
+#define MAX98363_R2023_SPK_MON_DURATION                0x2023
+#define MAX98363_R2030_TONE_GEN_CFG            0x2030
+#define MAX98363_R203F_TONE_GEN_EN             0x203F
+#define MAX98363_R2040_AMP_VOL                 0x2040
+#define MAX98363_R2041_AMP_GAIN                        0x2041
+#define MAX98363_R2042_DSP_CFG                 0x2042
+#define MAX98363_R21FF_REV_ID                  0x21FF
+
+/* MAX98363_R2021_ERR_MON_CTRL */
+#define MAX98363_SPKMON_SHIFT                  (3)
+#define MAX98363_CLOCK_MON_SHIFT               (0)
+
+/* MAX98363_R2042_DSP_CFG */
+#define MAX98363_AMP_DSP_CFG_RMP_SHIFT         (3)
+
+struct max98363_priv {
+       struct regmap *regmap;
+       struct sdw_slave *slave;
+       bool hw_init;
+       bool first_hw_init;
+};
+#endif
index c9a2d4dabd3c4ef68f9c50b385be295a18e17b6c..df92242af960ee65af7e952bc0d68b8c76cb2981 100644 (file)
 #include "max98373.h"
 #include "max98373-sdw.h"
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 static const u32 max98373_sdw_cache_reg[] = {
        MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
        MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
@@ -536,12 +532,12 @@ static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
                snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int ret, chan_sz, sampling_rate;
 
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!max98373->slave)
@@ -565,7 +561,7 @@ static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
        }
 
        ret = sdw_stream_add_slave(max98373->slave, &stream_config,
-                                  &port_config, 1, stream->sdw_stream);
+                                  &port_config, 1, sdw_stream);
        if (ret) {
                dev_err(dai->dev, "Unable to configure port\n");
                return ret;
@@ -664,32 +660,20 @@ static int max98373_pcm_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component = dai->component;
        struct max98373_priv *max98373 =
                snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!max98373->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(max98373->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(max98373->slave, sdw_stream);
        return 0;
 }
 
 static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
                                   void *sdw_stream, int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -697,11 +681,7 @@ static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
 static void max98373_shutdown(struct snd_pcm_substream *substream,
                              struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai,
index e161ab037bf7c4cdf8465782ece480eb1244b682..ae552d72beecbcf4f1c7fc3a5dc511e86ddbfeba 100644 (file)
@@ -6,6 +6,7 @@
 // Copyright 2018 Ladislav Michl <ladis@linux-mips.org>
 //
 
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
@@ -16,6 +17,7 @@
 #include "max9867.h"
 
 struct max9867_priv {
+       struct clk *mclk;
        struct regmap *regmap;
        const struct snd_pcm_hw_constraint_list *constraints;
        unsigned int sysclk, pclk;
@@ -577,6 +579,11 @@ static int max9867_set_bias_level(struct snd_soc_component *component,
        struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
 
        switch (level) {
+       case SND_SOC_BIAS_ON:
+               err = clk_prepare_enable(max9867->mclk);
+               if (err)
+                       return err;
+               break;
        case SND_SOC_BIAS_STANDBY:
                if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
                        err = regcache_sync(max9867->regmap);
@@ -595,6 +602,7 @@ static int max9867_set_bias_level(struct snd_soc_component *component,
                        return err;
 
                regcache_mark_dirty(max9867->regmap);
+               clk_disable_unprepare(max9867->mclk);
                break;
        default:
                break;
@@ -663,9 +671,16 @@ static int max9867_i2c_probe(struct i2c_client *i2c)
        dev_info(&i2c->dev, "device revision: %x\n", reg);
        ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component,
                        max9867_dai, ARRAY_SIZE(max9867_dai));
-       if (ret < 0)
+       if (ret < 0) {
                dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
-       return ret;
+               return ret;
+       }
+
+       max9867->mclk = devm_clk_get(&i2c->dev, NULL);
+       if (IS_ERR(max9867->mclk))
+               return PTR_ERR(max9867->mclk);
+
+       return 0;
 }
 
 static const struct i2c_device_id max9867_i2c_id[] = {
index 78e543eb3c8341b90b4fb3f888eed07f08f9003b..cec90cf920ff75713202b9f113aab83ba6883a6e 100644 (file)
@@ -1276,13 +1276,11 @@ err_disable_clk:
        return ret;
 }
 
-static int pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
+static void pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
 {
        struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev);
 
        clk_disable_unprepare(priv->mclk);
-
-       return 0;
 }
 
 static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
@@ -1298,7 +1296,7 @@ static struct platform_driver pm8916_wcd_analog_spmi_driver = {
                   .of_match_table = pm8916_wcd_analog_spmi_match_table,
        },
        .probe = pm8916_wcd_analog_spmi_probe,
-       .remove = pm8916_wcd_analog_spmi_remove,
+       .remove_new = pm8916_wcd_analog_spmi_remove,
 };
 
 module_platform_driver(pm8916_wcd_analog_spmi_driver);
index d490a0f1867521fd62ed64e20b922c805f1b6c02..978c4d056e81052377cf81d2145e7cc917234a66 100644 (file)
@@ -1220,14 +1220,12 @@ err_clk:
        return ret;
 }
 
-static int msm8916_wcd_digital_remove(struct platform_device *pdev)
+static void msm8916_wcd_digital_remove(struct platform_device *pdev)
 {
        struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev);
 
        clk_disable_unprepare(priv->mclk);
        clk_disable_unprepare(priv->ahbclk);
-
-       return 0;
 }
 
 static const struct of_device_id msm8916_wcd_digital_match_table[] = {
@@ -1243,7 +1241,7 @@ static struct platform_driver msm8916_wcd_digital_driver = {
                   .of_match_table = msm8916_wcd_digital_match_table,
        },
        .probe = msm8916_wcd_digital_probe,
-       .remove = msm8916_wcd_digital_remove,
+       .remove_new = msm8916_wcd_digital_remove,
 };
 
 module_platform_driver(msm8916_wcd_digital_driver);
index b54610b27906437b0ede084710fb6fc1d5007600..d7b157ddc9a8ba9c7825cc61dab7cfe2dfdff47a 100644 (file)
@@ -429,7 +429,7 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
        struct mt6358_priv *priv = snd_soc_component_get_drvdata(component);
        struct soc_mixer_control *mc =
                        (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg;
+       unsigned int reg = 0;
        int ret;
 
        ret = snd_soc_put_volsw(kcontrol, ucontrol);
index c9a453ce8a2a888ef229bd36964a51cccd4e2a7a..cb487e63615c82ca93e6f5285aea3bfab4926236 100644 (file)
@@ -358,7 +358,7 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
        struct mt6359_priv *priv = snd_soc_component_get_drvdata(component);
        struct soc_mixer_control *mc =
                        (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg;
+       unsigned int reg = 0;
        int index = ucontrol->value.integer.value[0];
        int ret;
 
index 4a72b94e8410424023fdef82365e413d0dc256f7..fee970427a2436c057003395fef0437761ac8a07 100644 (file)
@@ -322,12 +322,92 @@ static const struct soc_enum nau8821_dac_oversampl_enum =
        SOC_ENUM_SINGLE(NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_SFT,
                ARRAY_SIZE(nau8821_dac_oversampl), nau8821_dac_oversampl);
 
+static const char * const nau8821_adc_drc_noise_gate[] = {
+       "1:1", "2:1", "4:1", "8:1" };
+
+static const struct soc_enum nau8821_adc_drc_noise_gate_enum =
+       SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_NG_SLP_ADC_SFT,
+               ARRAY_SIZE(nau8821_adc_drc_noise_gate),
+               nau8821_adc_drc_noise_gate);
+
+static const char * const nau8821_adc_drc_expansion_slope[] = {
+       "1:1", "2:1", "4:1" };
+
+static const struct soc_enum nau8821_adc_drc_expansion_slope_enum =
+       SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_EXP_SLP_ADC_SFT,
+               ARRAY_SIZE(nau8821_adc_drc_expansion_slope),
+               nau8821_adc_drc_expansion_slope);
+
+static const char * const nau8821_adc_drc_lower_region[] = {
+       "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_adc_drc_lower_region_enum =
+       SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+               NAU8821_DRC_CMP2_SLP_ADC_SFT,
+               ARRAY_SIZE(nau8821_adc_drc_lower_region),
+               nau8821_adc_drc_lower_region);
+
+static const char * const nau8821_higher_region[] = {
+       "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_higher_region_enum =
+       SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+               NAU8821_DRC_CMP1_SLP_ADC_SFT,
+               ARRAY_SIZE(nau8821_higher_region),
+               nau8821_higher_region);
+
+static const char * const nau8821_limiter_slope[] = {
+       "0", "1:2", "1:4", "1:8", "1:16", "1:32", "1:64", "1:1" };
+
+static const struct soc_enum nau8821_limiter_slope_enum =
+       SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+               NAU8821_DRC_LMT_SLP_ADC_SFT, ARRAY_SIZE(nau8821_limiter_slope),
+               nau8821_limiter_slope);
+
+static const char * const nau8821_detection_attack_time[] = {
+       "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+       "", "511Ts" };
+
+static const struct soc_enum nau8821_detection_attack_time_enum =
+       SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+               NAU8821_DRC_PK_COEF1_ADC_SFT,
+               ARRAY_SIZE(nau8821_detection_attack_time),
+               nau8821_detection_attack_time);
+
+static const char * const nau8821_detection_release_time[] = {
+       "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+       "8191Ts", "", "16383Ts" };
+
+static const struct soc_enum nau8821_detection_release_time_enum =
+       SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+               NAU8821_DRC_PK_COEF2_ADC_SFT,
+               ARRAY_SIZE(nau8821_detection_release_time),
+               nau8821_detection_release_time);
+
+static const char * const nau8821_attack_time[] = {
+       "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+       "511Ts", "1023Ts", "2047Ts", "4095Ts", "8191Ts" };
+
+static const struct soc_enum nau8821_attack_time_enum =
+       SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_ATK_ADC_SFT,
+               ARRAY_SIZE(nau8821_attack_time), nau8821_attack_time);
+
+static const char * const nau8821_decay_time[] = {
+       "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+       "8191Ts", "16383Ts", "32757Ts", "65535Ts" };
+
+static const struct soc_enum nau8821_decay_time_enum =
+       SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_DCY_ADC_SFT,
+               ARRAY_SIZE(nau8821_decay_time), nau8821_decay_time);
+
 static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -6600, 2400);
 static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
 static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -900, 0);
 static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -6600, 50, 1);
 static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
 static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -7000, 2400);
+static const DECLARE_TLV_DB_MINMAX(drc_knee4_tlv, -9800, -3500);
+static const DECLARE_TLV_DB_MINMAX(drc_knee3_tlv, -8100, -1800);
 
 static const struct snd_kcontrol_new nau8821_controls[] = {
        SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1,
@@ -346,6 +426,22 @@ static const struct snd_kcontrol_new nau8821_controls[] = {
        SOC_DOUBLE_TLV("Headphone Crosstalk Volume",
                NAU8821_R2F_DAC_DGAIN_CTRL,
                0, 8, 0xff, 0, crosstalk_vol_tlv),
+       SOC_SINGLE_TLV("ADC DRC KNEE4", NAU8821_R37_ADC_DRC_KNEE_IP34,
+               NAU8821_DRC_KNEE4_IP_ADC_SFT, 0x3f, 1, drc_knee4_tlv),
+       SOC_SINGLE_TLV("ADC DRC KNEE3", NAU8821_R37_ADC_DRC_KNEE_IP34,
+               NAU8821_DRC_KNEE3_IP_ADC_SFT, 0x3f, 1, drc_knee3_tlv),
+
+       SOC_ENUM("ADC DRC Noise Gate", nau8821_adc_drc_noise_gate_enum),
+       SOC_ENUM("ADC DRC Expansion Slope", nau8821_adc_drc_expansion_slope_enum),
+       SOC_ENUM("ADC DRC Lower Region", nau8821_adc_drc_lower_region_enum),
+       SOC_ENUM("ADC DRC Higher Region", nau8821_higher_region_enum),
+       SOC_ENUM("ADC DRC Limiter Slope", nau8821_limiter_slope_enum),
+       SOC_ENUM("ADC DRC Peak Detection Attack Time", nau8821_detection_attack_time_enum),
+       SOC_ENUM("ADC DRC Peak Detection Release Time", nau8821_detection_release_time_enum),
+       SOC_ENUM("ADC DRC Attack Time", nau8821_attack_time_enum),
+       SOC_ENUM("ADC DRC Decay Time", nau8821_decay_time_enum),
+       SOC_SINGLE("DRC Enable Switch", NAU8821_R36_ADC_DRC_KNEE_IP12,
+               NAU8821_DRC_ENA_ADC_SFT, 1, 0),
 
        SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum),
        SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum),
index c44251f54d48134f107736128b68bb9201d37ac7..d962293c218ebd9e4bb0f1afdb0ad4757ca8428c 100644 (file)
 #define NAU8821_ADCL_CH_VOL_SFT                0
 #define NAU8821_ADCL_CH_VOL_MASK       0xff
 
+/* ADC_DRC_KNEE_IP12 (0x36) */
+#define NAU8821_DRC_ENA_ADC_SFT                15
+#define NAU8821_DRC_ENA_ADC_EN         (0x1 << NAU8821_DRC_ENA_ADC_SFT)
+
+/* ADC_DRC_KNEE_IP34 (0x37) */
+#define NAU8821_DRC_KNEE4_IP_ADC_SFT   8
+#define NAU8821_DRC_KNEE4_IP_ADC_MASK  (0xff << NAU8821_DRC_KNEE4_IP_ADC_SFT)
+#define NAU8821_DRC_KNEE3_IP_ADC_SFT   0
+#define NAU8821_DRC_KNEE3_IP_ADC_MASK  0xff
+
+/* ADC_DRC_SLOPES (0x38) */
+#define NAU8821_DRC_NG_SLP_ADC_SFT     12
+#define NAU8821_DRC_EXP_SLP_ADC_SFT    9
+#define NAU8821_DRC_CMP2_SLP_ADC_SFT   6
+#define NAU8821_DRC_CMP1_SLP_ADC_SFT   3
+#define NAU8821_DRC_LMT_SLP_ADC_SFT    0
+
+/* ADC_DRC_ATKDCY (0x39) */
+#define NAU8821_DRC_PK_COEF1_ADC_SFT   12
+#define NAU8821_DRC_PK_COEF2_ADC_SFT   8
+#define NAU8821_DRC_ATK_ADC_SFT                4
+#define NAU8821_DRC_DCY_ADC_SFT                0
+
 /* BIQ1_COF10 (0x4a) */
 #define NAU8821_BIQ1_DAC_EN_SFT                3
 #define NAU8821_BIQ1_DAC_EN_EN          (0x1 << NAU8821_BIQ1_DAC_EN_SFT)
index 4bffa9c20f2bcfefc8cebfe826d4cfead1a922e4..f4eb999761a4bb08cddfe6ec27939234f7319ec0 100644 (file)
@@ -919,7 +919,7 @@ static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
-               msleep(125);
+               msleep(nau8825->adc_delay);
                regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
                        NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
                break;
@@ -2752,6 +2752,7 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
        dev_dbg(dev, "crosstalk-enable:     %d\n",
                        nau8825->xtalk_enable);
        dev_dbg(dev, "adcout-drive-strong:  %d\n", nau8825->adcout_ds);
+       dev_dbg(dev, "adc-delay-ms:         %d\n", nau8825->adc_delay);
 }
 
 static int nau8825_read_device_properties(struct device *dev,
@@ -2819,6 +2820,11 @@ static int nau8825_read_device_properties(struct device *dev,
        nau8825->xtalk_enable = device_property_read_bool(dev,
                "nuvoton,crosstalk-enable");
        nau8825->adcout_ds = device_property_read_bool(dev, "nuvoton,adcout-drive-strong");
+       ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms", &nau8825->adc_delay);
+       if (ret)
+               nau8825->adc_delay = 125;
+       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) {
index 0c3a446e0e1aed51aa1ad163b8031576d9b1e646..44b62bc3880f8c4abc7b308a65d1fbaaaca507eb 100644 (file)
@@ -517,6 +517,7 @@ struct nau8825 {
        int xtalk_enable;
        bool xtalk_baktab_initialized; /* True if initialized. */
        bool adcout_ds;
+       int adc_delay;
 };
 
 int nau8825_enable_jack_detect(struct snd_soc_component *component,
index ebf63ea90a1c4c7097f37383f0654b92e75fcc9e..192fee90c9714fce9bf7d512d87ca8e80065b15d 100644 (file)
@@ -29,7 +29,7 @@ static int pcm179x_spi_probe(struct spi_device *spi)
        return pcm179x_common_init(&spi->dev, regmap);
 }
 
-static const struct of_device_id pcm179x_of_match[] = {
+static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
        { .compatible = "ti,pcm1792a", },
        { }
 };
index 2a5b274bfc0f53febe21d34d24c884e6d989386c..d4da98469f8b898dab6585cf0321091857e0a908 100644 (file)
@@ -518,13 +518,11 @@ err_:
        return ret;
 }
 
-static int rk817_platform_remove(struct platform_device *pdev)
+static void rk817_platform_remove(struct platform_device *pdev)
 {
        struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev);
 
        clk_disable_unprepare(rk817->mclk);
-
-       return 0;
 }
 
 static struct platform_driver rk817_codec_driver = {
@@ -532,7 +530,7 @@ static struct platform_driver rk817_codec_driver = {
                   .name = "rk817-codec",
                   },
        .probe = rk817_platform_probe,
-       .remove = rk817_platform_remove,
+       .remove_new = rk817_platform_remove,
 };
 
 module_platform_driver(rk817_codec_driver);
index 49f527c61a7adcaf6935ffbadd400ece082d18fc..dff2596c81eb8eec5475ea160f581645483cea37 100644 (file)
@@ -546,7 +546,7 @@ static const struct i2c_device_id rt1019_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id);
 
-static const struct of_device_id rt1019_of_match[] = {
+static const struct of_device_id rt1019_of_match[] __maybe_unused = {
        { .compatible = "realtek,rt1019", },
        {},
 };
index 45544b530d3dcd9c7c79d698ecb39b3294650fbb..1797af824f60bfc9616f8dd601c29d1536aa9b33 100644 (file)
@@ -496,19 +496,7 @@ static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
 static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -516,11 +504,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai,
@@ -553,13 +537,13 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
                snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt1308->sdw_slave)
@@ -580,7 +564,7 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config,
-                               &port_config, 1, stream->sdw_stream);
+                               &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -595,13 +579,13 @@ static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component = dai->component;
        struct rt1308_sdw_priv *rt1308 =
                snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt1308->sdw_slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream);
        return 0;
 }
 
index 1eaaef9f351b296643772364c1bd739141487f1b..04ff18fa18e2b988bebf1fe5ac4e700a9b71d60f 100644 (file)
@@ -170,8 +170,4 @@ struct rt1308_sdw_priv {
        unsigned int bq_params_cnt;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 #endif /* __RT1308_SDW_H__ */
index b45121ee7533f3ee358f3636b98a4feb40828bed..2ee5e763e345c271bfe4b727ccb28a2180823451 100644 (file)
@@ -494,19 +494,7 @@ static const struct snd_soc_dapm_route rt1316_dapm_routes[] = {
 static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -514,11 +502,7 @@ static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -529,13 +513,13 @@ static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
                snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt1316->sdw_slave)
@@ -551,7 +535,7 @@ static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
                port_config.num = 2;
 
        retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config,
-                               &port_config, 1, stream->sdw_stream);
+                               &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -566,13 +550,13 @@ static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component = dai->component;
        struct rt1316_sdw_priv *rt1316 =
                snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt1316->sdw_slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt1316->sdw_slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt1316->sdw_slave, sdw_stream);
        return 0;
 }
 
index 57dbd49993b071e4b9fa28014ec2a95911b633d8..e37121655bc169102d9df5988b63374f2ad696fc 100644 (file)
@@ -50,8 +50,4 @@ struct rt1316_sdw_priv {
        unsigned int bq_params_cnt;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 #endif /* __RT1316_SDW_H__ */
index c32d8ae77981b03d125f352eb3dd7187a3a96c85..795accedc22ce96c66d568e504c05ef83d393031 100644 (file)
@@ -562,19 +562,7 @@ static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
 static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -582,11 +570,7 @@ static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt1318_sdw_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -598,14 +582,14 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
        struct sdw_stream_config stream_config;
        struct sdw_port_config port_config;
        enum sdw_data_direction direction;
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval, port, num_channels, ch_mask;
        unsigned int sampling_rate;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt1318->sdw_slave)
@@ -633,7 +617,7 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
        port_config.num = port;
 
        retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config,
-                               &port_config, 1, stream->sdw_stream);
+                               &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -679,13 +663,13 @@ static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component = dai->component;
        struct rt1318_sdw_priv *rt1318 =
                snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt1318->sdw_slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt1318->sdw_slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt1318->sdw_slave, sdw_stream);
        return 0;
 }
 
index 4d7ac9c4bd8de6171b0d1c7e25fd276e53c0c795..85918c184f16cd8f8df4a623c1a5190c63cbf9fd 100644 (file)
@@ -94,8 +94,4 @@ struct rt1318_sdw_priv {
        bool first_hw_init;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 #endif /* __RT1318_SDW_H__ */
index 5f80a5d59b65a2f1cef05cf3ff9b388d67f2cfe1..23f17f70d7e9bb0ddd9f5c4471472682c4c861b3 100644 (file)
@@ -88,26 +88,10 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = {
        .reg_write = rt5682_sdw_write,
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                 int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -115,11 +99,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt5682_sdw_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -130,14 +110,14 @@ static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
        struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
 
-       stream = snd_soc_dai_get_dma_data(dai, substream);
-       if (!stream)
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+       if (!sdw_stream)
                return -ENOMEM;
 
        if (!rt5682->slave)
@@ -152,7 +132,7 @@ static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
                port_config.num = 2;
 
        retval = sdw_stream_add_slave(rt5682->slave, &stream_config,
-                                     &port_config, 1, stream->sdw_stream);
+                                     &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -246,13 +226,13 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt5682->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt5682->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt5682->slave, sdw_stream);
        return 0;
 }
 
index 659ce26e9f3ba55df2c5e0ec99311e03732027ca..a04b9246256b4711cd09e8464e4dd19e7cd57768 100644 (file)
@@ -875,19 +875,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
 static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -895,11 +883,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt700_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -910,14 +894,14 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
        struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int val = 0;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt700->slave)
@@ -944,7 +928,7 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt700->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -991,13 +975,13 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt700->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt700->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt700->slave, sdw_stream);
        return 0;
 }
 
index bed9d1de6d5bada02d1448880ad5aaed0116e4a4..93c44005d38cc31e9f782fae961b04eb8b1e251a 100644 (file)
@@ -27,10 +27,6 @@ struct  rt700_priv {
        bool disable_irq;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 /* NID */
 #define RT700_AUDIO_FUNCTION_GROUP                     0x01
 #define RT700_DAC_OUT1                                 0x02
index c65abe812a4c7b3fb79f2e7bef186620ff78b92e..07640d2f6e560797bb300513309f022606f35bdb 100644 (file)
@@ -1237,19 +1237,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
 static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -1257,11 +1245,7 @@ static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt711_sdca_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -1272,14 +1256,14 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int sampling_rate;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt711->slave)
@@ -1300,7 +1284,7 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt711->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -1351,13 +1335,13 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt711->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt711->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt711->slave, sdw_stream);
        return 0;
 }
 
index 10e3c801b813306248a3f6ffed0f77299439f799..22076f268577c23eb7ce515c5d68fdcf27efdc50 100644 (file)
@@ -36,10 +36,6 @@ struct  rt711_sdca_priv {
        bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 /* NID */
 #define RT711_AUDIO_FUNCTION_GROUP                     0x01
 #define RT711_DAC_OUT2                                 0x03
index 862f509505652bdec0fd4f4ccb31c4fac95f51fc..af53cbcc7bf2dd8636937accdeebb514c7f9ddcb 100644 (file)
@@ -964,19 +964,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
 static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -984,11 +972,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt711_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -999,14 +983,14 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
        struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int val = 0;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt711->slave)
@@ -1027,7 +1011,7 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt711->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -1075,13 +1059,13 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt711->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt711->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt711->slave, sdw_stream);
        return 0;
 }
 
index f50f8c8d0934983de87b5b4fcf6aa23a3681d914..b31351f11df97995c47fdb0896746f98bcd5c618 100644 (file)
@@ -29,10 +29,6 @@ struct  rt711_priv {
        bool disable_irq;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 /* NID */
 #define RT711_AUDIO_FUNCTION_GROUP                     0x01
 #define RT711_DAC_OUT2                                 0x03
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
new file mode 100644 (file)
index 0000000..09807b6
--- /dev/null
@@ -0,0 +1,983 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-dmic.c -- rt712 SDCA DMIC ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-dmic.h"
+
+static bool rt712_sdca_dmic_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x201a ... 0x201f:
+       case 0x2029 ... 0x202a:
+       case 0x202d ... 0x2034:
+       case 0x2230 ... 0x2232:
+       case 0x2f01 ... 0x2f0a:
+       case 0x2f35 ... 0x2f36:
+       case 0x2f52:
+       case 0x2f58 ... 0x2f59:
+       case 0x3201:
+       case 0x320c:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt712_sdca_dmic_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x201b:
+       case 0x201c:
+       case 0x201d:
+       case 0x201f:
+       case 0x202d ... 0x202f:
+       case 0x2230:
+       case 0x2f01:
+       case 0x2f35:
+       case 0x320c:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt712_sdca_dmic_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x2000000 ... 0x200008e:
+       case 0x5300000 ... 0x530000e:
+       case 0x5400000 ... 0x540000e:
+       case 0x5600000 ... 0x5600008:
+       case 0x5700000 ... 0x570000d:
+       case 0x5800000 ... 0x5800021:
+       case 0x5900000 ... 0x5900028:
+       case 0x5a00000 ... 0x5a00009:
+       case 0x5b00000 ... 0x5b00051:
+       case 0x5c00000 ... 0x5c0009a:
+       case 0x5d00000 ... 0x5d00009:
+       case 0x5f00000 ... 0x5f00030:
+       case 0x6100000 ... 0x6100068:
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+       case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt712_sdca_dmic_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x2000000:
+       case 0x200001a:
+       case 0x2000024:
+       case 0x2000046:
+       case 0x200008a:
+       case 0x5800000:
+       case 0x5800001:
+       case 0x6100008:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config rt712_sdca_dmic_regmap = {
+       .reg_bits = 32,
+       .val_bits = 8,
+       .readable_reg = rt712_sdca_dmic_readable_register,
+       .volatile_reg = rt712_sdca_dmic_volatile_register,
+       .max_register = 0x40981300,
+       .reg_defaults = rt712_sdca_dmic_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
+       .use_single_read = true,
+       .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_dmic_mbq_regmap = {
+       .name = "sdw-mbq",
+       .reg_bits = 32,
+       .val_bits = 16,
+       .readable_reg = rt712_sdca_dmic_mbq_readable_register,
+       .volatile_reg = rt712_sdca_dmic_mbq_volatile_register,
+       .max_register = 0x40800f14,
+       .reg_defaults = rt712_sdca_dmic_mbq_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults),
+       .cache_type = REGCACHE_RBTREE,
+       .use_single_read = true,
+       .use_single_write = true,
+};
+
+static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712,
+               unsigned int nid, unsigned int reg, unsigned int value)
+{
+       int ret;
+       struct regmap *regmap = rt712->mbq_regmap;
+       unsigned int addr = (nid << 20) | reg;
+
+       ret = regmap_write(regmap, addr, value);
+       if (ret < 0)
+               dev_err(&rt712->slave->dev,
+                       "Failed to set private value: %06x <= %04x ret=%d\n",
+                       addr, value, ret);
+
+       return ret;
+}
+
+static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712,
+               unsigned int nid, unsigned int reg, unsigned int *value)
+{
+       int ret;
+       struct regmap *regmap = rt712->mbq_regmap;
+       unsigned int addr = (nid << 20) | reg;
+
+       ret = regmap_read(regmap, addr, value);
+       if (ret < 0)
+               dev_err(&rt712->slave->dev,
+                       "Failed to get private value: %06x => %04x ret=%d\n",
+                       addr, *value, ret);
+
+       return ret;
+}
+
+static int rt712_sdca_dmic_index_update_bits(struct rt712_sdca_dmic_priv *rt712,
+       unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+       unsigned int tmp;
+       int ret;
+
+       ret = rt712_sdca_dmic_index_read(rt712, nid, reg, &tmp);
+       if (ret < 0)
+               return ret;
+
+       set_mask_bits(&tmp, mask, val);
+       return rt712_sdca_dmic_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
+{
+       struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+       if (rt712->hw_init)
+               return 0;
+
+       if (rt712->first_hw_init) {
+               regcache_cache_only(rt712->regmap, false);
+               regcache_cache_bypass(rt712->regmap, true);
+               regcache_cache_only(rt712->mbq_regmap, false);
+               regcache_cache_bypass(rt712->mbq_regmap, true);
+       } else {
+               /*
+                * PM runtime is only enabled when a Slave reports as Attached
+                */
+
+               /* set autosuspend parameters */
+               pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+               pm_runtime_use_autosuspend(&slave->dev);
+
+               /* update count of parent 'active' children */
+               pm_runtime_set_active(&slave->dev);
+
+               /* make sure the device does not suspend immediately */
+               pm_runtime_mark_last_busy(&slave->dev);
+
+               pm_runtime_enable(&slave->dev);
+       }
+
+       pm_runtime_get_noresume(&slave->dev);
+
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_ADC0B_11_PDE_FLOAT_CTL, 0x1111);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_DMIC1_2_PDE_FLOAT_CTL, 0x1111);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_I2S_IN_OUT_PDE_FLOAT_CTL, 0x1155);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_DMIC_ENT_FLOAT_CTL, 0x2626);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_ADC_ENT_FLOAT_CTL, 0x1e19);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_ADC_VOL_CH_FLOAT_CTL2, 0x0304);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+       rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_HDA_LEGACY_CONFIG_CTL0, 0x0050);
+       regmap_write(rt712->regmap,
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+       rt712_sdca_dmic_index_write(rt712, RT712_ULTRA_SOUND_DET,
+               RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+       regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+       regmap_write(rt712->regmap, 0x2f52, 0x00);
+
+       if (rt712->first_hw_init) {
+               regcache_cache_bypass(rt712->regmap, false);
+               regcache_mark_dirty(rt712->regmap);
+               regcache_cache_bypass(rt712->mbq_regmap, false);
+               regcache_mark_dirty(rt712->mbq_regmap);
+       } else
+               rt712->first_hw_init = true;
+
+       /* Mark Slave initialization complete */
+       rt712->hw_init = true;
+
+       pm_runtime_mark_last_busy(&slave->dev);
+       pm_runtime_put_autosuspend(&slave->dev);
+
+       dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+       return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct rt712_sdca_dmic_kctrl_priv *p =
+               (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+       unsigned int regvalue, ctl, i;
+       unsigned int adc_vol_flag = 0;
+       const unsigned int interval_offset = 0xc0;
+
+       if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+               adc_vol_flag = 1;
+
+       /* check all channels */
+       for (i = 0; i < p->count; i++) {
+               regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+               if (!adc_vol_flag) /* boost gain */
+                       ctl = regvalue / 0x0a00;
+               else { /* ADC gain */
+                       if (adc_vol_flag)
+                               ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+                       else
+                               ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
+               }
+
+               ucontrol->value.integer.value[i] = ctl;
+       }
+
+       return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt712_sdca_dmic_kctrl_priv *p =
+               (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+       struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+       unsigned int gain_val[4];
+       unsigned int i, adc_vol_flag = 0, changed = 0;
+       unsigned int regvalue[4];
+       const unsigned int interval_offset = 0xc0;
+       int err;
+
+       if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+               adc_vol_flag = 1;
+
+       /* check all channels */
+       for (i = 0; i < p->count; i++) {
+               regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+               gain_val[i] = ucontrol->value.integer.value[i];
+               if (gain_val[i] > p->max)
+                       gain_val[i] = p->max;
+
+               if (!adc_vol_flag) /* boost gain */
+                       gain_val[i] = gain_val[i] * 0x0a00;
+               else { /* ADC gain */
+                       gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+                       gain_val[i] &= 0xffff;
+               }
+
+               if (regvalue[i] != gain_val[i])
+                       changed = 1;
+       }
+
+       if (!changed)
+               return 0;
+
+       for (i = 0; i < p->count; i++) {
+               err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+               if (err < 0)
+                       dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i);
+       }
+
+       return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_dmic_priv *rt712)
+{
+       int err, i;
+       unsigned int ch_mute;
+
+       for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+               ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+               err = regmap_write(rt712->regmap,
+                               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+                               RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct rt712_sdca_dmic_kctrl_priv *p =
+               (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+       unsigned int i;
+
+       for (i = 0; i < p->count; i++)
+               ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+       return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct rt712_sdca_dmic_kctrl_priv *p =
+               (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+       int err, changed = 0, i;
+
+       for (i = 0; i < p->count; i++) {
+               if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+                       changed = 1;
+               rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+       }
+
+       err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+       if (err < 0)
+               return err;
+
+       return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct rt712_sdca_dmic_kctrl_priv *p =
+               (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+       if (p->max == 1)
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = p->count;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = p->max;
+       return 0;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+       ((unsigned long)&(struct rt712_sdca_dmic_kctrl_priv) \
+               {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+               .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .info = rt712_sdca_fu_info, \
+       .get = rt712_sdca_dmic_fu1e_capture_get, \
+       .put = rt712_sdca_dmic_fu1e_capture_put, \
+       .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+        xhandler_put, xcount, xmax, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = rt712_sdca_fu_info, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+       RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+               1, 1, 4),
+       RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+               rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+       RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+               rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, mic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component =
+               snd_soc_dapm_kcontrol_component(kcontrol);
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       unsigned int val = 0, mask_sft;
+
+       if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+               mask_sft = 8;
+       else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+               mask_sft = 4;
+       else
+               return -EINVAL;
+
+       rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+       ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+       return 0;
+}
+
+static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component =
+               snd_soc_dapm_kcontrol_component(kcontrol);
+       struct snd_soc_dapm_context *dapm =
+               snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       unsigned int val, val2 = 0, change, mask_sft;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+
+       if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+               mask_sft = 8;
+       else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+               mask_sft = 4;
+       else
+               return -EINVAL;
+
+       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+       rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+               RT712_HDA_LEGACY_MUX_CTL0, &val2);
+       val2 = (0x7 << mask_sft) & val2;
+
+       if (val == val2)
+               change = 0;
+       else
+               change = 1;
+
+       if (change)
+               rt712_sdca_dmic_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+                       RT712_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft,
+                       val << mask_sft);
+
+       snd_soc_dapm_mux_update_power(dapm, kcontrol,
+               item[0], e, NULL);
+
+       return change;
+}
+
+static const char * const adc_mux_text[] = {
+       "DMIC1",
+       "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt712_adc25_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt712_adc26_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc25_mux =
+       SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt712_adc25_enum,
+                       rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc26_mux =
+       SOC_DAPM_ENUM_EXT("ADC 26 Mux", rt712_adc26_enum,
+                       rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               rt712->fu1e_dapm_mute = false;
+               rt712_sdca_set_fu1e_capture_ctl(rt712);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               rt712->fu1e_dapm_mute = true;
+               rt712_sdca_set_fu1e_capture_ctl(rt712);
+               break;
+       }
+       return 0;
+}
+
+static int rt712_sdca_dmic_pde11_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       unsigned char ps0 = 0x0, ps3 = 0x3;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_write(rt712->regmap,
+                       SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+                               RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+                               ps0);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_write(rt712->regmap,
+                       SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+                               RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+                               ps3);
+               break;
+       }
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("DMIC1"),
+       SND_SOC_DAPM_INPUT("DMIC2"),
+
+       SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+               rt712_sdca_dmic_pde11_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+               rt712_sdca_dmic_fu1e_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+               &rt712_sdca_dmic_adc25_mux),
+       SND_SOC_DAPM_MUX("ADC 26 Mux", SND_SOC_NOPM, 0, 0,
+               &rt712_sdca_dmic_adc26_mux),
+
+       SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+       {"DP2TX", NULL, "FU 1E"},
+
+       {"FU 1E", NULL, "PDE 11"},
+       {"FU 1E", NULL, "ADC 25 Mux"},
+       {"FU 1E", NULL, "ADC 26 Mux"},
+       {"ADC 25 Mux", "DMIC1", "DMIC1"},
+       {"ADC 25 Mux", "DMIC2", "DMIC2"},
+       {"ADC 26 Mux", "DMIC1", "DMIC1"},
+       {"ADC 26 Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       int ret;
+
+       rt712->component = component;
+
+       ret = pm_runtime_resume(component->dev);
+       if (ret < 0 && ret != -EACCES)
+               return ret;
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = {
+       .probe = rt712_sdca_dmic_probe,
+       .controls = rt712_sdca_dmic_snd_controls,
+       .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+       .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+       .dapm_routes = rt712_sdca_dmic_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+       .endianness = 1,
+};
+
+static int rt712_sdca_dmic_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+                               int direction)
+{
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+       return 0;
+}
+
+static void rt712_sdca_dmic_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct sdw_stream_config stream_config;
+       struct sdw_port_config port_config;
+       struct sdw_stream_runtime *sdw_stream;
+       int retval, num_channels;
+       unsigned int sampling_rate;
+
+       dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+       if (!sdw_stream)
+               return -EINVAL;
+
+       if (!rt712->slave)
+               return -EINVAL;
+
+       stream_config.frame_rate = params_rate(params);
+       stream_config.ch_count = params_channels(params);
+       stream_config.bps = snd_pcm_format_width(params_format(params));
+       stream_config.direction = SDW_DATA_DIR_TX;
+
+       num_channels = params_channels(params);
+       port_config.ch_mask = GENMASK(num_channels - 1, 0);
+       port_config.num = 2;
+
+       retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+                                       &port_config, 1, sdw_stream);
+       if (retval) {
+               dev_err(dai->dev, "Unable to configure port\n");
+               return retval;
+       }
+
+       if (params_channels(params) > 4) {
+               dev_err(component->dev, "Unsupported channels %d\n",
+                       params_channels(params));
+               return -EINVAL;
+       }
+
+       /* sampling rate configuration */
+       switch (params_rate(params)) {
+       case 16000:
+               sampling_rate = RT712_SDCA_RATE_16000HZ;
+               break;
+       case 32000:
+               sampling_rate = RT712_SDCA_RATE_32000HZ;
+               break;
+       case 44100:
+               sampling_rate = RT712_SDCA_RATE_44100HZ;
+               break;
+       case 48000:
+               sampling_rate = RT712_SDCA_RATE_48000HZ;
+               break;
+       case 96000:
+               sampling_rate = RT712_SDCA_RATE_96000HZ;
+               break;
+       case 192000:
+               sampling_rate = RT712_SDCA_RATE_192000HZ;
+               break;
+       default:
+               dev_err(component->dev, "Rate %d is not supported\n",
+                       params_rate(params));
+               return -EINVAL;
+       }
+
+       /* set sampling frequency */
+       regmap_write(rt712->regmap,
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+               sampling_rate);
+       regmap_write(rt712->regmap,
+               SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+               sampling_rate);
+
+       return 0;
+}
+
+static int rt712_sdca_dmic_hw_free(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+       struct sdw_stream_runtime *sdw_stream =
+               snd_soc_dai_get_dma_data(dai, substream);
+
+       if (!rt712->slave)
+               return -EINVAL;
+
+       sdw_stream_remove_slave(rt712->slave, sdw_stream);
+       return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_dmic_ops = {
+       .hw_params      = rt712_sdca_dmic_hw_params,
+       .hw_free        = rt712_sdca_dmic_hw_free,
+       .set_stream     = rt712_sdca_dmic_set_sdw_stream,
+       .shutdown       = rt712_sdca_dmic_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+       {
+               .name = "rt712-sdca-dmic-aif1",
+               .id = RT712_AIF1,
+               .capture = {
+                       .stream_name = "DP2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = RT712_STEREO_RATES,
+                       .formats = RT712_FORMATS,
+               },
+               .ops = &rt712_sdca_dmic_ops,
+       },
+};
+
+static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
+                       struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+       struct rt712_sdca_dmic_priv *rt712;
+       int ret;
+
+       rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+       if (!rt712)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, rt712);
+       rt712->slave = slave;
+       rt712->regmap = regmap;
+       rt712->mbq_regmap = mbq_regmap;
+
+       /*
+        * Mark hw_init to false
+        * HW init will be performed when device reports present
+        */
+       rt712->hw_init = false;
+       rt712->first_hw_init = false;
+       rt712->fu1e_dapm_mute = true;
+       rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+               rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+
+       ret =  devm_snd_soc_register_component(dev,
+                       &soc_sdca_dev_rt712_dmic,
+                       rt712_sdca_dmic_dai,
+                       ARRAY_SIZE(rt712_sdca_dmic_dai));
+
+       dev_dbg(&slave->dev, "%s\n", __func__);
+
+       return ret;
+}
+
+
+static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
+                               enum sdw_slave_status status)
+{
+       struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+       /* Update the status */
+       rt712->status = status;
+
+       if (status == SDW_SLAVE_UNATTACHED)
+               rt712->hw_init = false;
+
+       /*
+        * Perform initialization only if slave status is present and
+        * hw_init flag is false
+        */
+       if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED)
+               return 0;
+
+       /* perform I/O transfers required for Slave initialization */
+       return rt712_sdca_dmic_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_dmic_read_prop(struct sdw_slave *slave)
+{
+       struct sdw_slave_prop *prop = &slave->prop;
+       int nval, i;
+       u32 bit;
+       unsigned long addr;
+       struct sdw_dpn_prop *dpn;
+
+       prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+       prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+       prop->paging_support = true;
+
+       /* first we need to allocate memory for set bits in port lists */
+       prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+       prop->sink_ports = 0;
+
+       nval = hweight32(prop->source_ports);
+       prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+               sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+       if (!prop->src_dpn_prop)
+               return -ENOMEM;
+
+       i = 0;
+       dpn = prop->src_dpn_prop;
+       addr = prop->source_ports;
+       for_each_set_bit(bit, &addr, 32) {
+               dpn[i].num = bit;
+               dpn[i].type = SDW_DPN_FULL;
+               dpn[i].simple_ch_prep_sm = true;
+               dpn[i].ch_prep_timeout = 10;
+               i++;
+       }
+
+       /* set the timeout values */
+       prop->clk_stop_timeout = 200;
+
+       /* wake-up event */
+       prop->wake_capable = 1;
+
+       return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_dmic_id[] = {
+       SDW_SLAVE_ENTRY_EXT(0x025d, 0x1712, 0x3, 0x1, 0),
+       SDW_SLAVE_ENTRY_EXT(0x025d, 0x1713, 0x3, 0x1, 0),
+       SDW_SLAVE_ENTRY_EXT(0x025d, 0x1716, 0x3, 0x1, 0),
+       SDW_SLAVE_ENTRY_EXT(0x025d, 0x1717, 0x3, 0x1, 0),
+       {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id);
+
+static int __maybe_unused rt712_sdca_dmic_dev_suspend(struct device *dev)
+{
+       struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+       if (!rt712->hw_init)
+               return 0;
+
+       regcache_cache_only(rt712->regmap, true);
+       regcache_cache_only(rt712->mbq_regmap, true);
+
+       return 0;
+}
+
+static int __maybe_unused rt712_sdca_dmic_dev_system_suspend(struct device *dev)
+{
+       struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev);
+
+       if (!rt712_sdca->hw_init)
+               return 0;
+
+       return rt712_sdca_dmic_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt712_sdca_dmic_dev_resume(struct device *dev)
+{
+       struct sdw_slave *slave = dev_to_sdw_dev(dev);
+       struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+       unsigned long time;
+
+       if (!rt712->first_hw_init)
+               return 0;
+
+       if (!slave->unattach_request)
+               goto regmap_sync;
+
+       time = wait_for_completion_timeout(&slave->initialization_complete,
+                               msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+       if (!time) {
+               dev_err(&slave->dev, "Initialization not complete, timed out\n");
+               sdw_show_ping_status(slave->bus, true);
+
+               return -ETIMEDOUT;
+       }
+
+regmap_sync:
+       slave->unattach_request = 0;
+       regcache_cache_only(rt712->regmap, false);
+       regcache_sync(rt712->regmap);
+       regcache_cache_only(rt712->mbq_regmap, false);
+       regcache_sync(rt712->mbq_regmap);
+       return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_dmic_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume)
+       SET_RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL)
+};
+
+
+static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
+       .read_prop = rt712_sdca_dmic_read_prop,
+       .update_status = rt712_sdca_dmic_update_status,
+};
+
+static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave,
+                               const struct sdw_device_id *id)
+{
+       struct regmap *regmap, *mbq_regmap;
+
+       /* Regmap Initialization */
+       mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_dmic_mbq_regmap);
+       if (IS_ERR(mbq_regmap))
+               return PTR_ERR(mbq_regmap);
+
+       regmap = devm_regmap_init_sdw(slave, &rt712_sdca_dmic_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave)
+{
+       struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+       if (rt712->first_hw_init)
+               pm_runtime_disable(&slave->dev);
+
+       return 0;
+}
+
+static struct sdw_driver rt712_sdca_dmic_sdw_driver = {
+       .driver = {
+               .name = "rt712-sdca-dmic",
+               .owner = THIS_MODULE,
+               .pm = &rt712_sdca_dmic_pm,
+       },
+       .probe = rt712_sdca_dmic_sdw_probe,
+       .remove = rt712_sdca_dmic_sdw_remove,
+       .ops = &rt712_sdca_dmic_slave_ops,
+       .id_table = rt712_sdca_dmic_id,
+};
+module_sdw_driver(rt712_sdca_dmic_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA DMIC SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h
new file mode 100644 (file)
index 0000000..74c2967
--- /dev/null
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-dmic.h -- RT712 SDCA DMIC ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_DMIC_H__
+#define __RT712_SDW_DMIC_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+struct  rt712_sdca_dmic_priv {
+       struct regmap *regmap;
+       struct regmap *mbq_regmap;
+       struct snd_soc_component *component;
+       struct sdw_slave *slave;
+       enum sdw_slave_status status;
+       struct sdw_bus_params params;
+       bool hw_init;
+       bool first_hw_init;
+       bool fu1e_dapm_mute;
+       bool fu1e_mixer_mute[4];
+};
+
+struct rt712_sdca_dmic_kctrl_priv {
+       unsigned int reg_base;
+       unsigned int count;
+       unsigned int max;
+       unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01  0x01
+#define CH_02  0x02
+#define CH_03  0x03
+#define CH_04  0x04
+
+static const struct reg_default rt712_sdca_dmic_reg_defaults[] = {
+       { 0x201a, 0x00 },
+       { 0x201b, 0x00 },
+       { 0x201c, 0x00 },
+       { 0x201d, 0x00 },
+       { 0x201e, 0x00 },
+       { 0x201f, 0x00 },
+       { 0x2029, 0x00 },
+       { 0x202a, 0x00 },
+       { 0x202d, 0x00 },
+       { 0x202e, 0x00 },
+       { 0x202f, 0x00 },
+       { 0x2030, 0x00 },
+       { 0x2031, 0x00 },
+       { 0x2032, 0x00 },
+       { 0x2033, 0x00 },
+       { 0x2034, 0x00 },
+       { 0x2230, 0x00 },
+       { 0x2231, 0x2f },
+       { 0x2232, 0x80 },
+       { 0x2f01, 0x00 },
+       { 0x2f02, 0x09 },
+       { 0x2f03, 0x00 },
+       { 0x2f04, 0x00 },
+       { 0x2f05, 0x0b },
+       { 0x2f06, 0x01 },
+       { 0x2f08, 0x00 },
+       { 0x2f09, 0x00 },
+       { 0x2f0a, 0x01 },
+       { 0x2f35, 0x02 },
+       { 0x2f36, 0xcf },
+       { 0x2f52, 0x08 },
+       { 0x2f58, 0x07 },
+       { 0x2f59, 0x07 },
+       { 0x3201, 0x01 },
+       { 0x320c, 0x00 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = {
+       { 0x0590001e, 0x0020 },
+       { 0x06100000, 0x0010 },
+       { 0x06100006, 0x0055 },
+       { 0x06100010, 0x2630 },
+       { 0x06100011, 0x152f },
+       { 0x06100013, 0x0102 },
+       { 0x06100015, 0x2219 },
+       { 0x06100018, 0x0102 },
+       { 0x06100026, 0x2c29 },
+       { 0x06100027, 0x2d2b },
+       { 0x0610002b, 0x2a32 },
+       { 0x0610002f, 0x3355 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+       { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+};
+
+#endif /* __RT712_SDW_DMIC_H__ */
index 8d2fa769bb2e2cdc6152c09de10d1eb3e73b9cc5..89d245655ca480ac2a8f7b548f62bbd62412ebda 100644 (file)
@@ -992,19 +992,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt712 = {
 static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -1012,11 +1000,7 @@ static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void rt712_sdca_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -1028,14 +1012,14 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        struct sdw_stream_config stream_config;
        struct sdw_port_config port_config;
        enum sdw_data_direction direction;
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval, port, num_channels;
        unsigned int sampling_rate;
 
        dev_dbg(dai->dev, "%s %s", __func__, dai->name);
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt712->slave)
@@ -1068,7 +1052,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        port_config.num = port;
 
        retval = sdw_stream_add_slave(rt712->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -1128,13 +1112,13 @@ static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt712->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt712->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt712->slave, sdw_stream);
        return 0;
 }
 
index cf647162f9daf2582731e7b6374c421a852ffd5b..c6a94a23f46e8548b50dbfc2ddeef6bac1b11ba7 100644 (file)
@@ -40,10 +40,6 @@ struct  rt712_sdca_priv {
        bool fu0f_mixer_r_mute;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 /* NID */
 #define RT712_VENDOR_REG                       0x20
 #define RT712_VENDOR_CALI                      0x58
index 920510365fd772934e3675a93be21b8143c63d1b..b989f907784b9d4dd1f36c52f3362b1886f44bbf 100644 (file)
@@ -784,16 +784,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
 static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
-       struct rt715_sdw_stream_data *stream;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -802,14 +793,7 @@ static void rt715_sdca_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 
 {
-       struct rt715_sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
-       if (!stream)
-               return;
-
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -820,13 +804,13 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct rt715_sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int val;
 
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt715->slave)
@@ -851,7 +835,7 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt715->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(component->dev, "Unable to configure port, retval:%d\n",
                        retval);
@@ -922,13 +906,13 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
-       struct rt715_sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt715->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt715->slave, sdw_stream);
        return 0;
 }
 
index 90881b455ece9bb0b388f6a7c43b670730043086..7577f3151934f437042d0dcc62fa1210838ca8c1 100644 (file)
@@ -37,10 +37,6 @@ struct rt715_sdca_priv {
        int kctl_8ch_orig[8];
 };
 
-struct rt715_sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 struct rt715_sdca_kcontrol_private {
        unsigned int reg_base;
        unsigned int count;
index c6dd9df7be45c2baeaf4aca086784aac6e9d2442..6c2e165dd6218a7dbe15ea80df5cc43411d93bd4 100644 (file)
@@ -765,19 +765,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                int direction)
 {
 
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -786,11 +774,7 @@ static void rt715_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -801,13 +785,13 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
        struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream;
        int retval;
        unsigned int val = 0;
 
-       stream = snd_soc_dai_get_dma_data(dai, substream);
+       sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!rt715->slave)
@@ -830,7 +814,7 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        retval = sdw_stream_add_slave(rt715->slave, &stream_config,
-                                       &port_config, 1, stream->sdw_stream);
+                                       &port_config, 1, sdw_stream);
        if (retval) {
                dev_err(dai->dev, "Unable to configure port\n");
                return retval;
@@ -893,13 +877,13 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
+       struct sdw_stream_runtime *sdw_stream =
                snd_soc_dai_get_dma_data(dai, substream);
 
        if (!rt715->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(rt715->slave, sdw_stream);
        return 0;
 }
 
index 25dba61f176097326e062a3e1b492e57ba4c7344..17a8d041c1c361adf0dbff0d25b140e5d7a74c9b 100644 (file)
@@ -27,10 +27,6 @@ struct rt715_priv {
        unsigned int kctl_8ch_vol_ori[8];
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 /* NID */
 #define RT715_AUDIO_FUNCTION_GROUP                     0x01
 #define RT715_MIC_ADC                                  0x07
index 62b02d764f09054e6ebc9305847bfd255047bcd2..5498ff027c58ae795378519f07056e14297ec258 100644 (file)
@@ -23,10 +23,6 @@ struct  sdw_mockup_priv {
        struct sdw_slave *slave;
 };
 
-struct sdw_stream_data {
-       struct sdw_stream_runtime *sdw_stream;
-};
-
 static int sdw_mockup_component_probe(struct snd_soc_component *component)
 {
        return 0;
@@ -45,19 +41,7 @@ static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
 static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
                                     int direction)
 {
-       struct sdw_stream_data *stream;
-
-       if (!sdw_stream)
-               return 0;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       stream->sdw_stream = sdw_stream;
-
-       /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
-       snd_soc_dai_dma_data_set(dai, direction, stream);
+       snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
 
        return 0;
 }
@@ -65,11 +49,7 @@ static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
 static void sdw_mockup_shutdown(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
-       struct sdw_stream_data *stream;
-
-       stream = snd_soc_dai_get_dma_data(dai, substream);
        snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(stream);
 }
 
 static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -80,11 +60,10 @@ static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
        struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
        struct sdw_stream_config stream_config = {0};
        struct sdw_port_config port_config = {0};
-       struct sdw_stream_data *stream;
+       struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
        int ret;
 
-       stream = snd_soc_dai_get_dma_data(dai, substream);
-       if (!stream)
+       if (!sdw_stream)
                return -EINVAL;
 
        if (!sdw_mockup->slave)
@@ -99,7 +78,7 @@ static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
                port_config.num = 8;
 
        ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config,
-                                  &port_config, 1, stream->sdw_stream);
+                                  &port_config, 1, sdw_stream);
        if (ret)
                dev_err(dai->dev, "Unable to configure port\n");
 
@@ -111,13 +90,12 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
-       struct sdw_stream_data *stream =
-               snd_soc_dai_get_dma_data(dai, substream);
+       struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
 
        if (!sdw_mockup->slave)
                return -EINVAL;
 
-       sdw_stream_remove_slave(sdw_mockup->slave, stream->sdw_stream);
+       sdw_stream_remove_slave(sdw_mockup->slave, sdw_stream);
        return 0;
 }
 
index fa4b0a60f8a91dac9cd4b309a277c69c65d2cf75..b6c132edf3bd1b186596242cc2e5ba6ecab9e01c 100644 (file)
@@ -1591,7 +1591,7 @@ static const struct snd_soc_component_driver sma1303_component = {
        .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
 };
 
-const struct regmap_config sma_i2c_regmap = {
+static const struct regmap_config sma_i2c_regmap = {
        .reg_bits = 8,
        .val_bits = 8,
 
index 27026030704a3c8b6a255c185829234871638c70..a40fd20df984585cceca6630d45ab19d9c4f8e61 100644 (file)
@@ -24,7 +24,7 @@ static const struct i2c_device_id src4xxx_i2c_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
 
-static const struct of_device_id src4xxx_of_match[] = {
+static const struct of_device_id src4xxx_of_match[] __maybe_unused = {
        { .compatible = "ti,src4392", },
        { }
 };
index cbbe83b85adafbff2a8ccf84c4732591dd8dbece..00b60369b02988ad99d7c55d574dda385de182b1 100644 (file)
@@ -280,9 +280,12 @@ static inline int ssm2602_get_coeff(int mclk, int rate)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
-               if (ssm2602_coeff_table[i].rate == rate &&
-                       ssm2602_coeff_table[i].mclk == mclk)
-                       return ssm2602_coeff_table[i].srate;
+               if (ssm2602_coeff_table[i].rate == rate) {
+                       if (ssm2602_coeff_table[i].mclk == mclk)
+                               return ssm2602_coeff_table[i].srate;
+                       if (ssm2602_coeff_table[i].mclk == mclk / 2)
+                               return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2;
+               }
        }
        return -EINVAL;
 }
@@ -365,18 +368,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                switch (freq) {
                case 12288000:
                case 18432000:
+               case 24576000:
+               case 36864000:
                        ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
                        break;
                case 11289600:
                case 16934400:
+               case 22579200:
+               case 33868800:
                        ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
                        break;
                case 12000000:
+               case 24000000:
                        ssm2602->sysclk_constraints = NULL;
                        break;
                default:
                        return -EINVAL;
                }
+
                ssm2602->sysclk = freq;
        } else {
                unsigned int mask;
index 8c86b578eba8312a59fe5f811ba191b2378dca2c..29af9595dac19e4bdc32d7928d6bc8e3f185a611 100644 (file)
@@ -1054,35 +1054,32 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
        of_property_read_u8(np, "st,ch3-output-mapping",
                            &pdata->ch3_output_mapping);
 
-       if (of_get_property(np, "st,fault-detect-recovery", NULL))
-               pdata->fault_detect_recovery = 1;
-       if (of_get_property(np, "st,thermal-warning-recovery", NULL))
-               pdata->thermal_warning_recovery = 1;
-       if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
-               pdata->thermal_warning_adjustment = 1;
-       if (of_get_property(np, "st,needs_esd_watchdog", NULL))
-               pdata->needs_esd_watchdog = 1;
+       pdata->fault_detect_recovery =
+               of_property_read_bool(np, "st,fault-detect-recovery");
+       pdata->thermal_warning_recovery =
+               of_property_read_bool(np, "st,thermal-warning-recovery");
+       pdata->thermal_warning_adjustment =
+               of_property_read_bool(np, "st,thermal-warning-adjustment");
+       pdata->needs_esd_watchdog =
+               of_property_read_bool(np, "st,needs_esd_watchdog");
 
        tmp = 140;
        of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
        pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
 
        /* CONFE */
-       if (of_get_property(np, "st,max-power-use-mpcc", NULL))
-               pdata->max_power_use_mpcc = 1;
-
-       if (of_get_property(np, "st,max-power-correction", NULL))
-               pdata->max_power_correction = 1;
-
-       if (of_get_property(np, "st,am-reduction-mode", NULL))
-               pdata->am_reduction_mode = 1;
-
-       if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
-               pdata->odd_pwm_speed_mode = 1;
+       pdata->max_power_use_mpcc =
+               of_property_read_bool(np, "st,max-power-use-mpcc");
+       pdata->max_power_correction =
+               of_property_read_bool(np, "st,max-power-correction");
+       pdata->am_reduction_mode =
+               of_property_read_bool(np, "st,am-reduction-mode");
+       pdata->odd_pwm_speed_mode =
+               of_property_read_bool(np, "st,odd-pwm-speed-mode");
 
        /* CONFF */
-       if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
-               pdata->invalid_input_detect_mute = 1;
+       pdata->invalid_input_detect_mute =
+               of_property_read_bool(np, "st,invalid-input-detect-mute");
 
        sta32x->pdata = pdata;
 
index 9ed13aeb3cbdc6a10d8a106d399d017ff5ed54c4..b033a5fcd6c04b9ab263a8485e477e4e99aeb8e3 100644 (file)
@@ -1106,12 +1106,12 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
        of_property_read_u8(np, "st,ch3-output-mapping",
                            &pdata->ch3_output_mapping);
 
-       if (of_get_property(np, "st,thermal-warning-recovery", NULL))
-               pdata->thermal_warning_recovery = 1;
-       if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
-               pdata->thermal_warning_adjustment = 1;
-       if (of_get_property(np, "st,fault-detect-recovery", NULL))
-               pdata->fault_detect_recovery = 1;
+       pdata->thermal_warning_recovery =
+               of_property_read_bool(np, "st,thermal-warning-recovery");
+       pdata->thermal_warning_adjustment =
+               of_property_read_bool(np, "st,thermal-warning-adjustment");
+       pdata->fault_detect_recovery =
+               of_property_read_bool(np, "st,fault-detect-recovery");
 
        pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP;
        if (!of_property_read_string(np, "st,ffx-power-output-mode",
@@ -1133,41 +1133,34 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
        of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
        pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
 
-       if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
-               pdata->oc_warning_adjustment = 1;
+       pdata->oc_warning_adjustment =
+               of_property_read_bool(np, "st,overcurrent-warning-adjustment");
 
        /* CONFE */
-       if (of_get_property(np, "st,max-power-use-mpcc", NULL))
-               pdata->max_power_use_mpcc = 1;
-
-       if (of_get_property(np, "st,max-power-correction", NULL))
-               pdata->max_power_correction = 1;
-
-       if (of_get_property(np, "st,am-reduction-mode", NULL))
-               pdata->am_reduction_mode = 1;
-
-       if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
-               pdata->odd_pwm_speed_mode = 1;
-
-       if (of_get_property(np, "st,distortion-compensation", NULL))
-               pdata->distortion_compensation = 1;
+       pdata->max_power_use_mpcc =
+               of_property_read_bool(np, "st,max-power-use-mpcc");
+       pdata->max_power_correction =
+               of_property_read_bool(np, "st,max-power-correction");
+       pdata->am_reduction_mode =
+               of_property_read_bool(np, "st,am-reduction-mode");
+       pdata->odd_pwm_speed_mode =
+               of_property_read_bool(np, "st,odd-pwm-speed-mode");
+       pdata->distortion_compensation =
+               of_property_read_bool(np, "st,distortion-compensation");
 
        /* CONFF */
-       if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
-               pdata->invalid_input_detect_mute = 1;
+       pdata->invalid_input_detect_mute =
+               of_property_read_bool(np, "st,invalid-input-detect-mute");
 
        /* MISC */
-       if (of_get_property(np, "st,activate-mute-output", NULL))
-               pdata->activate_mute_output = 1;
-
-       if (of_get_property(np, "st,bridge-immediate-off", NULL))
-               pdata->bridge_immediate_off = 1;
-
-       if (of_get_property(np, "st,noise-shape-dc-cut", NULL))
-               pdata->noise_shape_dc_cut = 1;
-
-       if (of_get_property(np, "st,powerdown-master-volume", NULL))
-               pdata->powerdown_master_vol = 1;
+       pdata->activate_mute_output =
+               of_property_read_bool(np, "st,activate-mute-output");
+       pdata->bridge_immediate_off =
+               of_property_read_bool(np, "st,bridge-immediate-off");
+       pdata->noise_shape_dc_cut =
+               of_property_read_bool(np, "st,noise-shape-dc-cut");
+       pdata->powerdown_master_vol =
+               of_property_read_bool(np, "st,powerdown-master-volume");
 
        if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) {
                if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128)
index 22143cc5afa706f76df0796427c35df126d9d34c..f9e7122894bd27b4feb11d833831717689b4ed82 100644 (file)
@@ -840,7 +840,7 @@ static int tas5086_probe(struct snd_soc_component *component)
                        snprintf(name, sizeof(name),
                                 "ti,mid-z-channel-%d", i + 1);
 
-                       if (of_get_property(of_node, name, NULL) != NULL)
+                       if (of_property_read_bool(of_node, name))
                                priv->pwm_start_mid_z |= 1 << i;
                }
        }
index 84ec1b5276465134de1ab8cdff00ad08043a5dd5..f39c3273b2fd819fd9aa5220eef3a0a8c9c15324 100644 (file)
@@ -718,6 +718,63 @@ static const struct regmap_config tas5721_regmap_config = {
        .volatile_table                 = &tas571x_volatile_regs,
 };
 
+static const char *const tas5733_supply_names[] = {
+       "AVDD",
+       "DVDD",
+       "PVDD",
+};
+
+static const struct reg_default tas5733_reg_defaults[] = {
+       {TAS571X_CLK_CTRL_REG,          0x6c},
+       {TAS571X_DEV_ID_REG,            0x00},
+       {TAS571X_ERR_STATUS_REG,        0x00},
+       {TAS571X_SYS_CTRL_1_REG,        0xa0},
+       {TAS571X_SDI_REG,               0x05},
+       {TAS571X_SYS_CTRL_2_REG,        0x40},
+       {TAS571X_SOFT_MUTE_REG,         0x07},
+       {TAS571X_MVOL_REG,              0x03ff},
+       {TAS571X_CH1_VOL_REG,           0x00c0},
+       {TAS571X_CH2_VOL_REG,           0x00c0},
+       {TAS571X_CH3_VOL_REG,           0x00c0},
+       {TAS571X_VOL_CFG_REG,           0xf0},
+       {TAS571X_MODULATION_LIMIT_REG,  0x07},
+       {TAS571X_IC_DELAY_CH1_REG,      0xb8},
+       {TAS571X_IC_DELAY_CH2_REG,      0x60},
+       {TAS571X_IC_DELAY_CH3_REG,      0xa0},
+       {TAS571X_IC_DELAY_CH4_REG,      0x48},
+       {TAS571X_PWM_CH_SDN_GROUP_REG,  0x30},
+       {TAS571X_START_STOP_PERIOD_REG, 0x68},
+       {TAS571X_OSC_TRIM_REG,          0x82},
+       {TAS571X_BKND_ERR_REG,          0x02},
+       {TAS571X_INPUT_MUX_REG,         0x00897772},
+       {TAS571X_PWM_MUX_REG,           0x01021345},
+       {TAS5717_CH1_RIGHT_CH_MIX_REG,  0x00},
+       {TAS5717_CH1_LEFT_CH_MIX_REG,   0x800000},
+       {TAS5717_CH2_LEFT_CH_MIX_REG,   0x00},
+       {TAS5717_CH2_RIGHT_CH_MIX_REG,  0x800000},
+};
+
+static const struct regmap_config tas5733_regmap_config = {
+       .reg_bits                       = 8,
+       .val_bits                       = 32,
+       .max_register                   = 0xff,
+       .reg_read                       = tas571x_reg_read,
+       .reg_write                      = tas571x_reg_write,
+       .reg_defaults                   = tas5733_reg_defaults,
+       .num_reg_defaults               = ARRAY_SIZE(tas5733_reg_defaults),
+       .cache_type                     = REGCACHE_RBTREE,
+       .wr_table                       = &tas571x_write_regs,
+       .volatile_table                 = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5733_chip = {
+       .supply_names                   = tas5733_supply_names,
+       .num_supply_names               = ARRAY_SIZE(tas5733_supply_names),
+       .controls                       = tas5717_controls,
+       .num_controls                   = ARRAY_SIZE(tas5717_controls),
+       .regmap_config                  = &tas5733_regmap_config,
+       .vol_reg_size                   = 2,
+};
 
 static const struct tas571x_chip tas5721_chip = {
        .supply_names                   = tas5721_supply_names,
@@ -897,6 +954,7 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = {
        { .compatible = "ti,tas5717", .data = &tas5717_chip, },
        { .compatible = "ti,tas5719", .data = &tas5717_chip, },
        { .compatible = "ti,tas5721", .data = &tas5721_chip, },
+       { .compatible = "ti,tas5733", .data = &tas5733_chip, },
        { }
 };
 MODULE_DEVICE_TABLE(of, tas571x_of_match);
@@ -907,6 +965,7 @@ static const struct i2c_device_id tas571x_i2c_id[] = {
        { "tas5717", (kernel_ulong_t) &tas5717_chip },
        { "tas5719", (kernel_ulong_t) &tas5717_chip },
        { "tas5721", (kernel_ulong_t) &tas5721_chip },
+       { "tas5733", (kernel_ulong_t) &tas5733_chip },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
index de6d01c8fdd340012592f147278817c79f1ae108..4d27b60bd8043bc3b4935af60d2fe005ea38fb6c 100644 (file)
@@ -339,7 +339,8 @@ static int tas5720_codec_probe(struct snd_soc_component *component)
                break;
        default:
                dev_err(component->dev, "unexpected private driver data\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto probe_fail;
        }
 
        if (device_id != expected_device_id)
index d2548fdf9ae562378d8dca44403d3652df9a9e3b..8bf3510a3ea3f295613a7dc4c57504b402d53b8b 100644 (file)
@@ -5138,20 +5138,17 @@ static int wcd9335_irq_init(struct wcd9335_codec *wcd)
         * INTR2 is a subset of first interrupt sources MAD, VBAT, and SVA
         */
        wcd->intr1 = of_irq_get_byname(wcd->dev->of_node, "intr1");
-       if (wcd->intr1 < 0) {
-               if (wcd->intr1 != -EPROBE_DEFER)
-                       dev_err(wcd->dev, "Unable to configure IRQ\n");
-
-               return wcd->intr1;
-       }
+       if (wcd->intr1 < 0)
+               return dev_err_probe(wcd->dev, wcd->intr1,
+                                    "Unable to configure IRQ\n");
 
        ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->intr1,
                                 IRQF_TRIGGER_HIGH, 0,
                                 &wcd9335_regmap_irq1_chip, &wcd->irq_data);
        if (ret)
-               dev_err(wcd->dev, "Failed to register IRQ chip: %d\n", ret);
+               return dev_err_probe(wcd->dev, ret, "Failed to register IRQ chip\n");
 
-       return ret;
+       return 0;
 }
 
 static int wcd9335_slim_probe(struct slim_device *slim)
@@ -5207,17 +5204,15 @@ static int wcd9335_slim_status(struct slim_device *sdev,
        slim_get_logical_addr(wcd->slim_ifc_dev);
 
        wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config);
-       if (IS_ERR(wcd->regmap)) {
-               dev_err(dev, "Failed to allocate slim register map\n");
-               return PTR_ERR(wcd->regmap);
-       }
+       if (IS_ERR(wcd->regmap))
+               return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+                                    "Failed to allocate slim register map\n");
 
        wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev,
                                                  &wcd9335_ifc_regmap_config);
-       if (IS_ERR(wcd->if_regmap)) {
-               dev_err(dev, "Failed to allocate ifc register map\n");
-               return PTR_ERR(wcd->if_regmap);
-       }
+       if (IS_ERR(wcd->if_regmap))
+               return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+                                    "Failed to allocate ifc register map\n");
 
        ret = wcd9335_bring_up(wcd);
        if (ret) {
index 783479a4d535a17b92e6e15fdefef5bd0d884605..c0d1fa36d841175dba061096e0a0d475d6e37ca7 100644 (file)
@@ -5868,10 +5868,9 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
        slim_get_logical_addr(wcd->sidev);
        wcd->if_regmap = regmap_init_slimbus(wcd->sidev,
                                  &wcd934x_ifc_regmap_config);
-       if (IS_ERR(wcd->if_regmap)) {
-               dev_err(dev, "Failed to allocate ifc register map\n");
-               return PTR_ERR(wcd->if_regmap);
-       }
+       if (IS_ERR(wcd->if_regmap))
+               return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+                                    "Failed to allocate ifc register map\n");
 
        of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate",
                             &wcd->dmic_sample_rate);
@@ -5893,12 +5892,12 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
 
 static int wcd934x_codec_probe(struct platform_device *pdev)
 {
-       struct wcd934x_ddata *data = dev_get_drvdata(pdev->dev.parent);
-       struct wcd934x_codec *wcd;
        struct device *dev = &pdev->dev;
+       struct wcd934x_ddata *data = dev_get_drvdata(dev->parent);
+       struct wcd934x_codec *wcd;
        int ret, irq;
 
-       wcd = devm_kzalloc(&pdev->dev, sizeof(*wcd), GFP_KERNEL);
+       wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
        if (!wcd)
                return -ENOMEM;
 
@@ -5923,19 +5922,15 @@ static int wcd934x_codec_probe(struct platform_device *pdev)
        memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs));
 
        irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS);
-       if (irq < 0) {
-               dev_err(wcd->dev, "Failed to get SLIM IRQ\n");
-               return irq;
-       }
+       if (irq < 0)
+               return dev_err_probe(wcd->dev, irq, "Failed to get SLIM IRQ\n");
 
        ret = devm_request_threaded_irq(dev, irq, NULL,
                                        wcd934x_slim_irq_handler,
                                        IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                                        "slim", wcd);
-       if (ret) {
-               dev_err(dev, "Failed to request slimbus irq\n");
-               return ret;
-       }
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to request slimbus irq\n");
 
        wcd934x_register_mclk_output(wcd);
        platform_set_drvdata(pdev, wcd);
index fcac763b04d1bc18bd73e0e00a3f2ef477952d09..11b264a63b044882a36063d8e478fae65190e031 100644 (file)
@@ -4235,18 +4235,15 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
        int ret;
 
        wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
-       if (wcd938x->reset_gpio < 0) {
-               dev_err(dev, "Failed to get reset gpio: err = %d\n",
-                       wcd938x->reset_gpio);
-               return wcd938x->reset_gpio;
-       }
+       if (wcd938x->reset_gpio < 0)
+               return dev_err_probe(dev, wcd938x->reset_gpio,
+                                    "Failed to get reset gpio\n");
 
        wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro",
                                                GPIOD_OUT_LOW);
-       if (IS_ERR(wcd938x->us_euro_gpio)) {
-               dev_err(dev, "us-euro swap Control GPIO not found\n");
-               return PTR_ERR(wcd938x->us_euro_gpio);
-       }
+       if (IS_ERR(wcd938x->us_euro_gpio))
+               return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
+                                    "us-euro swap Control GPIO not found\n");
 
        cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
 
@@ -4256,16 +4253,12 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
        wcd938x->supplies[3].supply = "vdd-mic-bias";
 
        ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies);
-       if (ret) {
-               dev_err(dev, "Failed to get supplies: err = %d\n", ret);
-               return ret;
-       }
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get supplies\n");
 
        ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
-       if (ret) {
-               dev_err(dev, "Failed to enable supplies: err = %d\n", ret);
-               return ret;
-       }
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to enable supplies\n");
 
        wcd938x_dt_parse_micbias_info(dev, wcd938x);
 
@@ -4529,11 +4522,9 @@ static int wcd938x_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int wcd938x_remove(struct platform_device *pdev)
+static void wcd938x_remove(struct platform_device *pdev)
 {
        component_master_del(&pdev->dev, &wcd938x_comp_ops);
-
-       return 0;
 }
 
 #if defined(CONFIG_OF)
@@ -4547,7 +4538,7 @@ MODULE_DEVICE_TABLE(of, wcd938x_dt_match);
 
 static struct platform_driver wcd938x_codec_driver = {
        .probe = wcd938x_probe,
-       .remove = wcd938x_remove,
+       .remove_new = wcd938x_remove,
        .driver = {
                .name = "wcd938x_codec",
                .of_match_table = of_match_ptr(wcd938x_dt_match),
index adaf886b0a9d20911915eaa602f423fcc87d791e..3bdbdf3770b5d87abebd9951aa9e2d79c34c2378 100644 (file)
@@ -2148,7 +2148,7 @@ err_jack_codec_dev:
        return ret;
 }
 
-static int wm5102_remove(struct platform_device *pdev)
+static void wm5102_remove(struct platform_device *pdev)
 {
        struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
        struct arizona *arizona = wm5102->core.arizona;
@@ -2163,8 +2163,6 @@ static int wm5102_remove(struct platform_device *pdev)
        arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
 
        arizona_jack_codec_dev_remove(&wm5102->core);
-
-       return 0;
 }
 
 static struct platform_driver wm5102_codec_driver = {
@@ -2172,7 +2170,7 @@ static struct platform_driver wm5102_codec_driver = {
                .name = "wm5102-codec",
        },
        .probe = wm5102_probe,
-       .remove = wm5102_remove,
+       .remove_new = wm5102_remove,
 };
 
 module_platform_driver(wm5102_codec_driver);
index e0b971620d0fb4da99235c42739bfbc27efd0b7b..ad670300de8d0c9e5e760b1ce5152141f11db476 100644 (file)
@@ -2506,7 +2506,7 @@ err_jack_codec_dev:
        return ret;
 }
 
-static int wm5110_remove(struct platform_device *pdev)
+static void wm5110_remove(struct platform_device *pdev)
 {
        struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
        struct arizona *arizona = wm5110->core.arizona;
@@ -2523,8 +2523,6 @@ static int wm5110_remove(struct platform_device *pdev)
        arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
 
        arizona_jack_codec_dev_remove(&wm5110->core);
-
-       return 0;
 }
 
 static struct platform_driver wm5110_codec_driver = {
@@ -2532,7 +2530,7 @@ static struct platform_driver wm5110_codec_driver = {
                .name = "wm5110-codec",
        },
        .probe = wm5110_probe,
-       .remove = wm5110_remove,
+       .remove_new = wm5110_remove,
 };
 
 module_platform_driver(wm5110_codec_driver);
index 41346e5ec5ad401e23e013c375fce2476114f38d..1dc8e20bdace62938f69f6dae475185b6e75c283 100644 (file)
@@ -9,7 +9,6 @@
  *
  * TODO:
  *  - TDM mode configuration.
- *  - Digital microphone support.
  */
 
 #include <linux/module.h>
index 8fe9a75d12357810f88fd596de1b39006545493d..bca3ebe0dac4c1aeecd44a6114c9c97da8b81c7e 100644 (file)
@@ -4657,11 +4657,9 @@ static int wm8994_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int wm8994_remove(struct platform_device *pdev)
+static void wm8994_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -4701,7 +4699,7 @@ static struct platform_driver wm8994_codec_driver = {
                .pm = &wm8994_pm_ops,
        },
        .probe = wm8994_probe,
-       .remove = wm8994_remove,
+       .remove_new = wm8994_remove,
 };
 
 module_platform_driver(wm8994_codec_driver);
index c0207e9a7d53a020343b8ff93f186fbeafed6808..87442840f0af20705bab7f1bf9d942b8cb2a31b5 100644 (file)
@@ -1193,7 +1193,7 @@ err_jack_codec_dev:
        return ret;
 }
 
-static int wm8997_remove(struct platform_device *pdev)
+static void wm8997_remove(struct platform_device *pdev)
 {
        struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
        struct arizona *arizona = wm8997->core.arizona;
@@ -1203,8 +1203,6 @@ static int wm8997_remove(struct platform_device *pdev)
        arizona_free_spk_irqs(arizona);
 
        arizona_jack_codec_dev_remove(&wm8997->core);
-
-       return 0;
 }
 
 static struct platform_driver wm8997_codec_driver = {
@@ -1212,7 +1210,7 @@ static struct platform_driver wm8997_codec_driver = {
                .name = "wm8997-codec",
        },
        .probe = wm8997_probe,
-       .remove = wm8997_remove,
+       .remove_new = wm8997_remove,
 };
 
 module_platform_driver(wm8997_codec_driver);
index 79fc6bbaa3aa3cd7b52ae2e8c9b56feaa5357520..3c2c4d12c08ecac7b511c844e795c04195fc5966 100644 (file)
@@ -1409,7 +1409,7 @@ err_pm_disable:
        return ret;
 }
 
-static int wm8998_remove(struct platform_device *pdev)
+static void wm8998_remove(struct platform_device *pdev)
 {
        struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
        struct arizona *arizona = wm8998->core.arizona;
@@ -1419,8 +1419,6 @@ static int wm8998_remove(struct platform_device *pdev)
        arizona_free_spk_irqs(arizona);
 
        arizona_jack_codec_dev_remove(&wm8998->core);
-
-       return 0;
 }
 
 static struct platform_driver wm8998_codec_driver = {
@@ -1428,7 +1426,7 @@ static struct platform_driver wm8998_codec_driver = {
                .name = "wm8998-codec",
        },
        .probe = wm8998_probe,
-       .remove = wm8998_remove,
+       .remove_new = wm8998_remove,
 };
 
 module_platform_driver(wm8998_codec_driver);
index ea0dbc634ecf1582d94bd64e5c87e5004fcf8a30..216120b68b64bbd5ade25cff7705d8066a707148 100644 (file)
@@ -787,6 +787,8 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
                adsp_dbg(dsp, "Failed to request '%s'\n", *filename);
                kfree(*filename);
                *filename = NULL;
+       } else {
+               adsp_dbg(dsp, "Found '%s'\n", *filename);
        }
 
        return ret;
@@ -807,7 +809,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
                if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
                                                   cirrus_dir, system_name,
                                                   asoc_component_prefix, "wmfw")) {
-                       adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
                        wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
                                                      cirrus_dir, system_name,
                                                      asoc_component_prefix, "bin");
@@ -819,7 +820,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
                if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
                                                   cirrus_dir, system_name,
                                                   NULL, "wmfw")) {
-                       adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
                        if (asoc_component_prefix)
                                wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
                                                              cirrus_dir, system_name,
@@ -835,7 +835,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
 
        if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
                                           "", NULL, NULL, "wmfw")) {
-               adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
                wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
                                              "", NULL, NULL, "bin");
                return 0;
@@ -844,12 +843,35 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
        ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
                                            cirrus_dir, NULL, NULL, "wmfw");
        if (!ret) {
-               adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
                wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
                                              cirrus_dir, NULL, NULL, "bin");
                return 0;
        }
 
+       if (dsp->wmfw_optional) {
+               if (system_name) {
+                       if (asoc_component_prefix)
+                               wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+                                                             cirrus_dir, system_name,
+                                                             asoc_component_prefix, "bin");
+
+                       if (!*coeff_firmware)
+                               wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+                                                             cirrus_dir, system_name,
+                                                             NULL, "bin");
+               }
+
+               if (!*coeff_firmware)
+                       wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+                                                     "", NULL, NULL, "bin");
+
+               if (!*coeff_firmware)
+                       wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+                                                     cirrus_dir, NULL, NULL, "bin");
+
+               return 0;
+       }
+
        adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
                 cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file,
                 system_name, asoc_component_prefix);
@@ -995,11 +1017,8 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
 
-static void wm_adsp_boot_work(struct work_struct *work)
+int wm_adsp_power_up(struct wm_adsp *dsp)
 {
-       struct wm_adsp *dsp = container_of(work,
-                                          struct wm_adsp,
-                                          boot_work);
        int ret = 0;
        char *wmfw_filename = NULL;
        const struct firmware *wmfw_firmware = NULL;
@@ -1010,16 +1029,28 @@ static void wm_adsp_boot_work(struct work_struct *work)
                                             &wmfw_firmware, &wmfw_filename,
                                             &coeff_firmware, &coeff_filename);
        if (ret)
-               return;
+               return ret;
 
-       cs_dsp_power_up(&dsp->cs_dsp,
-                       wmfw_firmware, wmfw_filename,
-                       coeff_firmware, coeff_filename,
-                       wm_adsp_fw_text[dsp->fw]);
+       ret = cs_dsp_power_up(&dsp->cs_dsp,
+                             wmfw_firmware, wmfw_filename,
+                             coeff_firmware, coeff_filename,
+                             wm_adsp_fw_text[dsp->fw]);
 
        wm_adsp_release_firmware_files(dsp,
                                       wmfw_firmware, wmfw_filename,
                                       coeff_firmware, coeff_filename);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_power_up);
+
+static void wm_adsp_boot_work(struct work_struct *work)
+{
+       struct wm_adsp *dsp = container_of(work,
+                                          struct wm_adsp,
+                                          boot_work);
+
+       wm_adsp_power_up(dsp);
 }
 
 int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
@@ -1102,8 +1133,10 @@ int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *comp
 {
        char preload[32];
 
-       snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
-       snd_soc_component_disable_pin(component, preload);
+       if (!dsp->cs_dsp.no_core_startstop) {
+               snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
+               snd_soc_component_disable_pin(component, preload);
+       }
 
        cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
 
index dc2f7a096e2667f2895f330e96d040bbe21103e3..769904d34a87c978a48615567675aa5299150677 100644 (file)
@@ -34,6 +34,7 @@ struct wm_adsp {
        unsigned int sys_config_size;
 
        int fw;
+       bool wmfw_optional;
 
        struct work_struct boot_work;
        int (*pre_run)(struct wm_adsp *dsp);
@@ -90,6 +91,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
 int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
                        struct snd_kcontrol *kcontrol, int event);
 
+int wm_adsp_power_up(struct wm_adsp *dsp);
+
 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);
index c3d0a2a7c36f2a362f174858ba892d1e5db3e5c2..28c92d90299e9e66490b978911a6f6e4e0f29fc4 100644 (file)
@@ -608,7 +608,7 @@ static int zl38_spi_probe(struct spi_device *spi)
                                               &zl38_dai, 1);
 }
 
-static const struct of_device_id zl38_dt_ids[] = {
+static const struct of_device_id zl38_dt_ids[] __maybe_unused = {
        { .compatible = "mscc,zl38060", },
        { /* sentinel */ }
 };
index 7f7dd07c63b2f1fdcfb710e30f0250c850fa14ce..acdf98b2ee9c20a006d8c997fd9f84ecb8d59c7a 100644 (file)
@@ -730,7 +730,7 @@ err_clk_disable:
        return ret;
 }
 
-static int dw_i2s_remove(struct platform_device *pdev)
+static void dw_i2s_remove(struct platform_device *pdev)
 {
        struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
 
@@ -738,7 +738,6 @@ static int dw_i2s_remove(struct platform_device *pdev)
                clk_disable_unprepare(dev->clk);
 
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -756,7 +755,7 @@ static const struct dev_pm_ops dwc_pm_ops = {
 
 static struct platform_driver dw_i2s_driver = {
        .probe          = dw_i2s_probe,
-       .remove         = dw_i2s_remove,
+       .remove_new     = dw_i2s_remove,
        .driver         = {
                .name   = "designware-i2s",
                .of_match_table = of_match_ptr(dw_i2s_of_match),
index 33b67db8794e367b5a0d4e7e5d603b8a1f48bb2a..725c530a363607aefd02b6a325ad0859ec9995f7 100644 (file)
@@ -172,6 +172,15 @@ config SND_MPC52xx_DMA
 config SND_SOC_POWERPC_DMA
        tristate
 
+config SND_SOC_POWERPC_QMC_AUDIO
+       tristate "QMC ALSA SoC support"
+       depends on CPM_QMC
+       help
+         ALSA SoC Audio support using the Freescale QUICC Multichannel
+         Controller (QMC).
+         Say Y or M if you want to add support for SoC audio using Freescale
+         QMC.
+
 comment "SoC Audio support for Freescale PPC boards:"
 
 config SND_SOC_MPC8610_HPCD
index b54beb1a66fa8665f5d9fc602dc1e81fb25c21b7..8db7e97d0bd5232a39918b7dc57079e467f4f30c 100644 (file)
@@ -28,6 +28,7 @@ snd-soc-fsl-easrc-objs := fsl_easrc.o
 snd-soc-fsl-xcvr-objs := fsl_xcvr.o
 snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o
 snd-soc-fsl-rpmsg-objs := fsl_rpmsg.o
+snd-soc-fsl-qmc-audio-objs := fsl_qmc_audio.o
 
 obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
 obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -44,6 +45,7 @@ obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o
 obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o
 obj-$(CONFIG_SND_SOC_FSL_RPMSG) += snd-soc-fsl-rpmsg.o
+obj-$(CONFIG_SND_SOC_POWERPC_QMC_AUDIO) += snd-soc-fsl-qmc-audio.o
 
 # MPC5200 Platform Support
 obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
index 9af4c4a35eb16610a92e48975e6f5a1121a1e9c7..e65a85feba78f66cb3b4cbf7d45bc41c35c8d123 100644 (file)
@@ -205,11 +205,9 @@ err:
        return ret;
 }
 
-static int eukrea_tlv320_remove(struct platform_device *pdev)
+static void eukrea_tlv320_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_card(&eukrea_tlv320);
-
-       return 0;
 }
 
 static const struct of_device_id imx_tlv320_dt_ids[] = {
@@ -224,7 +222,7 @@ static struct platform_driver eukrea_tlv320_driver = {
                .of_match_table = imx_tlv320_dt_ids,
        },
        .probe = eukrea_tlv320_probe,
-       .remove = eukrea_tlv320_remove,
+       .remove_new = eukrea_tlv320_remove,
 };
 
 module_platform_driver(eukrea_tlv320_driver);
index cdfca9fd1eb0079563af499ab68a2821a14a11da..40870668ee24fbe40eb7a3ef26b54c881351ff0f 100644 (file)
@@ -28,6 +28,8 @@
 #include "../codecs/wm8994.h"
 #include "../codecs/tlv320aic31xx.h"
 
+#define DRIVER_NAME "fsl-asoc-card"
+
 #define CS427x_SYSCLK_MCLK 0
 
 #define RX 0
@@ -607,6 +609,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
        priv->card.dapm_routes = audio_map;
        priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
+       priv->card.driver_name = DRIVER_NAME;
        /* Diversify the card configurations */
        if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
                codec_dai_name = "cs42888";
@@ -855,7 +858,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
        ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
        if (ret) {
-               dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed: %d\n", ret);
+               dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
                goto asrc_fail;
        }
 
@@ -915,7 +918,7 @@ MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
 static struct platform_driver fsl_asoc_card_driver = {
        .probe = fsl_asoc_card_probe,
        .driver = {
-               .name = "fsl-asoc-card",
+               .name = DRIVER_NAME,
                .pm = &snd_soc_pm_ops,
                .of_match_table = fsl_asoc_card_dt_ids,
        },
@@ -924,5 +927,5 @@ module_platform_driver(fsl_asoc_card_driver);
 
 MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
 MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
-MODULE_ALIAS("platform:fsl-asoc-card");
+MODULE_ALIAS("platform:" DRIVER_NAME);
 MODULE_LICENSE("GPL");
index e16e7b3fa96cce78688772f89571b1e3f1ef6498..adb8a59de2bd9eaab25daf2d932762c1c017cb2d 100644 (file)
@@ -1252,13 +1252,11 @@ err_pm_disable:
        return ret;
 }
 
-static int fsl_asrc_remove(struct platform_device *pdev)
+static void fsl_asrc_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                fsl_asrc_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static int fsl_asrc_runtime_resume(struct device *dev)
@@ -1394,7 +1392,7 @@ MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
 
 static struct platform_driver fsl_asrc_driver = {
        .probe = fsl_asrc_probe,
-       .remove = fsl_asrc_remove,
+       .remove_new = fsl_asrc_remove,
        .driver = {
                .name = "fsl-asrc",
                .of_match_table = fsl_asrc_ids,
index 1e421d9a03fbe45ec48865bd748d1dfaeba48b98..46b0c5dcc4a502d4207d3045333a0fcb26cafa70 100644 (file)
@@ -257,11 +257,9 @@ static int fsl_aud2htx_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int fsl_aud2htx_remove(struct platform_device *pdev)
+static void fsl_aud2htx_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
@@ -300,7 +298,7 @@ static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
 
 static struct platform_driver fsl_aud2htx_driver = {
        .probe = fsl_aud2htx_probe,
-       .remove = fsl_aud2htx_remove,
+       .remove_new = fsl_aud2htx_remove,
        .driver = {
                .name = "fsl-aud2htx",
                .pm = &fsl_aud2htx_pm_ops,
index 672148dd4b234febc98e4c7133bc09fda7d5ecad..0ab2c19621175e8c951393fb44b8594fd52eb0ac 100644 (file)
@@ -506,7 +506,7 @@ err_disable_pm:
        return ret;
 }
 
-static int fsl_audmix_remove(struct platform_device *pdev)
+static void fsl_audmix_remove(struct platform_device *pdev)
 {
        struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
 
@@ -514,8 +514,6 @@ static int fsl_audmix_remove(struct platform_device *pdev)
 
        if (priv->pdev)
                platform_device_unregister(priv->pdev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -558,7 +556,7 @@ static const struct dev_pm_ops fsl_audmix_pm = {
 
 static struct platform_driver fsl_audmix_driver = {
        .probe = fsl_audmix_probe,
-       .remove = fsl_audmix_remove,
+       .remove_new = fsl_audmix_remove,
        .driver = {
                .name = "fsl-audmix",
                .of_match_table = fsl_audmix_ids,
index 808fb61a7a0f9fb873bb5f8dafc2349128f2f6cd..963f9774c883fa4ab6fef62b57e90166e5700dd2 100644 (file)
@@ -890,15 +890,13 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int fsl_soc_dma_remove(struct platform_device *pdev)
+static void fsl_soc_dma_remove(struct platform_device *pdev)
 {
        struct dma_object *dma = dev_get_drvdata(&pdev->dev);
 
        iounmap(dma->channel);
        irq_dispose_mapping(dma->irq);
        kfree(dma);
-
-       return 0;
 }
 
 static const struct of_device_id fsl_soc_dma_ids[] = {
@@ -913,7 +911,7 @@ static struct platform_driver fsl_soc_dma_driver = {
                .of_match_table = fsl_soc_dma_ids,
        },
        .probe = fsl_soc_dma_probe,
-       .remove = fsl_soc_dma_remove,
+       .remove_new = fsl_soc_dma_remove,
 };
 
 module_platform_driver(fsl_soc_dma_driver);
index 3153d19136b29957df92d37eb8fdaed75b55fd5d..670cbdb361b6c7e547cc6ce2a1a61588d455eeb7 100644 (file)
@@ -1979,11 +1979,9 @@ static int fsl_easrc_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int fsl_easrc_remove(struct platform_device *pdev)
+static void fsl_easrc_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev)
@@ -2093,7 +2091,7 @@ static const struct dev_pm_ops fsl_easrc_pm_ops = {
 
 static struct platform_driver fsl_easrc_driver = {
        .probe = fsl_easrc_probe,
-       .remove = fsl_easrc_remove,
+       .remove_new = fsl_easrc_remove,
        .driver = {
                .name = "fsl-easrc",
                .pm = &fsl_easrc_pm_ops,
index 17fefd27ec90afeac6def55d320510d329a3db57..936f0cd4b06d8e20c34e9b172f9be581db43da05 100644 (file)
@@ -1101,7 +1101,7 @@ err_pm_disable:
        return ret;
 }
 
-static int fsl_esai_remove(struct platform_device *pdev)
+static void fsl_esai_remove(struct platform_device *pdev)
 {
        struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
 
@@ -1110,8 +1110,6 @@ static int fsl_esai_remove(struct platform_device *pdev)
                fsl_esai_runtime_suspend(&pdev->dev);
 
        cancel_work_sync(&esai_priv->work);
-
-       return 0;
 }
 
 static const struct of_device_id fsl_esai_dt_ids[] = {
@@ -1200,7 +1198,7 @@ static const struct dev_pm_ops fsl_esai_pm_ops = {
 
 static struct platform_driver fsl_esai_driver = {
        .probe = fsl_esai_probe,
-       .remove = fsl_esai_remove,
+       .remove_new = fsl_esai_remove,
        .driver = {
                .name = "fsl-esai-dai",
                .pm = &fsl_esai_pm_ops,
index 4922e6795b73f02a8d53a33f0fad079e4ccd0739..49ae7f6267d3712726a141634be70aeb5c38029f 100644 (file)
@@ -210,10 +210,10 @@ static int fsl_mqs_probe(struct platform_device *pdev)
                }
 
                mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
+               of_node_put(gpr_np);
                if (IS_ERR(mqs_priv->regmap)) {
                        dev_err(&pdev->dev, "failed to get gpr regmap\n");
-                       ret = PTR_ERR(mqs_priv->regmap);
-                       goto err_free_gpr_np;
+                       return PTR_ERR(mqs_priv->regmap);
                }
        } else {
                regs = devm_platform_ioremap_resource(pdev, 0);
@@ -242,8 +242,7 @@ static int fsl_mqs_probe(struct platform_device *pdev)
        if (IS_ERR(mqs_priv->mclk)) {
                dev_err(&pdev->dev, "failed to get the clock: %ld\n",
                        PTR_ERR(mqs_priv->mclk));
-               ret = PTR_ERR(mqs_priv->mclk);
-               goto err_free_gpr_np;
+               return PTR_ERR(mqs_priv->mclk);
        }
 
        dev_set_drvdata(&pdev->dev, mqs_priv);
@@ -252,19 +251,14 @@ static int fsl_mqs_probe(struct platform_device *pdev)
        ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
                        &fsl_mqs_dai, 1);
        if (ret)
-               goto err_free_gpr_np;
-       return 0;
-
-err_free_gpr_np:
-       of_node_put(gpr_np);
+               return ret;
 
-       return ret;
+       return 0;
 }
 
-static int fsl_mqs_remove(struct platform_device *pdev)
+static void fsl_mqs_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -360,7 +354,7 @@ MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
 
 static struct platform_driver fsl_mqs_driver = {
        .probe          = fsl_mqs_probe,
-       .remove         = fsl_mqs_remove,
+       .remove_new     = fsl_mqs_remove,
        .driver         = {
                .name   = "fsl-mqs",
                .of_match_table = fsl_mqs_dt_ids,
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
new file mode 100644 (file)
index 0000000..7cbb8e4
--- /dev/null
@@ -0,0 +1,735 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC using the QUICC Multichannel Controller (QMC)
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <soc/fsl/qe/qmc.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct qmc_dai {
+       char *name;
+       int id;
+       struct device *dev;
+       struct qmc_chan *qmc_chan;
+       unsigned int nb_tx_ts;
+       unsigned int nb_rx_ts;
+};
+
+struct qmc_audio {
+       struct device *dev;
+       unsigned int num_dais;
+       struct qmc_dai *dais;
+       struct snd_soc_dai_driver *dai_drivers;
+};
+
+struct qmc_dai_prtd {
+       struct qmc_dai *qmc_dai;
+       dma_addr_t dma_buffer_start;
+       dma_addr_t period_ptr_submitted;
+       dma_addr_t period_ptr_ended;
+       dma_addr_t dma_buffer_end;
+       size_t period_size;
+       struct snd_pcm_substream *substream;
+};
+
+static int qmc_audio_pcm_construct(struct snd_soc_component *component,
+                                  struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_card *card = rtd->card->snd_card;
+       int ret;
+
+       ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+       if (ret)
+               return ret;
+
+       snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+                                      64*1024, 64*1024);
+       return 0;
+}
+
+static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
+                                  struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+       prtd->dma_buffer_start = runtime->dma_addr;
+       prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params);
+       prtd->period_size = params_period_bytes(params);
+       prtd->period_ptr_submitted = prtd->dma_buffer_start;
+       prtd->period_ptr_ended = prtd->dma_buffer_start;
+       prtd->substream = substream;
+
+       return 0;
+}
+
+static void qmc_audio_pcm_write_complete(void *context)
+{
+       struct qmc_dai_prtd *prtd = context;
+       int ret;
+
+       prtd->period_ptr_ended += prtd->period_size;
+       if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
+               prtd->period_ptr_ended = prtd->dma_buffer_start;
+
+       prtd->period_ptr_submitted += prtd->period_size;
+       if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
+               prtd->period_ptr_submitted = prtd->dma_buffer_start;
+
+       ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+               prtd->period_ptr_submitted, prtd->period_size,
+               qmc_audio_pcm_write_complete, prtd);
+       if (ret) {
+               dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
+                       ret);
+       }
+
+       snd_pcm_period_elapsed(prtd->substream);
+}
+
+static void qmc_audio_pcm_read_complete(void *context, size_t length)
+{
+       struct qmc_dai_prtd *prtd = context;
+       int ret;
+
+       if (length != prtd->period_size) {
+               dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
+                       length, prtd->period_size);
+       }
+
+       prtd->period_ptr_ended += prtd->period_size;
+       if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
+               prtd->period_ptr_ended = prtd->dma_buffer_start;
+
+       prtd->period_ptr_submitted += prtd->period_size;
+       if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
+               prtd->period_ptr_submitted = prtd->dma_buffer_start;
+
+       ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+               prtd->period_ptr_submitted, prtd->period_size,
+               qmc_audio_pcm_read_complete, prtd);
+       if (ret) {
+               dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
+                       ret);
+       }
+
+       snd_pcm_period_elapsed(prtd->substream);
+}
+
+static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
+                                struct snd_pcm_substream *substream, int cmd)
+{
+       struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+       int ret;
+
+       if (!prtd->qmc_dai) {
+               dev_err(component->dev, "qmc_dai is not set\n");
+               return -EINVAL;
+       }
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /* Submit first chunk ... */
+                       ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+                               prtd->period_ptr_submitted, prtd->period_size,
+                               qmc_audio_pcm_write_complete, prtd);
+                       if (ret) {
+                               dev_err(component->dev, "write_submit failed %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       /* ... prepare next one ... */
+                       prtd->period_ptr_submitted += prtd->period_size;
+                       if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
+                               prtd->period_ptr_submitted = prtd->dma_buffer_start;
+
+                       /* ... and send it */
+                       ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+                               prtd->period_ptr_submitted, prtd->period_size,
+                               qmc_audio_pcm_write_complete, prtd);
+                       if (ret) {
+                               dev_err(component->dev, "write_submit failed %d\n",
+                                       ret);
+                               return ret;
+                       }
+               } else {
+                       /* Submit first chunk ... */
+                       ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+                               prtd->period_ptr_submitted, prtd->period_size,
+                               qmc_audio_pcm_read_complete, prtd);
+                       if (ret) {
+                               dev_err(component->dev, "read_submit failed %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       /* ... prepare next one ... */
+                       prtd->period_ptr_submitted += prtd->period_size;
+                       if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
+                               prtd->period_ptr_submitted = prtd->dma_buffer_start;
+
+                       /* ... and send it */
+                       ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+                               prtd->period_ptr_submitted, prtd->period_size,
+                               qmc_audio_pcm_read_complete, prtd);
+                       if (ret) {
+                               dev_err(component->dev, "write_submit failed %d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+               break;
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *component,
+                                              struct snd_pcm_substream *substream)
+{
+       struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+       return bytes_to_frames(substream->runtime,
+                              prtd->period_ptr_ended - prtd->dma_buffer_start);
+}
+
+static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
+                                       const struct of_phandle_args *args,
+                                       const char **dai_name)
+{
+       struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev);
+       struct snd_soc_dai_driver *dai_driver;
+       int id = args->args[0];
+       int i;
+
+       for (i = 0; i  < qmc_audio->num_dais; i++) {
+               dai_driver = qmc_audio->dai_drivers + i;
+               if (dai_driver->id == id) {
+                       *dai_name = dai_driver->name;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_PAUSE,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 64*1024,
+       .periods_min            = 2,
+       .periods_max            = 2*1024,
+       .buffer_bytes_max       = 64*1024,
+};
+
+static int qmc_audio_pcm_open(struct snd_soc_component *component,
+                             struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct qmc_dai_prtd *prtd;
+       int ret;
+
+       snd_soc_set_runtime_hwparams(substream, &qmc_audio_pcm_hardware);
+
+       /* ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               return ret;
+
+       prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+       if (prtd == NULL)
+               return -ENOMEM;
+
+       runtime->private_data = prtd;
+
+       return 0;
+}
+
+static int qmc_audio_pcm_close(struct snd_soc_component *component,
+                              struct snd_pcm_substream *substream)
+{
+       struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+       kfree(prtd);
+       return 0;
+}
+
+static const struct snd_soc_component_driver qmc_audio_soc_platform = {
+       .open                   = qmc_audio_pcm_open,
+       .close                  = qmc_audio_pcm_close,
+       .hw_params              = qmc_audio_pcm_hw_params,
+       .trigger                = qmc_audio_pcm_trigger,
+       .pointer                = qmc_audio_pcm_pointer,
+       .pcm_construct          = qmc_audio_pcm_construct,
+       .of_xlate_dai_name      = qmc_audio_of_xlate_dai_name,
+};
+
+static unsigned int qmc_dai_get_index(struct snd_soc_dai *dai)
+{
+       struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
+
+       return dai->driver - qmc_audio->dai_drivers;
+}
+
+static struct qmc_dai *qmc_dai_get_data(struct snd_soc_dai *dai)
+{
+       struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
+       unsigned int index;
+
+       index = qmc_dai_get_index(dai);
+       if (index > qmc_audio->num_dais)
+               return NULL;
+
+       return qmc_audio->dais + index;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_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};
+
+       switch (snd_pcm_format_physical_width(format)) {
+       case 8:
+               ch.max = nb_ts;
+               break;
+       case 16:
+               ch.max = nb_ts/2;
+               break;
+       case 32:
+               ch.max = nb_ts/4;
+               break;
+       case 64:
+               ch.max = nb_ts/8;
+               break;
+       default:
+               dev_err(qmc_dai->dev, "format physical width %u not supported\n",
+                       snd_pcm_format_physical_width(format));
+               return -EINVAL;
+       }
+
+       ch.min = ch.max ? 1 : 0;
+
+       return snd_interval_refine(c, &ch);
+}
+
+static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+                                                      struct snd_pcm_hw_rule *rule)
+{
+       struct qmc_dai *qmc_dai = rule->private;
+
+       return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts);
+}
+
+static int qmc_dai_hw_rule_capture_channels_by_format(
+                       struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct qmc_dai *qmc_dai = rule->private;
+
+       return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_rx_ts);
+}
+
+static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_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;
+       struct snd_mask f_new;
+       unsigned int i;
+
+       if (!channels || channels > nb_ts) {
+               dev_err(qmc_dai->dev, "channels %u not supported\n",
+                       nb_ts);
+               return -EINVAL;
+       }
+
+       slot_width = (nb_ts / channels) * 8;
+
+       snd_mask_none(&f_new);
+       for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+               if (snd_mask_test(f_old, i)) {
+                       if (snd_pcm_format_physical_width(i) <= slot_width)
+                               snd_mask_set(&f_new, i);
+               }
+       }
+
+       return snd_mask_refine(f_old, &f_new);
+}
+
+static int qmc_dai_hw_rule_playback_format_by_channels(
+                       struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct qmc_dai *qmc_dai = rule->private;
+
+       return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts);
+}
+
+static int qmc_dai_hw_rule_capture_format_by_channels(
+                       struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct qmc_dai *qmc_dai = rule->private;
+
+       return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts);
+}
+
+static int qmc_dai_startup(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+       snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+       snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+       struct qmc_dai *qmc_dai;
+       unsigned int frame_bits;
+       int ret;
+
+       qmc_dai = qmc_dai_get_data(dai);
+       if (!qmc_dai) {
+               dev_err(dai->dev, "Invalid dai\n");
+               return -EINVAL;
+       }
+
+       prtd->qmc_dai = qmc_dai;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
+               hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
+               frame_bits = qmc_dai->nb_rx_ts * 8;
+       } else {
+               hw_rule_channels_by_format = qmc_dai_hw_rule_playback_channels_by_format;
+               hw_rule_format_by_channels = qmc_dai_hw_rule_playback_format_by_channels;
+               frame_bits = qmc_dai->nb_tx_ts * 8;
+       }
+
+       ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels_by_format, qmc_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, qmc_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 int qmc_dai_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct qmc_chan_param chan_param = {0};
+       struct qmc_dai *qmc_dai;
+       int ret;
+
+       qmc_dai = qmc_dai_get_data(dai);
+       if (!qmc_dai) {
+               dev_err(dai->dev, "Invalid dai\n");
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               chan_param.mode = QMC_TRANSPARENT;
+               chan_param.transp.max_rx_buf_size = params_period_bytes(params);
+               ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param);
+               if (ret) {
+                       dev_err(dai->dev, "set param failed %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
+{
+       struct qmc_dai *qmc_dai;
+       int direction;
+       int ret;
+
+       qmc_dai = qmc_dai_get_data(dai);
+       if (!qmc_dai) {
+               dev_err(dai->dev, "Invalid dai\n");
+               return -EINVAL;
+       }
+
+       direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                   QMC_CHAN_WRITE : QMC_CHAN_READ;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = qmc_chan_start(qmc_dai->qmc_chan, direction);
+               if (ret)
+                       return ret;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
+               if (ret)
+                       return ret;
+               ret = qmc_chan_reset(qmc_dai->qmc_chan, direction);
+               if (ret)
+                       return ret;
+               break;
+
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
+               if (ret)
+                       return ret;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops qmc_dai_ops = {
+       .startup        = qmc_dai_startup,
+       .trigger        = qmc_dai_trigger,
+       .hw_params      = qmc_dai_hw_params,
+};
+
+static u64 qmc_audio_formats(u8 nb_ts)
+{
+       u64 formats;
+       unsigned int chan_width;
+       unsigned int format_width;
+       int i;
+
+       if (!nb_ts)
+               return 0;
+
+       formats = 0;
+       chan_width = nb_ts * 8;
+       for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+               /*
+                * Support format other than little-endian (ie big-endian or
+                * without endianness such as 8bit formats)
+                */
+               if (snd_pcm_format_little_endian(i) == 1)
+                       continue;
+
+               /* Support physical width multiple of 8bit */
+               format_width = snd_pcm_format_physical_width(i);
+               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 |= (1ULL << i);
+       }
+       return formats;
+}
+
+static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
+       struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver)
+{
+       struct qmc_chan_info info;
+       u32 val;
+       int ret;
+
+       qmc_dai->dev = qmc_audio->dev;
+
+       ret = of_property_read_u32(np, "reg", &val);
+       if (ret) {
+               dev_err(qmc_audio->dev, "%pOF: failed to read reg\n", np);
+               return ret;
+       }
+       qmc_dai->id = val;
+
+       qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d",
+                                      np->parent->name, qmc_dai->id);
+
+       qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
+                                                       "fsl,qmc-chan");
+       if (IS_ERR(qmc_dai->qmc_chan)) {
+               ret = PTR_ERR(qmc_dai->qmc_chan);
+               return dev_err_probe(qmc_audio->dev, ret,
+                                    "dai %d get QMC channel failed\n", qmc_dai->id);
+       }
+
+       qmc_soc_dai_driver->id = qmc_dai->id;
+       qmc_soc_dai_driver->name = qmc_dai->name;
+
+       ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info);
+       if (ret) {
+               dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
+                       qmc_dai->id, ret);
+               return ret;
+       }
+       dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
+                qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+
+       if (info.mode != QMC_TRANSPARENT) {
+               dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
+                       qmc_dai->id, info.mode);
+               return -EINVAL;
+       }
+       qmc_dai->nb_tx_ts = info.nb_tx_ts;
+       qmc_dai->nb_rx_ts = info.nb_rx_ts;
+
+       qmc_soc_dai_driver->playback.channels_min = 0;
+       qmc_soc_dai_driver->playback.channels_max = 0;
+       if (qmc_dai->nb_tx_ts) {
+               qmc_soc_dai_driver->playback.channels_min = 1;
+               qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
+       }
+       qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts);
+
+       qmc_soc_dai_driver->capture.channels_min = 0;
+       qmc_soc_dai_driver->capture.channels_max = 0;
+       if (qmc_dai->nb_rx_ts) {
+               qmc_soc_dai_driver->capture.channels_min = 1;
+               qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
+       }
+       qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts);
+
+       qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate);
+       qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
+       qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
+       qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate);
+       qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
+       qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
+
+       qmc_soc_dai_driver->ops = &qmc_dai_ops;
+
+       return 0;
+}
+
+static int qmc_audio_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct qmc_audio *qmc_audio;
+       struct device_node *child;
+       unsigned int i;
+       int ret;
+
+       qmc_audio = devm_kzalloc(&pdev->dev, sizeof(*qmc_audio), GFP_KERNEL);
+       if (!qmc_audio)
+               return -ENOMEM;
+
+       qmc_audio->dev = &pdev->dev;
+
+       qmc_audio->num_dais = of_get_available_child_count(np);
+       if (qmc_audio->num_dais) {
+               qmc_audio->dais = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
+                                              sizeof(*qmc_audio->dais),
+                                              GFP_KERNEL);
+               if (!qmc_audio->dais)
+                       return -ENOMEM;
+
+               qmc_audio->dai_drivers = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
+                                                     sizeof(*qmc_audio->dai_drivers),
+                                                     GFP_KERNEL);
+               if (!qmc_audio->dai_drivers)
+                       return -ENOMEM;
+       }
+
+       i = 0;
+       for_each_available_child_of_node(np, child) {
+               ret = qmc_audio_dai_parse(qmc_audio, child,
+                                         qmc_audio->dais + i,
+                                         qmc_audio->dai_drivers + i);
+               if (ret) {
+                       of_node_put(child);
+                       return ret;
+               }
+               i++;
+       }
+
+
+       platform_set_drvdata(pdev, qmc_audio);
+
+       ret = devm_snd_soc_register_component(qmc_audio->dev,
+                                             &qmc_audio_soc_platform,
+                                             qmc_audio->dai_drivers,
+                                             qmc_audio->num_dais);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static const struct of_device_id qmc_audio_id_table[] = {
+       { .compatible = "fsl,qmc-audio" },
+       {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, qmc_audio_id_table);
+
+static struct platform_driver qmc_audio_driver = {
+       .driver = {
+               .name = "fsl-qmc-audio",
+               .of_match_table = of_match_ptr(qmc_audio_id_table),
+       },
+       .probe = qmc_audio_probe,
+};
+module_platform_driver(qmc_audio_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("CPM/QE QMC audio driver");
+MODULE_LICENSE("GPL");
index 46c7868a2653763d2f4e256defe9369c315cbe53..15b48b5ea856599d849c53c26e77cade36e39a96 100644 (file)
@@ -247,14 +247,12 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int fsl_rpmsg_remove(struct platform_device *pdev)
+static void fsl_rpmsg_remove(struct platform_device *pdev)
 {
        struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
 
        if (rpmsg->card_pdev)
                platform_device_unregister(rpmsg->card_pdev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -302,7 +300,7 @@ static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
 
 static struct platform_driver fsl_rpmsg_driver = {
        .probe  = fsl_rpmsg_probe,
-       .remove = fsl_rpmsg_remove,
+       .remove_new = fsl_rpmsg_remove,
        .driver = {
                .name = "fsl_rpmsg",
                .pm = &fsl_rpmsg_pm_ops,
index 990bba0be1fb1cb2b8edae47ce9347f27250a6e9..abdaffb00fbdc978509538b3b667d0c2fad78c73 100644 (file)
@@ -1380,18 +1380,18 @@ static int fsl_sai_probe(struct platform_device *pdev)
        sai->cpu_dai_drv.symmetric_channels = 1;
        sai->cpu_dai_drv.symmetric_sample_bits = 1;
 
-       if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
-           of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+       if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
+           of_property_read_bool(np, "fsl,sai-asynchronous")) {
                /* error out if both synchronous and asynchronous are present */
                dev_err(dev, "invalid binding for synchronous mode\n");
                return -EINVAL;
        }
 
-       if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) {
+       if (of_property_read_bool(np, "fsl,sai-synchronous-rx")) {
                /* Sync Rx with Tx */
                sai->synchronous[RX] = false;
                sai->synchronous[TX] = true;
-       } else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+       } else if (of_property_read_bool(np, "fsl,sai-asynchronous")) {
                /* Discard all settings for asynchronous mode */
                sai->synchronous[RX] = false;
                sai->synchronous[TX] = false;
@@ -1400,7 +1400,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
                sai->cpu_dai_drv.symmetric_sample_bits = 0;
        }
 
-       if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+       if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") &&
            of_device_is_compatible(np, "fsl,imx6ul-sai")) {
                gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
                if (IS_ERR(gpr)) {
@@ -1443,7 +1443,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
                dev_warn(dev, "Error reading SAI version: %d\n", ret);
 
        /* Select MCLK direction */
-       if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+       if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") &&
            sai->soc_data->max_register >= FSL_SAI_MCTL) {
                regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
                                   FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
@@ -1489,13 +1489,11 @@ err_pm_disable:
        return ret;
 }
 
-static int fsl_sai_remove(struct platform_device *pdev)
+static void fsl_sai_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                fsl_sai_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
@@ -1696,7 +1694,7 @@ static const struct dev_pm_ops fsl_sai_pm_ops = {
 
 static struct platform_driver fsl_sai_driver = {
        .probe = fsl_sai_probe,
-       .remove = fsl_sai_remove,
+       .remove_new = fsl_sai_remove,
        .driver = {
                .name = "fsl-sai",
                .pm = &fsl_sai_pm_ops,
index 275aba8e0c46959cc6d4afa77469801b41a7bb66..015c3708aa04e1564d07548ed0908b9a6582b164 100644 (file)
@@ -1659,11 +1659,9 @@ err_pm_disable:
        return ret;
 }
 
-static int fsl_spdif_remove(struct platform_device *pdev)
+static void fsl_spdif_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1765,7 +1763,7 @@ static struct platform_driver fsl_spdif_driver = {
                .pm = &fsl_spdif_pm,
        },
        .probe = fsl_spdif_probe,
-       .remove = fsl_spdif_remove,
+       .remove_new = fsl_spdif_remove,
 };
 
 module_platform_driver(fsl_spdif_driver);
index 46a53551b955c6c33415df20d2fcb8f576e90a43..53ed3701b0b0e83d578f551056247d0d017cba3d 100644 (file)
@@ -1447,7 +1447,7 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
                        return -EINVAL;
                }
                strcpy(ssi->card_name, "ac97-codec");
-       } else if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
+       } else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) {
                /*
                 * In synchronous mode, STCK and STFS ports are used by RX
                 * as well. So the software should limit the sample rates,
@@ -1671,7 +1671,7 @@ error_ac97_ops:
        return ret;
 }
 
-static int fsl_ssi_remove(struct platform_device *pdev)
+static void fsl_ssi_remove(struct platform_device *pdev)
 {
        struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
 
@@ -1690,8 +1690,6 @@ static int fsl_ssi_remove(struct platform_device *pdev)
                snd_soc_set_ac97_ops(NULL);
                mutex_destroy(&ssi->ac97_reg_lock);
        }
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1737,7 +1735,7 @@ static struct platform_driver fsl_ssi_driver = {
                .pm = &fsl_ssi_pm,
        },
        .probe = fsl_ssi_probe,
-       .remove = fsl_ssi_remove,
+       .remove_new = fsl_ssi_remove,
 };
 
 module_platform_driver(fsl_ssi_driver);
index 2a78243df75263fb7500a80da3ebbcc6c0d43c9d..318fe77683f56bed3dc706a71804234a92b0d3bf 100644 (file)
@@ -1339,10 +1339,9 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int fsl_xcvr_remove(struct platform_device *pdev)
+static void fsl_xcvr_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
@@ -1478,7 +1477,7 @@ static struct platform_driver fsl_xcvr_driver = {
                .pm = &fsl_xcvr_pm_ops,
                .of_match_table = fsl_xcvr_dt_ids,
        },
-       .remove = fsl_xcvr_remove,
+       .remove_new = fsl_xcvr_remove,
 };
 module_platform_driver(fsl_xcvr_driver);
 
index 1292a845c42446797de09105d49d953998d9752c..b2c5aca92c6bf43fb8690e27beff88c25ae15352 100644 (file)
@@ -207,8 +207,8 @@ static int imx_audmix_probe(struct platform_device *pdev)
        for (i = 0; i < num_dai; i++) {
                struct snd_soc_dai_link_component *dlc;
 
-               /* for CPU/Codec/Platform x 2 */
-               dlc = devm_kcalloc(&pdev->dev, 6, sizeof(*dlc), GFP_KERNEL);
+               /* for CPU/Codec x 2 */
+               dlc = devm_kcalloc(&pdev->dev, 4, sizeof(*dlc), GFP_KERNEL);
                if (!dlc)
                        return -ENOMEM;
 
@@ -238,9 +238,13 @@ static int imx_audmix_probe(struct platform_device *pdev)
                                               dai_name, "CPU-Capture");
                }
 
-               priv->dai[i].cpus = &dlc[0];
-               priv->dai[i].codecs = &dlc[1];
-               priv->dai[i].platforms = &dlc[2];
+               /*
+                * CPU == Platform
+                * platform is using soc-generic-dmaengine-pcm
+                */
+               priv->dai[i].cpus       =
+               priv->dai[i].platforms  = &dlc[0];
+               priv->dai[i].codecs     = &dlc[1];
 
                priv->dai[i].num_cpus = 1;
                priv->dai[i].num_codecs = 1;
@@ -252,7 +256,6 @@ static int imx_audmix_probe(struct platform_device *pdev)
                priv->dai[i].codecs->name = "snd-soc-dummy";
                priv->dai[i].cpus->of_node = args.np;
                priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev);
-               priv->dai[i].platforms->of_node = args.np;
                priv->dai[i].dynamic = 1;
                priv->dai[i].dpcm_playback = 1;
                priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
@@ -267,20 +270,17 @@ static int imx_audmix_probe(struct platform_device *pdev)
                be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
                                       "AUDMIX-Capture-%d", i);
 
-               priv->dai[num_dai + i].cpus = &dlc[3];
-               priv->dai[num_dai + i].codecs = &dlc[4];
-               priv->dai[num_dai + i].platforms = &dlc[5];
+               priv->dai[num_dai + i].cpus     = &dlc[2];
+               priv->dai[num_dai + i].codecs   = &dlc[3];
 
                priv->dai[num_dai + i].num_cpus = 1;
                priv->dai[num_dai + i].num_codecs = 1;
-               priv->dai[num_dai + i].num_platforms = 1;
 
                priv->dai[num_dai + i].name = be_name;
                priv->dai[num_dai + i].codecs->dai_name = "snd-soc-dummy-dai";
                priv->dai[num_dai + i].codecs->name = "snd-soc-dummy";
                priv->dai[num_dai + i].cpus->of_node = audmix_np;
                priv->dai[num_dai + i].cpus->dai_name = be_name;
-               priv->dai[num_dai + i].platforms->name = "snd-soc-dummy";
                priv->dai[num_dai + i].no_pcm = 1;
                priv->dai[num_dai + i].dpcm_playback = 1;
                priv->dai[num_dai + i].dpcm_capture  = 1;
index 582f1e2431eee3b4db2c238715732c48644139a2..be003a117b39f878f5310143d901f05e836295c5 100644 (file)
@@ -315,12 +315,10 @@ static int imx_audmux_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int imx_audmux_remove(struct platform_device *pdev)
+static void imx_audmux_remove(struct platform_device *pdev)
 {
        if (audmux_type == IMX31_AUDMUX)
                audmux_debugfs_remove();
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -359,7 +357,7 @@ static const struct dev_pm_ops imx_audmux_pm = {
 
 static struct platform_driver imx_audmux_driver = {
        .probe          = imx_audmux_probe,
-       .remove         = imx_audmux_remove,
+       .remove_new     = imx_audmux_remove,
        .driver = {
                .name   = DRIVER_NAME,
                .pm = &imx_audmux_pm,
index 3f128ced418098521355fad94f02350041736256..64a4d7e9db60313102c1ec2e971251054be52253 100644 (file)
@@ -563,7 +563,7 @@ static int imx_card_parse_of(struct imx_card_data *data)
                        link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1;
 
                        /* sai may support mclk/bclk = 1 */
-                       if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) {
+                       if (of_property_read_bool(np, "fsl,mclk-equal-bclk")) {
                                link_data->one2one_ratio = true;
                        } else {
                                int i;
index b80c57362fb8109486980343ca571be876efba26..85bd36fb68a2290180f539723b7ed1251fa057a7 100644 (file)
@@ -149,7 +149,7 @@ static int imx_es8328_probe(struct platform_device *pdev)
                goto put_device;
        }
 
-       comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
+       comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
        if (!comp) {
                ret = -ENOMEM;
                goto put_device;
@@ -159,9 +159,13 @@ static int imx_es8328_probe(struct platform_device *pdev)
 
        data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
 
-       data->dai.cpus          = &comp[0];
+       /*
+        * CPU == Platform
+        * platform is using soc-generic-dmaengine-pcm
+        */
+       data->dai.cpus          =
+       data->dai.platforms     = &comp[0];
        data->dai.codecs        = &comp[1];
-       data->dai.platforms     = &comp[2];
 
        data->dai.num_cpus      = 1;
        data->dai.num_codecs    = 1;
@@ -172,7 +176,6 @@ static int imx_es8328_probe(struct platform_device *pdev)
        data->dai.codecs->dai_name = "es8328-hifi-analog";
        data->dai.codecs->of_node = codec_np;
        data->dai.cpus->of_node = ssi_np;
-       data->dai.platforms->of_node = ssi_np;
        data->dai.init = &imx_es8328_dai_init;
        data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                            SND_SOC_DAIFMT_CBP_CFP;
index 6614b3447649b7e76da3a8253d740e1ad0786615..765dad607bf6133f1123b87c6145509ef99702e3 100644 (file)
@@ -743,14 +743,12 @@ fail:
        return ret;
 }
 
-static int imx_rpmsg_pcm_remove(struct platform_device *pdev)
+static void imx_rpmsg_pcm_remove(struct platform_device *pdev)
 {
        struct rpmsg_info *info = platform_get_drvdata(pdev);
 
        if (info->rpmsg_wq)
                destroy_workqueue(info->rpmsg_wq);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -821,7 +819,7 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
 
 static struct platform_driver imx_pcm_rpmsg_driver = {
        .probe  = imx_rpmsg_pcm_probe,
-       .remove = imx_rpmsg_pcm_remove,
+       .remove_new = imx_rpmsg_pcm_remove,
        .driver = {
                .name = IMX_PCM_DRV_NAME,
                .pm = &imx_rpmsg_pcm_pm_ops,
index 580a0d963f0ebc8a01fcd4315dae8a0920fd2c73..26c22783927b8f0c5a6b47ecaf4a937adee38868 100644 (file)
@@ -193,14 +193,12 @@ fail:
        return ret;
 }
 
-static int imx_sgtl5000_remove(struct platform_device *pdev)
+static void imx_sgtl5000_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(card);
 
        clk_put(data->codec_clk);
-
-       return 0;
 }
 
 static const struct of_device_id imx_sgtl5000_dt_ids[] = {
@@ -216,7 +214,7 @@ static struct platform_driver imx_sgtl5000_driver = {
                .of_match_table = imx_sgtl5000_dt_ids,
        },
        .probe = imx_sgtl5000_probe,
-       .remove = imx_sgtl5000_remove,
+       .remove_new = imx_sgtl5000_remove,
 };
 module_platform_driver(imx_sgtl5000_driver);
 
index 4446fba755b9afbac7b8a1cc254a65e54419aa7a..ab978431ac98c91115e0a430d128ffb45d761872 100644 (file)
@@ -26,15 +26,19 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
        }
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
-       comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+       comp = devm_kzalloc(&pdev->dev, 2 * sizeof(*comp), GFP_KERNEL);
        if (!data || !comp) {
                ret = -ENOMEM;
                goto end;
        }
 
-       data->dai.cpus          = &comp[0];
+       /*
+        * CPU == Platform
+        * platform is using soc-generic-dmaengine-pcm
+        */
+       data->dai.cpus          =
+       data->dai.platforms     = &comp[0];
        data->dai.codecs        = &comp[1];
-       data->dai.platforms     = &comp[2];
 
        data->dai.num_cpus      = 1;
        data->dai.num_codecs    = 1;
@@ -45,7 +49,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
        data->dai.codecs->dai_name = "snd-soc-dummy-dai";
        data->dai.codecs->name = "snd-soc-dummy";
        data->dai.cpus->of_node = spdif_np;
-       data->dai.platforms->of_node = spdif_np;
        data->dai.playback_only = true;
        data->dai.capture_only = true;
 
index a082ae636a4fe4d65d5033afe892ba9f41285278..40a4a2667394606a98d16278184d1046d777a6e1 100644 (file)
@@ -311,12 +311,11 @@ static int psc_ac97_of_probe(struct platform_device *op)
        return 0;
 }
 
-static int psc_ac97_of_remove(struct platform_device *op)
+static void psc_ac97_of_remove(struct platform_device *op)
 {
        mpc5200_audio_dma_destroy(op);
        snd_soc_unregister_component(&op->dev);
        snd_soc_set_ac97_ops(NULL);
-       return 0;
 }
 
 /* Match table for of_platform binding */
@@ -329,7 +328,7 @@ MODULE_DEVICE_TABLE(of, psc_ac97_match);
 
 static struct platform_driver psc_ac97_driver = {
        .probe = psc_ac97_of_probe,
-       .remove = psc_ac97_of_remove,
+       .remove_new = psc_ac97_of_remove,
        .driver = {
                .name = "mpc5200-psc-ac97",
                .of_match_table = psc_ac97_match,
index 73f3e61f208a7a579478b5b268de91d9e5cb26c2..413df413b5eb1af75304791bf3cbe69a10ddd6a4 100644 (file)
@@ -210,11 +210,10 @@ static int psc_i2s_of_probe(struct platform_device *op)
 
 }
 
-static int psc_i2s_of_remove(struct platform_device *op)
+static void psc_i2s_of_remove(struct platform_device *op)
 {
        mpc5200_audio_dma_destroy(op);
        snd_soc_unregister_component(&op->dev);
-       return 0;
 }
 
 /* Match table for of_platform binding */
@@ -227,7 +226,7 @@ MODULE_DEVICE_TABLE(of, psc_i2s_match);
 
 static struct platform_driver psc_i2s_driver = {
        .probe = psc_i2s_of_probe,
-       .remove = psc_i2s_of_remove,
+       .remove_new = psc_i2s_of_remove,
        .driver = {
                .name = "mpc5200-psc-i2s",
                .of_match_table = psc_i2s_match,
index e71a992fbf93c46d39cfef327b705362120a3220..ea2076ea8afe4784d0e371095bc174ff734ede2c 100644 (file)
@@ -387,7 +387,7 @@ error_alloc:
  *
  * This function is called when the platform device is removed.
  */
-static int mpc8610_hpcd_remove(struct platform_device *pdev)
+static void mpc8610_hpcd_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct mpc8610_hpcd_data *machine_data =
@@ -395,13 +395,11 @@ static int mpc8610_hpcd_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
        kfree(machine_data);
-
-       return 0;
 }
 
 static struct platform_driver mpc8610_hpcd_driver = {
        .probe = mpc8610_hpcd_probe,
-       .remove = mpc8610_hpcd_remove,
+       .remove_new = mpc8610_hpcd_remove,
        .driver = {
                /* The name must match 'compatible' property in the device tree,
                 * in lowercase letters.
index b45742931b0d116ceb2c1f815ea19d34da81fd45..0b1418abeb9c3686bd14efd804b02fbc000e4803 100644 (file)
@@ -396,7 +396,7 @@ error_put:
  *
  * This function is called when the platform device is removed.
  */
-static int p1022_ds_remove(struct platform_device *pdev)
+static void p1022_ds_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct machine_data *mdata =
@@ -404,13 +404,11 @@ static int p1022_ds_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
        kfree(mdata);
-
-       return 0;
 }
 
 static struct platform_driver p1022_ds_driver = {
        .probe = p1022_ds_probe,
-       .remove = p1022_ds_remove,
+       .remove_new = p1022_ds_remove,
        .driver = {
                /*
                 * The name must match 'compatible' property in the device tree,
index b395adabe8235c030a59c6a90a69dc9707052acf..4d85b742114c58a53fa51f1c4712f7559e0ac099 100644 (file)
@@ -345,7 +345,7 @@ error_put:
  *
  * This function is called when the platform device is removed.
  */
-static int p1022_rdk_remove(struct platform_device *pdev)
+static void p1022_rdk_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct machine_data *mdata =
@@ -353,13 +353,11 @@ static int p1022_rdk_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
        kfree(mdata);
-
-       return 0;
 }
 
 static struct platform_driver p1022_rdk_driver = {
        .probe = p1022_rdk_probe,
-       .remove = p1022_rdk_remove,
+       .remove_new = p1022_rdk_remove,
        .driver = {
                /*
                 * The name must match 'compatible' property in the device tree,
index 997c3e66c636532589687412e26c6f61dd443d81..d24c02e908783e1937e1babe4c42cfe2a9f5a0a3 100644 (file)
@@ -109,14 +109,12 @@ static int pcm030_fabric_probe(struct platform_device *op)
 
 }
 
-static int pcm030_fabric_remove(struct platform_device *op)
+static void pcm030_fabric_remove(struct platform_device *op)
 {
        struct pcm030_audio_data *pdata = platform_get_drvdata(op);
 
        snd_soc_unregister_card(pdata->card);
        platform_device_unregister(pdata->codec_device);
-
-       return 0;
 }
 
 static const struct of_device_id pcm030_audio_match[] = {
@@ -127,7 +125,7 @@ MODULE_DEVICE_TABLE(of, pcm030_audio_match);
 
 static struct platform_driver pcm030_fabric_driver = {
        .probe          = pcm030_fabric_probe,
-       .remove         = pcm030_fabric_remove,
+       .remove_new     = pcm030_fabric_remove,
        .driver         = {
                .name   = DRV_NAME,
                .of_match_table    = pcm030_audio_match,
index 5daa824a4ffcf8925cd18b9406e4353413dd038b..4e85536a1b261862de6d6d0ba9f6a0759e901bb1 100644 (file)
@@ -78,7 +78,7 @@ static int graph_get_dai_id(struct device_node *ep)
                 * only of_graph_parse_endpoint().
                 * We need to check "reg" property
                 */
-               if (of_get_property(ep,   "reg", NULL))
+               if (of_property_present(ep,   "reg"))
                        return info.id;
 
                node = of_get_parent(ep);
@@ -613,10 +613,16 @@ static int graph_count_noml(struct asoc_simple_priv *priv,
                return -EINVAL;
        }
 
+       /*
+        * DON'T REMOVE platforms
+        * see
+        *      simple-card.c :: simple_count_noml()
+        */
        li->num[li->link].cpus          = 1;
-       li->num[li->link].codecs        = 1;
        li->num[li->link].platforms     = 1;
 
+       li->num[li->link].codecs        = 1;
+
        li->link += 1; /* 1xCPU-Codec */
 
        dev_dbg(dev, "Count As Normal\n");
@@ -637,6 +643,11 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv,
        }
 
        if (li->cpu) {
+               /*
+                * DON'T REMOVE platforms
+                * see
+                *      simple-card.c :: simple_count_noml()
+                */
                li->num[li->link].cpus          = 1;
                li->num[li->link].platforms     = 1;
 
index 4a2c743e286c4c7f2ad92b91b023223ed4a88ed7..a3142be9323ee38ee543337facab18113e54916f 100644 (file)
@@ -151,6 +151,9 @@ static int custom_probe(struct platform_device *pdev)
        simple_priv             = &custom_priv->simple_priv;
        simple_priv->ops        = &custom_ops; /* customize dai_link ops */
 
+       /* "audio-graph-card2-custom-sample" is too long */
+       simple_priv->snd_card.name = "card2-custom";
+
        /* use audio-graph-card2 parsing with own custom hooks */
        ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks);
        if (ret < 0)
index 06609a526b78321f44a9a6e1e9eb5cc09cf9cd09..25aa79dd55b320d252d4af848bccfeada8e4d55c 100644 (file)
@@ -376,7 +376,7 @@ static int graph_get_dai_id(struct device_node *ep)
                 * only of_graph_parse_endpoint().
                 * We need to check "reg" property
                 */
-               if (of_get_property(ep,   "reg", NULL))
+               if (of_property_present(ep,   "reg"))
                        return info.id;
 
                node = of_get_parent(ep);
@@ -920,8 +920,8 @@ int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
                c2c_conf->channels_min  =
                c2c_conf->channels_max  = 2; /* update ME */
 
-               dai_link->params        = c2c_conf;
-               dai_link->num_params    = 1;
+               dai_link->c2c_params            = c2c_conf;
+               dai_link->num_c2c_params        = 1;
        }
 
        ep0 = port_to_endpoint(port0);
@@ -1046,8 +1046,14 @@ static int graph_count_normal(struct asoc_simple_priv *priv,
         * =>           lnk: port { endpoint { .. }; };
         *      };
         */
+       /*
+        * DON'T REMOVE platforms
+        * see
+        *      simple-card.c :: simple_count_noml()
+        */
        li->num[li->link].cpus          =
        li->num[li->link].platforms     = graph_counter(cpu_port);
+
        li->num[li->link].codecs        = graph_counter(codec_port);
 
        of_node_put(cpu_ep);
@@ -1079,6 +1085,11 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv,
         */
 
        if (asoc_graph_is_ports0(lnk)) {
+               /*
+                * DON'T REMOVE platforms
+                * see
+                *      simple-card.c :: simple_count_noml()
+                */
                li->num[li->link].cpus          = graph_counter(rport); /* FE */
                li->num[li->link].platforms     = graph_counter(rport);
        } else {
@@ -1113,8 +1124,14 @@ static int graph_count_c2c(struct asoc_simple_priv *priv,
         *      };
         * };
         */
+       /*
+        * DON'T REMOVE platforms
+        * see
+        *      simple-card.c :: simple_count_noml()
+        */
        li->num[li->link].cpus          =
        li->num[li->link].platforms     = graph_counter(codec0);
+
        li->num[li->link].codecs        = graph_counter(codec1);
 
        of_node_put(ports);
index 56552a616f21ff066d09ebdf4490139f454407c1..467edd96eae5f2d4e2f54788f54f9719a1e22ee9 100644 (file)
@@ -562,12 +562,12 @@ static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
 {
        struct snd_soc_dai_link *dai_link = rtd->dai_link;
        struct snd_soc_component *component;
-       struct snd_soc_pcm_stream *params;
+       struct snd_soc_pcm_stream *c2c_params;
        struct snd_pcm_hardware hw;
        int i, ret, stream;
 
        /* Do nothing if it already has Codec2Codec settings */
-       if (dai_link->params)
+       if (dai_link->c2c_params)
                return 0;
 
        /* Do nothing if it was DPCM :: BE */
@@ -592,19 +592,19 @@ static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
                return ret;
        }
 
-       params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL);
-       if (!params)
+       c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
+       if (!c2c_params)
                return -ENOMEM;
 
-       params->formats = hw.formats;
-       params->rates = hw.rates;
-       params->rate_min = hw.rate_min;
-       params->rate_max = hw.rate_max;
-       params->channels_min = hw.channels_min;
-       params->channels_max = hw.channels_max;
+       c2c_params->formats             = hw.formats;
+       c2c_params->rates               = hw.rates;
+       c2c_params->rate_min            = hw.rate_min;
+       c2c_params->rate_max            = hw.rate_max;
+       c2c_params->channels_min        = hw.channels_min;
+       c2c_params->channels_max        = hw.channels_max;
 
-       dai_link->params = params;
-       dai_link->num_params = 1;
+       dai_link->c2c_params            = c2c_params;
+       dai_link->num_c2c_params        = 1;
 
        return 0;
 }
@@ -638,7 +638,16 @@ EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
 void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
                                       struct snd_soc_dai_link_component *cpus)
 {
-       /* Assumes platform == cpu */
+       /*
+        * Assumes Platform == CPU
+        *
+        * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
+        * are different Component, but are sharing same component->dev.
+        *
+        * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.
+        * see
+        *      simple-card.c :: simple_count_noml()
+        */
        if (!platforms->of_node)
                platforms->of_node = cpus->of_node;
 }
index e98932c167542fad2d5a0aae9071286042a3cd79..6f044cc8357ea5bc5e323134fd85e7f09313f7b9 100644 (file)
@@ -422,6 +422,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
                }
 
                of_node_put(codec);
+               of_node_put(plat);
                node = of_get_next_child(top, node);
        } while (!is_top && node);
 
@@ -509,10 +510,25 @@ static int simple_count_noml(struct asoc_simple_priv *priv,
                return -EINVAL;
        }
 
+       /*
+        * DON'T REMOVE platforms
+        *
+        * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
+        * are different Component, but are sharing same component->dev.
+        * Simple Card had been supported it without special Platform selection.
+        * We need platforms here.
+        *
+        * In case of no Platform, it will be Platform == CPU, but Platform will be
+        * ignored by snd_soc_rtd_add_component().
+        *
+        * see
+        *      simple-card-utils.c :: asoc_simple_canonicalize_platform()
+        */
        li->num[li->link].cpus          = 1;
-       li->num[li->link].codecs        = 1;
        li->num[li->link].platforms     = 1;
 
+       li->num[li->link].codecs        = 1;
+
        li->link += 1;
 
        return 0;
@@ -531,6 +547,11 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv,
        }
 
        if (li->cpu) {
+               /*
+                * DON'T REMOVE platforms
+                * see
+                *      simple_count_noml()
+                */
                li->num[li->link].cpus          = 1;
                li->num[li->link].platforms     = 1;
 
index 98c8990596a88400bad98da802c34a03a640d289..e10e5bf284323d909a2d4635f3f0802d4953a506 100644 (file)
@@ -635,11 +635,9 @@ static int test_driver_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int test_driver_remove(struct platform_device *pdev)
+static void test_driver_remove(struct platform_device *pdev)
 {
        mile_stone_x(&pdev->dev);
-
-       return 0;
 }
 
 static struct platform_driver test_driver = {
@@ -648,7 +646,7 @@ static struct platform_driver test_driver = {
                .of_match_table = test_of_match,
        },
        .probe  = test_driver_probe,
-       .remove = test_driver_remove,
+       .remove_new = test_driver_remove,
 };
 module_platform_driver(test_driver);
 
index 56bb7bbd3976ce59516e598a80223104c8eb06ab..b7ab8467b5cf7d094ddca93630230874b5b773f9 100644 (file)
@@ -532,13 +532,11 @@ err_pm_disable:
        return ret;
 }
 
-static int img_i2s_in_dev_remove(struct platform_device *pdev)
+static void img_i2s_in_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                img_i2s_in_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -609,7 +607,7 @@ static struct platform_driver img_i2s_in_driver = {
                .pm = &img_i2s_in_pm_ops
        },
        .probe = img_i2s_in_probe,
-       .remove = img_i2s_in_dev_remove
+       .remove_new = img_i2s_in_dev_remove
 };
 module_platform_driver(img_i2s_in_driver);
 
index abeff78293100468434cffcde90bfe48a1488d77..fe95ddfb840775879025a613bca362abf2ee8bc9 100644 (file)
@@ -532,13 +532,11 @@ err_pm_disable:
        return ret;
 }
 
-static int img_i2s_out_dev_remove(struct platform_device *pdev)
+static void img_i2s_out_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                img_i2s_out_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -609,7 +607,7 @@ static struct platform_driver img_i2s_out_driver = {
                .pm = &img_i2s_out_pm_ops
        },
        .probe = img_i2s_out_probe,
-       .remove = img_i2s_out_dev_remove
+       .remove_new = img_i2s_out_dev_remove
 };
 module_platform_driver(img_i2s_out_driver);
 
index 08506b05e226584bb7c746cf24ebb6d023fbb677..df1291ee2b3b36b326d5453ccdf1ab2c3234dbff 100644 (file)
@@ -282,7 +282,7 @@ err_pm_disable:
        return ret;
 }
 
-static int img_prl_out_dev_remove(struct platform_device *pdev)
+static void img_prl_out_dev_remove(struct platform_device *pdev)
 {
        struct img_prl_out *prl = platform_get_drvdata(pdev);
 
@@ -291,8 +291,6 @@ static int img_prl_out_dev_remove(struct platform_device *pdev)
                img_prl_out_suspend(&pdev->dev);
 
        clk_disable_unprepare(prl->clk_sys);
-
-       return 0;
 }
 
 static const struct of_device_id img_prl_out_of_match[] = {
@@ -313,7 +311,7 @@ static struct platform_driver img_prl_out_driver = {
                .pm = &img_prl_out_pm_ops
        },
        .probe = img_prl_out_probe,
-       .remove = img_prl_out_dev_remove
+       .remove_new = img_prl_out_dev_remove
 };
 module_platform_driver(img_prl_out_driver);
 
index 3f1d1a7e8735b7b8cf502faf75756d5290be3d9d..558062a1804ac2318b8622a32320107df1019ce7 100644 (file)
@@ -810,13 +810,11 @@ err_pm_disable:
        return ret;
 }
 
-static int img_spdif_in_dev_remove(struct platform_device *pdev)
+static void img_spdif_in_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                img_spdif_in_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -880,7 +878,7 @@ static struct platform_driver img_spdif_in_driver = {
                .pm = &img_spdif_in_pm_ops
        },
        .probe = img_spdif_in_probe,
-       .remove = img_spdif_in_dev_remove
+       .remove_new = img_spdif_in_dev_remove
 };
 module_platform_driver(img_spdif_in_driver);
 
index 983761d3fa7e6ce8af416df31772091ac39b4f52..b13e128e50d6d1970a6a02897eb2e23ab950f9f9 100644 (file)
@@ -402,13 +402,11 @@ err_pm_disable:
        return ret;
 }
 
-static int img_spdif_out_dev_remove(struct platform_device *pdev)
+static void img_spdif_out_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                img_spdif_out_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -470,7 +468,7 @@ static struct platform_driver img_spdif_out_driver = {
                .pm = &img_spdif_out_pm_ops
        },
        .probe = img_spdif_out_probe,
-       .remove = img_spdif_out_dev_remove
+       .remove_new = img_spdif_out_dev_remove
 };
 module_platform_driver(img_spdif_out_driver);
 
index e3b858643bd5d5e43941c17714b5535fd05b4e0e..da6251680e41484b02014ea266d9405bf0910257 100644 (file)
@@ -215,15 +215,13 @@ err_regulator:
        return ret;
 }
 
-static int pistachio_internal_dac_remove(struct platform_device *pdev)
+static void pistachio_internal_dac_remove(struct platform_device *pdev)
 {
        struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
        pistachio_internal_dac_pwr_off(dac);
        regulator_disable(dac->supply);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -273,7 +271,7 @@ static struct platform_driver pistachio_internal_dac_plat_driver = {
                .pm = &pistachio_internal_dac_pm_ops
        },
        .probe = pistachio_internal_dac_probe,
-       .remove = pistachio_internal_dac_remove
+       .remove_new = pistachio_internal_dac_remove
 };
 module_platform_driver(pistachio_internal_dac_plat_driver);
 
index c75616a5fd0abc150b9efc3c17b38932e9c82916..ba4597bdf32e72475f01397a59256ca489354447 100644 (file)
@@ -741,10 +741,9 @@ static int sst_platform_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int sst_platform_remove(struct platform_device *pdev)
+static void sst_platform_remove(struct platform_device *pdev)
 {
        dev_dbg(&pdev->dev, "sst_platform_remove success\n");
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -813,7 +812,7 @@ static struct platform_driver sst_platform_driver = {
                .pm             = &sst_platform_pm,
        },
        .probe          = sst_platform_probe,
-       .remove         = sst_platform_remove,
+       .remove_new     = sst_platform_remove,
 };
 
 module_platform_driver(sst_platform_driver);
index 3be64430c2567feb4b9b5142c83803261b3611d4..d3973936426a07a2a6f42a7a1b8ee8e03cc7d553 100644 (file)
@@ -335,14 +335,13 @@ static int sst_acpi_probe(struct platform_device *pdev)
 * This function is called by OS when a device is unloaded
 * This frees the interrupt etc
 */
-static int sst_acpi_remove(struct platform_device *pdev)
+static void sst_acpi_remove(struct platform_device *pdev)
 {
        struct intel_sst_drv *ctx;
 
        ctx = platform_get_drvdata(pdev);
        sst_context_cleanup(ctx);
        platform_set_drvdata(pdev, NULL);
-       return 0;
 }
 
 static const struct acpi_device_id sst_acpi_ids[] = {
@@ -360,7 +359,7 @@ static struct platform_driver sst_acpi_driver = {
                .pm                     = &intel_sst_pm,
        },
        .probe  = sst_acpi_probe,
-       .remove = sst_acpi_remove,
+       .remove_new = sst_acpi_remove,
 };
 
 module_platform_driver(sst_acpi_driver);
index e68c4c7aa2bad5d9840d3f4f62eefb0fe594fa39..a542a67e21d07759e40e192fe948e518fc20f136 100644 (file)
@@ -194,12 +194,10 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
                return ret;
        }
 
-       for (n = 0; n < pcm_count; n++) {
-               ret = snd_soc_add_pcm_runtime(card, &links[n]);
-               if (ret < 0) {
-                       dev_err(card->dev, "add links failed: %d\n", ret);
-                       return ret;
-               }
+       ret = snd_soc_add_pcm_runtimes(card, links, pcm_count);
+       if (ret < 0) {
+               dev_err(card->dev, "add links failed: %d\n", ret);
+               return ret;
        }
 
        ret = avs_create_dapm_routes(card->dev, codec, pcm_count, &routes, &n);
index 05302ab705ae027f036589f6c9d108e9452fe222..adbe23a47847b7b4163828c7401a55caf3fd71f7 100644 (file)
@@ -478,7 +478,7 @@ static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
        int ret, i;
 
        num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins;
-       cfg_size = sizeof(*cfg) + sizeof(*cfg->pin_fmts) * num_pins;
+       cfg_size = struct_size(cfg, pin_fmts, num_pins);
 
        cfg = kzalloc(cfg_size, GFP_KERNEL);
        if (!cfg)
index df157b01df8bba58d6cd7e9c9c0c69ad6c5e7b24..7a30d2d36f199be191de37c1a5880dbf8ea05af8 100644 (file)
@@ -628,7 +628,7 @@ err_put_codec:
        return ret;
 }
 
-static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
+static void snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
@@ -636,7 +636,6 @@ static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
        gpiod_put(priv->speaker_en_gpio);
        device_remove_software_node(priv->codec_dev);
        put_device(priv->codec_dev);
-       return 0;
 }
 
 static struct platform_driver snd_byt_cht_es8316_mc_driver = {
@@ -644,7 +643,7 @@ static struct platform_driver snd_byt_cht_es8316_mc_driver = {
                .name = "bytcht_es8316",
        },
        .probe = snd_byt_cht_es8316_mc_probe,
-       .remove = snd_byt_cht_es8316_mc_remove,
+       .remove_new = snd_byt_cht_es8316_mc_remove,
 };
 
 module_platform_driver(snd_byt_cht_es8316_mc_driver);
index 5a12940ef9070819f693e00cb0c338088f439aad..630784b6cb6d303ec8d67d053a22aa8c0de3c878 100644 (file)
@@ -1856,7 +1856,7 @@ err_device:
        return ret_val;
 }
 
-static int snd_byt_rt5640_mc_remove(struct platform_device *pdev)
+static void snd_byt_rt5640_mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
@@ -1866,7 +1866,6 @@ static int snd_byt_rt5640_mc_remove(struct platform_device *pdev)
 
        device_remove_software_node(priv->codec_dev);
        put_device(priv->codec_dev);
-       return 0;
 }
 
 static struct platform_driver snd_byt_rt5640_mc_driver = {
@@ -1874,7 +1873,7 @@ static struct platform_driver snd_byt_rt5640_mc_driver = {
                .name = "bytcr_rt5640",
        },
        .probe = snd_byt_rt5640_mc_probe,
-       .remove = snd_byt_rt5640_mc_remove,
+       .remove_new = snd_byt_rt5640_mc_remove,
 };
 
 module_platform_driver(snd_byt_rt5640_mc_driver);
index 8fca9b82d4d04ae189c136436747230485b3644e..805afaf47b2904c4595b770fc8b11e94d06de739 100644 (file)
@@ -1127,14 +1127,13 @@ err_device:
        return ret_val;
 }
 
-static int snd_byt_rt5651_mc_remove(struct platform_device *pdev)
+static void snd_byt_rt5651_mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
 
        device_remove_software_node(priv->codec_dev);
        put_device(priv->codec_dev);
-       return 0;
 }
 
 static struct platform_driver snd_byt_rt5651_mc_driver = {
@@ -1142,7 +1141,7 @@ static struct platform_driver snd_byt_rt5651_mc_driver = {
                .name = "bytcr_rt5651",
        },
        .probe = snd_byt_rt5651_mc_probe,
-       .remove = snd_byt_rt5651_mc_remove,
+       .remove_new = snd_byt_rt5651_mc_remove,
 };
 
 module_platform_driver(snd_byt_rt5651_mc_driver);
index c0706537f6736a4112642a121d53c961da875a5f..f2382d4cb76f20ac37cfef1901a5257a75f41860 100644 (file)
@@ -466,13 +466,12 @@ out_put_gpio:
        return ret;
 }
 
-static int snd_byt_wm5102_mc_remove(struct platform_device *pdev)
+static void snd_byt_wm5102_mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
 
        gpiod_put(priv->spkvdd_en_gpio);
-       return 0;
 }
 
 static struct platform_driver snd_byt_wm5102_mc_driver = {
@@ -480,7 +479,7 @@ static struct platform_driver snd_byt_wm5102_mc_driver = {
                .name = "bytcr_wm5102",
        },
        .probe = snd_byt_wm5102_mc_probe,
-       .remove = snd_byt_wm5102_mc_remove,
+       .remove_new = snd_byt_wm5102_mc_remove,
 };
 
 module_platform_driver(snd_byt_wm5102_mc_driver);
index 2c086e901aae8f09af1473b51721c340c3e02e15..850310de774be86fe673c921a74134b5817b0fd4 100644 (file)
@@ -623,15 +623,13 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        return ret_val;
 }
 
-static int snd_cht_mc_remove(struct platform_device *pdev)
+static void snd_cht_mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
 
        if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
                clk_disable_unprepare(ctx->mclk);
-
-       return 0;
 }
 
 static struct platform_driver snd_cht_mc_driver = {
@@ -639,7 +637,7 @@ static struct platform_driver snd_cht_mc_driver = {
                .name = "cht-bsw-max98090",
        },
        .probe = snd_cht_mc_probe,
-       .remove = snd_cht_mc_remove,
+       .remove_new = snd_cht_mc_remove,
 };
 
 module_platform_driver(snd_cht_mc_driver)
index 851c516c8f5b14d3eeccd5a3ddd05badda47392d..8b8b07e4f2febddf68624299ef90e3e35180d884 100644 (file)
@@ -168,11 +168,16 @@ static int cs35l41_compute_codec_conf(void)
                        continue;
                }
                physdev = get_device(acpi_get_first_physical_node(adev));
+               acpi_dev_put(adev);
+               if (!physdev) {
+                       pr_devel("Cannot find physical node for HID %s UID %u (%s)\n", CS35L41_HID,
+                                       uid, cs35l41_name_prefixes[uid]);
+                       return 0;
+               }
                cs35l41_components[sz].name = dev_name(physdev);
                cs35l41_components[sz].dai_name = CS35L41_CODEC_DAI;
                cs35l41_codec_conf[sz].dlc.name = dev_name(physdev);
                cs35l41_codec_conf[sz].name_prefix = cs35l41_name_prefixes[uid];
-               acpi_dev_put(adev);
                sz++;
        }
 
index 894b6610b9e27d7da84bcf28efc94da0bf2f2c07..adf5852b2c9a107af05ddb0f016ae7e08842a710 100644 (file)
@@ -780,7 +780,7 @@ err_put_codec:
        return ret;
 }
 
-static int sof_es8336_remove(struct platform_device *pdev)
+static void sof_es8336_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
@@ -789,8 +789,6 @@ static int sof_es8336_remove(struct platform_device *pdev)
        gpiod_put(priv->gpio_speakers);
        device_remove_software_node(priv->codec_dev);
        put_device(priv->codec_dev);
-
-       return 0;
 }
 
 static const struct platform_device_id board_ids[] = {
@@ -817,7 +815,7 @@ static struct platform_driver sof_es8336_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = sof_es8336_probe,
-       .remove = sof_es8336_remove,
+       .remove_new = sof_es8336_remove,
        .id_table = board_ids,
 };
 module_platform_driver(sof_es8336_driver);
index d4c67d5340a92a5bda7da1bd8b45fd229ad35c2c..5192e02b3cee049dc47952f4ed08bf8894af51b6 100644 (file)
@@ -416,7 +416,7 @@ static int sof_audio_probe(struct platform_device *pdev)
                                          &sof_audio_card_pcm512x);
 }
 
-static int sof_pcm512x_remove(struct platform_device *pdev)
+static void sof_pcm512x_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct snd_soc_component *component;
@@ -427,13 +427,11 @@ static int sof_pcm512x_remove(struct platform_device *pdev)
                        break;
                }
        }
-
-       return 0;
 }
 
 static struct platform_driver sof_audio = {
        .probe = sof_audio_probe,
-       .remove = sof_pcm512x_remove,
+       .remove_new = sof_pcm512x_remove,
        .driver = {
                .name = "sof_pcm512x",
                .pm = &snd_soc_pm_ops,
index 4fe448295a902604900d31d7814583363ff6d2c8..791a59c5f00dcc710fd405143663643bc474fddd 100644 (file)
@@ -234,7 +234,9 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = {
                                        SOF_SPEAKER_AMP_PRESENT |
                                        SOF_MAX98360A_SPEAKER_AMP_PRESENT |
                                        SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(4)
+                                       SOF_RT5682_NUM_HDMIDEV(4) |
+                                       SOF_BT_OFFLOAD_SSP(1) |
+                                       SOF_SSP_BT_OFFLOAD_PRESENT
                                        ),
        },
        {
@@ -246,7 +248,9 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = {
                                        SOF_RT5682_SSP_CODEC(2) |
                                        SOF_SPEAKER_AMP_PRESENT |
                                        SOF_RT5682_SSP_AMP(0) |
-                                       SOF_RT5682_NUM_HDMIDEV(4)
+                                       SOF_RT5682_NUM_HDMIDEV(4) |
+                                       SOF_BT_OFFLOAD_SSP(1) |
+                                       SOF_SSP_BT_OFFLOAD_PRESENT
                                        ),
        },
        {}
@@ -793,7 +797,6 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
                        }
                        links[id].init = max_98390_spk_codec_init;
                        links[id].ops = &max_98390_ops;
-                       links[id].dpcm_capture = 1;
 
                } else {
                        max_98357a_dai_link(&links[id]);
@@ -1109,7 +1112,20 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_SPEAKER_AMP_PRESENT |
                                        SOF_RT1019_SPEAKER_AMP_PRESENT |
                                        SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4)),
+                                       SOF_RT5682_NUM_HDMIDEV(4) |
+                                       SOF_BT_OFFLOAD_SSP(2) |
+                                       SOF_SSP_BT_OFFLOAD_PRESENT),
+       },
+       {
+               .name = "rpl_mx98360_rt5682",
+               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+                                       SOF_RT5682_SSP_CODEC(0) |
+                                       SOF_SPEAKER_AMP_PRESENT |
+                                       SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+                                       SOF_RT5682_SSP_AMP(1) |
+                                       SOF_RT5682_NUM_HDMIDEV(4) |
+                                       SOF_BT_OFFLOAD_SSP(2) |
+                                       SOF_SSP_BT_OFFLOAD_PRESENT),
        },
        {
                .name = "mtl_mx98357_rt5682",
@@ -1117,7 +1133,9 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_RT5682_SSP_CODEC(0) |
                                        SOF_SPEAKER_AMP_PRESENT |
                                        SOF_RT5682_SSP_AMP(1) |
-                                       SOF_RT5682_NUM_HDMIDEV(4)),
+                                       SOF_RT5682_NUM_HDMIDEV(4) |
+                                       SOF_BT_OFFLOAD_SSP(2) |
+                                       SOF_SSP_BT_OFFLOAD_PRESENT),
        },
        {
                .name = "mtl_mx98360_rt5682",
index 767fa89d08708519d32027540b7d289771532527..6faf4a43eaf5ad11f7d5172446116078ddd2e4c7 100644 (file)
@@ -621,7 +621,6 @@ static struct sof_sdw_codec_info codec_info_list[] = {
                .direction = {true, true},
                .dai_name = "max98373-aif1",
                .init = sof_sdw_mx8373_init,
-               .codec_card_late_probe = sof_sdw_mx8373_late_probe,
                .codec_type = SOF_SDW_CODEC_TYPE_AMP,
        },
        {
@@ -733,34 +732,36 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li
                int stream;
                u64 adr;
 
-               adr = link->adr_d->adr;
-               codec_index = find_codec_info_part(adr);
-               if (codec_index < 0)
-                       return codec_index;
+               for (i = 0; i < link->num_adr; i++) {
+                       adr = link->adr_d[i].adr;
+                       codec_index = find_codec_info_part(adr);
+                       if (codec_index < 0)
+                               return codec_index;
 
-               if (codec_info_list[codec_index].codec_type < _codec_type)
-                       dev_warn(dev,
-                                "Unexpected address table ordering. Expected order: jack -> amp -> mic\n");
+                       if (codec_info_list[codec_index].codec_type < _codec_type)
+                               dev_warn(dev,
+                                        "Unexpected address table ordering. Expected order: jack -> amp -> mic\n");
 
-               _codec_type = codec_info_list[codec_index].codec_type;
+                       _codec_type = codec_info_list[codec_index].codec_type;
 
-               endpoint = link->adr_d->endpoints;
+                       endpoint = link->adr_d[i].endpoints;
 
-               /* count DAI number for playback and capture */
-               for_each_pcm_streams(stream) {
-                       if (!codec_info_list[codec_index].direction[stream])
-                               continue;
+                       /* count DAI number for playback and capture */
+                       for_each_pcm_streams(stream) {
+                               if (!codec_info_list[codec_index].direction[stream])
+                                       continue;
 
-                       (*sdw_cpu_dai_num)++;
+                               (*sdw_cpu_dai_num)++;
 
-                       /* count BE for each non-aggregated slave or group */
-                       if (!endpoint->aggregated || no_aggregation ||
-                           !group_visited[endpoint->group_id])
-                               (*sdw_be_num)++;
-               }
+                               /* count BE for each non-aggregated slave or group */
+                               if (!endpoint->aggregated || no_aggregation ||
+                                   !group_visited[endpoint->group_id])
+                                       (*sdw_be_num)++;
+                       }
 
-               if (endpoint->aggregated)
-                       group_visited[endpoint->group_id] = true;
+                       if (endpoint->aggregated)
+                               group_visited[endpoint->group_id] = true;
+               }
        }
 
        return 0;
@@ -830,17 +831,19 @@ static int create_codec_dai_name(struct device *dev,
                                 int offset,
                                 struct snd_soc_codec_conf *codec_conf,
                                 int codec_count,
-                                int *codec_conf_index)
+                                int *codec_conf_index,
+                                int adr_index)
 {
+       int _codec_index = -1;
        int i;
 
        /* sanity check */
-       if (*codec_conf_index + link->num_adr > codec_count) {
+       if (*codec_conf_index + link->num_adr - adr_index > codec_count) {
                dev_err(dev, "codec_conf: out-of-bounds access requested\n");
                return -EINVAL;
        }
 
-       for (i = 0; i < link->num_adr; i++) {
+       for (i = adr_index; i < link->num_adr; i++) {
                unsigned int sdw_version, unique_id, mfg_id;
                unsigned int link_id, part_id, class_id;
                int codec_index, comp_index;
@@ -856,7 +859,7 @@ static int create_codec_dai_name(struct device *dev,
                part_id = SDW_PART_ID(adr);
                class_id = SDW_CLASS_ID(adr);
 
-               comp_index = i + offset;
+               comp_index = i - adr_index + offset;
                if (is_unique_device(link, sdw_version, mfg_id, part_id,
                                     class_id, i)) {
                        codec_str = "sdw:%01x:%04x:%04x:%02x";
@@ -878,6 +881,11 @@ static int create_codec_dai_name(struct device *dev,
                codec_index = find_codec_info_part(adr);
                if (codec_index < 0)
                        return codec_index;
+               if (_codec_index != -1 && codec_index != _codec_index) {
+                       dev_dbg(dev, "Different devices on the same sdw link\n");
+                       break;
+               }
+               _codec_index = codec_index;
 
                codec[comp_index].dai_name =
                        codec_info_list[codec_index].dai_name;
@@ -944,16 +952,16 @@ static int set_codec_init_func(struct snd_soc_card *card,
 static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
                          struct device *dev, int *cpu_dai_id, int *cpu_dai_num,
                          int *codec_num, unsigned int *group_id,
-                         bool *group_generated)
+                         bool *group_generated, int adr_index)
 {
        const struct snd_soc_acpi_adr_device *adr_d;
        const struct snd_soc_acpi_link_adr *adr_next;
        bool no_aggregation;
        int index = 0;
+       int i;
 
        no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
-       *codec_num = adr_link->num_adr;
-       adr_d = adr_link->adr_d;
+       adr_d = &adr_link->adr_d[adr_index];
 
        /* make sure the link mask has a single bit set */
        if (!is_power_of_2(adr_link->mask))
@@ -962,12 +970,21 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
        cpu_dai_id[index++] = ffs(adr_link->mask) - 1;
        if (!adr_d->endpoints->aggregated || no_aggregation) {
                *cpu_dai_num = 1;
+               *codec_num = 1;
                *group_id = 0;
                return 0;
        }
 
        *group_id = adr_d->endpoints->group_id;
 
+       /* Count endpoints with the same group_id in the adr_link */
+       *codec_num = 0;
+       for (i = 0; i < adr_link->num_adr; i++) {
+               if (adr_link->adr_d[i].endpoints->aggregated &&
+                   adr_link->adr_d[i].endpoints->group_id == *group_id)
+                       (*codec_num)++;
+       }
+
        /* gather other link ID of slaves in the same group */
        for (adr_next = adr_link + 1; adr_next && adr_next->num_adr;
                adr_next++) {
@@ -988,7 +1005,11 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
                }
 
                cpu_dai_id[index++] = ffs(adr_next->mask) - 1;
-               *codec_num += adr_next->num_adr;
+               for (i = 0; i < adr_next->num_adr; i++) {
+                       if (adr_next->adr_d[i].endpoints->aggregated &&
+                           adr_next->adr_d[i].endpoints->group_id == *group_id)
+                               (*codec_num)++;
+               }
        }
 
        /*
@@ -1001,6 +1022,8 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
        return 0;
 }
 
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
 static int create_sdw_dailink(struct snd_soc_card *card,
                              struct device *dev, int *link_index,
                              struct snd_soc_dai_link *dai_links,
@@ -1011,7 +1034,9 @@ static int create_sdw_dailink(struct snd_soc_card *card,
                              struct snd_soc_codec_conf *codec_conf,
                              int codec_count, int *link_id,
                              int *codec_conf_index,
-                             bool *ignore_pch_dmic)
+                             bool *ignore_pch_dmic,
+                             bool append_codec_type,
+                             int adr_index)
 {
        const struct snd_soc_acpi_link_adr *link_next;
        struct snd_soc_dai_link_component *codecs;
@@ -1027,7 +1052,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
        int k;
 
        ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num,
-                            &group_id, group_generated);
+                            &group_id, group_generated, adr_index);
        if (ret)
                return ret;
 
@@ -1050,7 +1075,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
                        continue;
 
                ret = create_codec_dai_name(dev, link_next, codecs, codec_idx,
-                                           codec_conf, codec_count, codec_conf_index);
+                                           codec_conf, codec_count, codec_conf_index, adr_index);
                if (ret < 0)
                        return ret;
 
@@ -1060,7 +1085,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
        }
 
        /* find codec info to create BE DAI */
-       codec_index = find_codec_info_part(link->adr_d[0].adr);
+       codec_index = find_codec_info_part(link->adr_d[adr_index].adr);
        if (codec_index < 0)
                return codec_index;
 
@@ -1087,14 +1112,22 @@ static int create_sdw_dailink(struct snd_soc_card *card,
                static const char * const sdw_stream_name[] = {
                        "SDW%d-Playback",
                        "SDW%d-Capture",
+                       "SDW%d-Playback-%s",
+                       "SDW%d-Capture-%s",
                };
 
                if (!codec_info_list[codec_index].direction[stream])
                        continue;
 
                /* create stream name according to first link id */
-               name = devm_kasprintf(dev, GFP_KERNEL,
-                                     sdw_stream_name[stream], cpu_dai_id[0]);
+               if (append_codec_type) {
+                       name = devm_kasprintf(dev, GFP_KERNEL,
+                                             sdw_stream_name[stream + 2], cpu_dai_id[0],
+                                             type_strings[codec_info_list[codec_index].codec_type]);
+               } else {
+                       name = devm_kasprintf(dev, GFP_KERNEL,
+                                             sdw_stream_name[stream], cpu_dai_id[0]);
+               }
                if (!name)
                        return -ENOMEM;
 
@@ -1210,6 +1243,7 @@ static int sof_card_dai_links_create(struct device *dev,
        const struct snd_soc_acpi_link_adr *adr_link;
        struct snd_soc_dai_link_component *cpus;
        struct snd_soc_codec_conf *codec_conf;
+       bool append_codec_type = false;
        bool ignore_pch_dmic = false;
        int codec_conf_count;
        int codec_conf_index = 0;
@@ -1301,31 +1335,54 @@ static int sof_card_dai_links_create(struct device *dev,
        for (i = 0; i < SDW_MAX_GROUPS; i++)
                group_generated[i] = false;
 
-       /* generate DAI links by each sdw link */
        for (; adr_link->num_adr; adr_link++) {
-               const struct snd_soc_acpi_endpoint *endpoint;
-
-               endpoint = adr_link->adr_d->endpoints;
-               if (endpoint->aggregated && !endpoint->group_id) {
-                       dev_err(dev, "invalid group id on link %x",
-                               adr_link->mask);
-                       continue;
+               /*
+                * If there are two or more different devices on the same sdw link, we have to
+                * append the codec type to the dai link name to prevent duplicated dai link name.
+                * The same type devices on the same sdw link will be in the same
+                * snd_soc_acpi_adr_device array. They won't be described in different adr_links.
+                */
+               for (i = 0; i < adr_link->num_adr; i++) {
+                       for (j = 0; j < i; j++) {
+                               if ((SDW_PART_ID(adr_link->adr_d[i].adr) !=
+                                   SDW_PART_ID(adr_link->adr_d[j].adr)) ||
+                                   (SDW_MFG_ID(adr_link->adr_d[i].adr) !=
+                                   SDW_MFG_ID(adr_link->adr_d[i].adr))) {
+                                       append_codec_type = true;
+                                       goto out;
+                               }
+                       }
                }
+       }
+out:
 
-               /* this group has been generated */
-               if (endpoint->aggregated &&
-                   group_generated[endpoint->group_id])
-                       continue;
+       /* generate DAI links by each sdw link */
+       for (adr_link = mach_params->links ; adr_link->num_adr; adr_link++) {
+               for (i = 0; i < adr_link->num_adr; i++) {
+                       const struct snd_soc_acpi_endpoint *endpoint;
 
-               ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num,
-                                        sdw_cpu_dai_num, cpus, adr_link,
-                                        &cpu_id, group_generated,
-                                        codec_conf, codec_conf_count,
-                                        &be_id, &codec_conf_index,
-                                        &ignore_pch_dmic);
-               if (ret < 0) {
-                       dev_err(dev, "failed to create dai link %d", link_index);
-                       return ret;
+                       endpoint = adr_link->adr_d[i].endpoints;
+                       if (endpoint->aggregated && !endpoint->group_id) {
+                               dev_err(dev, "invalid group id on link %x",
+                                       adr_link->mask);
+                               continue;
+                       }
+
+                       /* this group has been generated */
+                       if (endpoint->aggregated &&
+                           group_generated[endpoint->group_id])
+                               continue;
+
+                       ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num,
+                                                sdw_cpu_dai_num, cpus, adr_link,
+                                                &cpu_id, group_generated,
+                                                codec_conf, codec_conf_count,
+                                                &be_id, &codec_conf_index,
+                                                &ignore_pch_dmic, append_codec_type, i);
+                       if (ret < 0) {
+                               dev_err(dev, "failed to create dai link %d", link_index);
+                               return ret;
+                       }
                }
        }
 
@@ -1490,12 +1547,12 @@ static int sof_sdw_card_late_probe(struct snd_soc_card *card)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
-               if (!codec_info_list[i].late_probe)
-                       continue;
+               if (codec_info_list[i].codec_card_late_probe) {
+                       ret = codec_info_list[i].codec_card_late_probe(card);
 
-               ret = codec_info_list[i].codec_card_late_probe(card);
-               if (ret < 0)
-                       return ret;
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        if (ctx->idisp_codec)
@@ -1613,13 +1670,11 @@ static int mc_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int mc_remove(struct platform_device *pdev)
+static void mc_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        mc_dailink_exit_loop(card);
-
-       return 0;
 }
 
 static struct platform_driver sof_sdw_driver = {
@@ -1628,7 +1683,7 @@ static struct platform_driver sof_sdw_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = mc_probe,
-       .remove = mc_remove,
+       .remove_new = mc_remove,
 };
 
 module_platform_driver(sof_sdw_driver);
index 350010b0e5f4f1d9d999c42c1bfdd00fd8181ced..081ab7eac5b618bdb4a21e8ebd937428848d180d 100644 (file)
@@ -74,7 +74,6 @@ struct sof_sdw_codec_info {
                     bool playback);
 
        int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
-       bool late_probe;
        int (*codec_card_late_probe)(struct snd_soc_card *card);
 };
 
@@ -159,8 +158,6 @@ int sof_sdw_mx8373_init(struct snd_soc_card *card,
                        struct sof_sdw_codec_info *info,
                        bool playback);
 
-int sof_sdw_mx8373_late_probe(struct snd_soc_card *card);
-
 /* RT5682 support */
 int sof_sdw_rt5682_init(struct snd_soc_card *card,
                        const struct snd_soc_acpi_link_adr *link,
index 77a3f32db11edc98a57206fd0c41c3a264ef53b7..3d7df58c0f1d1bbbb61acbbc5452a7b4af871720 100644 (file)
@@ -120,6 +120,16 @@ static const struct snd_soc_ops max_98373_sdw_ops = {
        .shutdown = sdw_shutdown,
 };
 
+static int mx8373_sdw_late_probe(struct snd_soc_card *card)
+{
+       struct snd_soc_dapm_context *dapm = &card->dapm;
+
+       /* Disable Left and Right Spk pin after boot */
+       snd_soc_dapm_disable_pin(dapm, "Left Spk");
+       snd_soc_dapm_disable_pin(dapm, "Right Spk");
+       return snd_soc_dapm_sync(dapm);
+}
+
 int sof_sdw_mx8373_init(struct snd_soc_card *card,
                        const struct snd_soc_acpi_link_adr *link,
                        struct snd_soc_dai_link *dai_links,
@@ -130,19 +140,9 @@ int sof_sdw_mx8373_init(struct snd_soc_card *card,
        if (info->amp_num == 2)
                dai_links->init = spk_init;
 
-       info->late_probe = true;
+       info->codec_card_late_probe = mx8373_sdw_late_probe;
 
        dai_links->ops = &max_98373_sdw_ops;
 
        return 0;
 }
-
-int sof_sdw_mx8373_late_probe(struct snd_soc_card *card)
-{
-       struct snd_soc_dapm_context *dapm = &card->dapm;
-
-       /* Disable Left and Right Spk pin after boot */
-       snd_soc_dapm_disable_pin(dapm, "Left Spk");
-       snd_soc_dapm_disable_pin(dapm, "Right Spk");
-       return snd_soc_dapm_sync(dapm);
-}
index fbad5a73de440d30e3b704b7bc018e73631bd942..17224d26d9d6bf84cb310745ab43a1557ca8b7bf 100644 (file)
@@ -278,11 +278,10 @@ static int sof_wm8804_probe(struct platform_device *pdev)
        return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
-static int sof_wm8804_remove(struct platform_device *pdev)
+static void sof_wm8804_remove(struct platform_device *pdev)
 {
        if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK)
                gpiod_remove_lookup_table(&up2_gpios_table);
-       return 0;
 }
 
 static struct platform_driver sof_wm8804_driver = {
@@ -291,7 +290,7 @@ static struct platform_driver sof_wm8804_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = sof_wm8804_probe,
-       .remove = sof_wm8804_remove,
+       .remove_new = sof_wm8804_remove,
 };
 module_platform_driver(sof_wm8804_driver);
 
index d5d08bd766c702b66d69a40f8cda0cf308bb180c..cac3dffbd0d9f631f439cabb83b39d240665064e 100644 (file)
@@ -293,7 +293,7 @@ static int catpt_acpi_probe(struct platform_device *pdev)
        return catpt_probe_components(cdev);
 }
 
-static int catpt_acpi_remove(struct platform_device *pdev)
+static void catpt_acpi_remove(struct platform_device *pdev)
 {
        struct catpt_dev *cdev = platform_get_drvdata(pdev);
 
@@ -305,8 +305,6 @@ static int catpt_acpi_remove(struct platform_device *pdev)
 
        catpt_sram_free(&cdev->iram);
        catpt_sram_free(&cdev->dram);
-
-       return 0;
 }
 
 static struct snd_soc_acpi_mach lpt_machines[] = {
@@ -376,7 +374,7 @@ MODULE_DEVICE_TABLE(acpi, catpt_ids);
 
 static struct platform_driver catpt_acpi_driver = {
        .probe = catpt_acpi_probe,
-       .remove = catpt_acpi_remove,
+       .remove_new = catpt_acpi_remove,
        .driver = {
                .name = "intel_catpt",
                .acpi_match_table = catpt_ids,
index 07f96a11ea2f971aa7ec7159d7b7fe00529b5d93..749d371a86ae2c68ac4cd1471f9e4046076cdf80 100644 (file)
@@ -284,7 +284,33 @@ static const struct snd_soc_acpi_link_adr rpl_sdw_rt1316_link12_rt714_link0[] =
        {}
 };
 
+static const struct snd_soc_acpi_link_adr rplp_crb[] = {
+       {
+               .mask = BIT(2),
+               .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+               .adr_d = rt711_sdca_2_adr,
+       },
+       {}
+};
+
+static const struct snd_soc_acpi_codecs rpl_rt5682_hp = {
+       .num_codecs = 2,
+       .codecs = {"10EC5682", "RTL5682"},
+};
+
+static const struct snd_soc_acpi_codecs rpl_max98360a_amp = {
+       .num_codecs = 1,
+       .codecs = {"MX98360A"},
+};
+
 struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
+       {
+               .comp_ids = &rpl_rt5682_hp,
+               .drv_name = "rpl_mx98360_rt5682",
+               .machine_quirk = snd_soc_acpi_codec_list,
+               .quirk_data = &rpl_max98360a_amp,
+               .sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg",
+       },
        {},
 };
 EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines);
@@ -331,7 +357,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = {
                .link_mask = 0x1, /* link0 required */
                .links = rpl_rvp,
                .drv_name = "sof_sdw",
-               .sof_tplg_filename = "sof-rpl-rt711.tplg",
+               .sof_tplg_filename = "sof-rpl-rt711-l0.tplg",
+       },
+       {
+               .link_mask = 0x4, /* link2 required */
+               .links = rplp_crb,
+               .drv_name = "sof_sdw",
+               .sof_tplg_filename = "sof-rpl-rt711-l2.tplg",
        },
        {},
 };
index a3a73c26f9aac26c17f263a3a79eee52bf582dc4..50e93c3707e8be9031051d1a1342b73eeb1c8c04 100644 (file)
@@ -402,15 +402,13 @@ err_unreg_skl_clk:
        return ret;
 }
 
-static int skl_clk_dev_remove(struct platform_device *pdev)
+static void skl_clk_dev_remove(struct platform_device *pdev)
 {
        struct skl_clk_data *data;
 
        data = platform_get_drvdata(pdev);
        unregister_src_clk(data);
        unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
-
-       return 0;
 }
 
 static struct platform_driver skl_clk_driver = {
@@ -418,7 +416,7 @@ static struct platform_driver skl_clk_driver = {
                .name = "skl-ssp-clk",
        },
        .probe = skl_clk_dev_probe,
-       .remove = skl_clk_dev_remove,
+       .remove_new = skl_clk_dev_remove,
 };
 
 module_platform_driver(skl_clk_driver);
index afdf7d61e4c5810d80adfd5d0e07c977a0f0746f..d1eb90310afa2dcba2c035675d70dd2f63b144ce 100644 (file)
@@ -736,7 +736,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
        return err;
 }
 
-static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
+static void kirkwood_i2s_dev_remove(struct platform_device *pdev)
 {
        struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
 
@@ -744,8 +744,6 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
        if (!IS_ERR(priv->extclk))
                clk_disable_unprepare(priv->extclk);
        clk_disable_unprepare(priv->clk);
-
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -761,7 +759,7 @@ MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
 
 static struct platform_driver kirkwood_i2s_driver = {
        .probe  = kirkwood_i2s_dev_probe,
-       .remove = kirkwood_i2s_dev_remove,
+       .remove_new = kirkwood_i2s_dev_remove,
        .driver = {
                .name = DRV_NAME,
                .of_match_table = of_match_ptr(mvebu_audio_of_match),
index 1c28b41e4311250486b9181de87073c097aa1c5c..1ba0633e542ff377543575f2fd6c9836c99ef19e 100644 (file)
@@ -1387,13 +1387,12 @@ unmap_pkv_err:
        return ret;
 }
 
-static int mtk_btcvsd_snd_remove(struct platform_device *pdev)
+static void mtk_btcvsd_snd_remove(struct platform_device *pdev)
 {
        struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev);
 
        iounmap(btcvsd->bt_pkv_base);
        iounmap(btcvsd->bt_sram_bank2_base);
-       return 0;
 }
 
 static const struct of_device_id mtk_btcvsd_snd_dt_match[] = {
@@ -1408,7 +1407,7 @@ static struct platform_driver mtk_btcvsd_snd_driver = {
                .of_match_table = mtk_btcvsd_snd_dt_match,
        },
        .probe = mtk_btcvsd_snd_probe,
-       .remove = mtk_btcvsd_snd_remove,
+       .remove_new = mtk_btcvsd_snd_remove,
 };
 
 module_platform_driver(mtk_btcvsd_snd_driver);
index 7c55c2cb1f2142ce31a79c7362b793eb179a1574..738093451ccbfc35dcbc1938044b410964ef6e7a 100644 (file)
@@ -47,20 +47,26 @@ int parse_dai_link_info(struct snd_soc_card *card)
        /* Loop over all the dai link sub nodes */
        for_each_available_child_of_node(dev->of_node, sub_node) {
                if (of_property_read_string(sub_node, "link-name",
-                                           &dai_link_name))
+                                           &dai_link_name)) {
+                       of_node_put(sub_node);
                        return -EINVAL;
+               }
 
                for_each_card_prelinks(card, i, dai_link) {
                        if (!strcmp(dai_link_name, dai_link->name))
                                break;
                }
 
-               if (i >= card->num_links)
+               if (i >= card->num_links) {
+                       of_node_put(sub_node);
                        return -EINVAL;
+               }
 
                ret = set_card_codec_info(card, sub_node, dai_link);
-               if (ret < 0)
+               if (ret < 0) {
+                       of_node_put(sub_node);
                        return ret;
+               }
        }
 
        return 0;
index 0f178de92a0faff7ccc5669d6e7b72823d4478e5..c9d4420e9b4c18945b52fb9d539723c43ed0054f 100644 (file)
@@ -1439,14 +1439,12 @@ err_pm_disable:
        return ret;
 }
 
-static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                mt2701_afe_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct mt2701_soc_variants mt2701_soc_v1 = {
@@ -1477,7 +1475,7 @@ static struct platform_driver mt2701_afe_pcm_driver = {
                   .pm = &mt2701_afe_pm_ops,
        },
        .probe = mt2701_afe_pcm_dev_probe,
-       .remove = mt2701_afe_pcm_dev_remove,
+       .remove_new = mt2701_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt2701_afe_pcm_driver);
index fb4abec9aa5f9a2eedcde72a65b7ae29fd910fc9..43038444c43dce7264b2c269d60924ce8bc9f81f 100644 (file)
@@ -876,14 +876,12 @@ err_pm_disable:
        return ret;
 }
 
-static int mt6797_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt6797_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                mt6797_afe_runtime_suspend(&pdev->dev);
        pm_runtime_put_sync(&pdev->dev);
-
-       return 0;
 }
 
 static const struct of_device_id mt6797_afe_pcm_dt_match[] = {
@@ -904,7 +902,7 @@ static struct platform_driver mt6797_afe_pcm_driver = {
                   .pm = &mt6797_afe_pm_ops,
        },
        .probe = mt6797_afe_pcm_dev_probe,
-       .remove = mt6797_afe_pcm_dev_remove,
+       .remove_new = mt6797_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt6797_afe_pcm_driver);
index bc155dd937e0b6cee043ddece87b24a9d923d380..f93c2ec8beb7bdd2cfe241ccf846c039c4197aa2 100644 (file)
@@ -1195,14 +1195,13 @@ err_pm_disable:
        return ret;
 }
 
-static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                mt8173_afe_runtime_suspend(&pdev->dev);
-       return 0;
 }
 
 static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
@@ -1223,7 +1222,7 @@ static struct platform_driver mt8173_afe_pcm_driver = {
                   .pm = &mt8173_afe_pm_ops,
        },
        .probe = mt8173_afe_pcm_dev_probe,
-       .remove = mt8173_afe_pcm_dev_remove,
+       .remove_new = mt8173_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt8173_afe_pcm_driver);
index 86c8a523fe9e37d85b4fd06eb8184bf87e72901e..90422ed2bbcc2709138058d4ca5908c7a0da6bed 100644 (file)
@@ -1255,13 +1255,11 @@ err_pm_disable:
        return ret;
 }
 
-static int mt8183_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt8183_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                mt8183_afe_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct of_device_id mt8183_afe_pcm_dt_match[] = {
@@ -1282,7 +1280,7 @@ static struct platform_driver mt8183_afe_pcm_driver = {
                   .pm = &mt8183_afe_pm_ops,
        },
        .probe = mt8183_afe_pcm_dev_probe,
-       .remove = mt8183_afe_pcm_dev_remove,
+       .remove_new = mt8183_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt8183_afe_pcm_driver);
index eda913fa147af9906ba420c0bd84d7ed2cef382b..f12e91cc4fcf5d065ff388678366e7fdf7a4aa82 100644 (file)
@@ -11,7 +11,7 @@
 #include "mt8186-afe-common.h"
 #include "mt8186-afe-gpio.h"
 
-struct pinctrl *aud_pinctrl;
+static struct pinctrl *aud_pinctrl;
 
 enum mt8186_afe_gpio {
        MT8186_AFE_GPIO_CLK_MOSI_OFF,
@@ -85,7 +85,7 @@ int mt8186_afe_gpio_init(struct device *dev)
                                                             aud_gpios[i].name);
                if (IS_ERR(aud_gpios[i].gpioctrl)) {
                        ret = PTR_ERR(aud_gpios[i].gpioctrl);
-                       dev_info(dev, "%s(), pinctrl_lookup_state %s fail, ret %d\n",
+                       dev_dbg(dev, "%s(), pinctrl_lookup_state %s fail, ret %d\n",
                                 __func__, aud_gpios[i].name, ret);
                } else {
                        aud_gpios[i].gpio_prepare = true;
@@ -108,13 +108,13 @@ static int mt8186_afe_gpio_select(struct device *dev,
        int ret = 0;
 
        if (type < 0 || type >= MT8186_AFE_GPIO_GPIO_NUM) {
-               dev_err(dev, "%s(), error, invalid gpio type %d\n",
+               dev_dbg(dev, "%s(), error, invalid gpio type %d\n",
                        __func__, type);
                return -EINVAL;
        }
 
        if (!aud_gpios[type].gpio_prepare) {
-               dev_err(dev, "%s(), error, gpio type %d not prepared\n",
+               dev_dbg(dev, "%s(), error, gpio type %d not prepared\n",
                        __func__, type);
                return -EIO;
        }
@@ -122,7 +122,7 @@ static int mt8186_afe_gpio_select(struct device *dev,
        ret = pinctrl_select_state(aud_pinctrl,
                                   aud_gpios[type].gpioctrl);
        if (ret) {
-               dev_err(dev, "%s(), error, can not set gpio type %d\n",
+               dev_dbg(dev, "%s(), error, can not set gpio type %d\n",
                        __func__, type);
                return ret;
        }
@@ -137,25 +137,25 @@ static int mt8186_afe_gpio_adda_dl(struct device *dev, bool enable)
        if (enable) {
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_ON);
                if (ret) {
-                       dev_err(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
                        return ret;
                }
 
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_ON);
                if (ret) {
-                       dev_err(dev, "%s(), MOSI DAT ON select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MOSI DAT ON select fail!\n", __func__);
                        return ret;
                }
        } else {
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_OFF);
                if (ret) {
-                       dev_err(dev, "%s(), MOSI DAT OFF select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MOSI DAT OFF select fail!\n", __func__);
                        return ret;
                }
 
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_OFF);
                if (ret) {
-                       dev_err(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
                        return ret;
                }
        }
@@ -170,25 +170,25 @@ static int mt8186_afe_gpio_adda_ul(struct device *dev, bool enable)
        if (enable) {
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_ON);
                if (ret) {
-                       dev_err(dev, "%s(), MISO CLK ON select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MISO CLK ON select fail!\n", __func__);
                        return ret;
                }
 
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_ON);
                if (ret) {
-                       dev_err(dev, "%s(), MISO DAT ON select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MISO DAT ON select fail!\n", __func__);
                        return ret;
                }
        } else {
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_OFF);
                if (ret) {
-                       dev_err(dev, "%s(), MISO DAT OFF select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MISO DAT OFF select fail!\n", __func__);
                        return ret;
                }
 
                ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_OFF);
                if (ret) {
-                       dev_err(dev, "%s(), MISO CLK OFF select fail!\n", __func__);
+                       dev_dbg(dev, "%s(), MISO CLK OFF select fail!\n", __func__);
                        return ret;
                }
        }
@@ -230,7 +230,7 @@ int mt8186_afe_gpio_request(struct device *dev, bool enable,
                sel = enable ? MT8186_AFE_GPIO_PCM_ON : MT8186_AFE_GPIO_PCM_OFF;
                break;
        default:
-               dev_err(dev, "%s(), invalid dai %d\n", __func__, dai);
+               dev_dbg(dev, "%s(), invalid dai %d\n", __func__, dai);
                goto unlock;
        }
 
index 094402470dc2383e8045a1991223f6c749a23156..247ab8df941f7b56294d651d458c0898ee508c0c 100644 (file)
@@ -110,7 +110,7 @@ static unsigned int adda_dl_rate_transform(struct mtk_base_afe *afe,
        case 192000:
                return MTK_AFE_ADDA_DL_RATE_192K;
        default:
-               dev_info(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+               dev_dbg(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
                         __func__, rate);
        }
 
@@ -134,7 +134,7 @@ static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe,
        case 192000:
                return MTK_AFE_ADDA_UL_RATE_192K;
        default:
-               dev_info(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+               dev_dbg(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
                         __func__, rate);
        }
 
index 970b980a81e64b86bdc88e10b0f9a1b7b4582a31..cdf54d1eb50de5c99b291db924514edc9cbb65a0 100644 (file)
@@ -1061,7 +1061,7 @@ static int mt8186_mt6366_da7219_max98357_dev_probe(struct platform_device *pdev)
                card->name = card->topology_shortname;
                sof_on = 1;
        } else {
-               dev_info(&pdev->dev, "Probe without adsp\n");
+               dev_dbg(&pdev->dev, "Probe without adsp\n");
        }
 
        if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
index b333950aa3c395d705a25761ac0eb84fd3b81ea7..7538274641fd1d72a99cadc27d40fd06ad4a1b32 100644 (file)
@@ -95,7 +95,7 @@ static int dmic_set(struct snd_kcontrol *kcontrol,
        priv->dmic_switch = ucontrol->value.integer.value[0];
        if (priv->dmic_sel) {
                gpiod_set_value(priv->dmic_sel, priv->dmic_switch);
-               dev_info(dapm->card->dev, "dmic_set_value %d\n",
+               dev_dbg(dapm->card->dev, "dmic_set_value %d\n",
                         priv->dmic_switch);
        }
        return 0;
@@ -139,7 +139,7 @@ static int primary_codec_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        if (!priv->dmic_sel) {
-               dev_info(card->dev, "dmic_sel is null\n");
+               dev_dbg(card->dev, "dmic_sel is null\n");
                return 0;
        }
 
@@ -1152,7 +1152,7 @@ static int mt8186_mt6366_rt1019_rt5682s_dev_probe(struct platform_device *pdev)
                card->name = card->topology_shortname;
                sof_on = 1;
        } else {
-               dev_info(&pdev->dev, "Probe without adsp\n");
+               dev_dbg(&pdev->dev, "Probe without adsp\n");
        }
 
        if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
index e8e84de865422021d2a99498128f5ce53a16c546..e5f9373bed562ecff59da0ae9da3c5163702c5ed 100644 (file)
@@ -3323,11 +3323,9 @@ err_pm_put:
        return ret;
 }
 
-static int mt8188_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt8188_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
-
-       return 0;
 }
 
 static const struct of_device_id mt8188_afe_pcm_dt_match[] = {
@@ -3348,7 +3346,7 @@ static struct platform_driver mt8188_afe_pcm_driver = {
                   .pm = &mt8188_afe_pm_ops,
        },
        .probe = mt8188_afe_pcm_dev_probe,
-       .remove = mt8188_afe_pcm_dev_remove,
+       .remove_new = mt8188_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt8188_afe_pcm_driver);
index bba5f3056e8ffd225fed80398b37422daa3de903..416aff7262539957b6f12882277343c88d273fc4 100644 (file)
@@ -206,8 +206,6 @@ int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
        struct mt8192_afe_private *afe_priv = afe->platform_priv;
        int ret;
 
-       dev_info(afe->dev, "%s()\n", __func__);
-
        ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
        if (ret) {
                dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
@@ -276,8 +274,6 @@ void mt8192_afe_disable_clock(struct mtk_base_afe *afe)
 {
        struct mt8192_afe_private *afe_priv = afe->platform_priv;
 
-       dev_info(afe->dev, "%s()\n", __func__);
-
        clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
        mt8192_set_audio_int_bus_parent(afe, CLK_CLK26M);
        clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
index e1e4ca931551828264470d0f13034a1058a76cec..d0520e7e1d79aed065fcd592c20481be6a8db747 100644 (file)
@@ -369,7 +369,7 @@ static int ul_tinyconn_event(struct snd_soc_dapm_widget *w,
        unsigned int reg_shift;
        unsigned int reg_mask_shift;
 
-       dev_info(afe->dev, "%s(), event 0x%x\n", __func__, event);
+       dev_dbg(afe->dev, "%s(), event 0x%x\n", __func__, event);
 
        if (strstr(w->name, "UL1")) {
                reg_shift = VUL1_USE_TINY_SFT;
@@ -2055,8 +2055,6 @@ static int mt8192_afe_runtime_suspend(struct device *dev)
        unsigned int value;
        int ret;
 
-       dev_info(afe->dev, "%s()\n", __func__);
-
        if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
                goto skip_regmap;
 
@@ -2097,8 +2095,6 @@ static int mt8192_afe_runtime_resume(struct device *dev)
        struct mt8192_afe_private *afe_priv = afe->platform_priv;
        int ret;
 
-       dev_info(afe->dev, "%s()\n", __func__);
-
        ret = mt8192_afe_enable_clock(afe);
        if (ret)
                return ret;
@@ -2353,7 +2349,7 @@ err_pm_disable:
        return ret;
 }
 
-static int mt8192_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt8192_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        struct mtk_base_afe *afe = platform_get_drvdata(pdev);
 
@@ -2363,7 +2359,6 @@ static int mt8192_afe_pcm_dev_remove(struct platform_device *pdev)
 
        /* disable afe clock */
        mt8192_afe_disable_clock(afe);
-       return 0;
 }
 
 static const struct of_device_id mt8192_afe_pcm_dt_match[] = {
@@ -2384,7 +2379,7 @@ static struct platform_driver mt8192_afe_pcm_driver = {
                   .pm = &mt8192_afe_pm_ops,
        },
        .probe = mt8192_afe_pcm_dev_probe,
-       .remove = mt8192_afe_pcm_dev_remove,
+       .remove_new = mt8192_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt8192_afe_pcm_driver);
index f3bebed2428a72bb4b57f7748aae4c9dc05b3249..9ce06821c7d0f01834e0469553492b4850b5388b 100644 (file)
@@ -256,8 +256,8 @@ static int mtk_tdm_en_event(struct snd_soc_dapm_widget *w,
                return -EINVAL;
        }
 
-       dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
-                __func__, w->name, event);
+       dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+               __func__, w->name, event);
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
@@ -288,8 +288,8 @@ static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
                return -EINVAL;
        }
 
-       dev_info(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
-                __func__, w->name, event, dai_id);
+       dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
+               __func__, w->name, event, dai_id);
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
@@ -320,8 +320,8 @@ static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
                return -EINVAL;
        }
 
-       dev_info(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
-                __func__, w->name, event, dai_id);
+       dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
+               __func__, w->name, event, dai_id);
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
@@ -545,13 +545,13 @@ static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
        if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
                dev_warn(afe->dev, "%s(), bck cannot generate", __func__);
 
-       dev_info(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
-                __func__,
-                tdm_id, rate, channels, format,
-                tdm_priv->mclk_rate, tdm_priv->bck_rate);
+       dev_dbg(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
+               __func__,
+               tdm_id, rate, channels, format,
+               tdm_priv->mclk_rate, tdm_priv->bck_rate);
 
-       dev_info(afe->dev, "%s(), out_channels_per_sdata = %d\n",
-                __func__, out_channels_per_sdata);
+       dev_dbg(afe->dev, "%s(), out_channels_per_sdata = %d\n",
+               __func__, out_channels_per_sdata);
 
        /* set tdm */
        if (tdm_priv->bck_invert)
@@ -644,7 +644,7 @@ static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
-       dev_info(afe->dev, "%s(), freq %d\n", __func__, freq);
+       dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
 
        return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
 }
@@ -754,8 +754,6 @@ int mt8192_dai_tdm_register(struct mtk_base_afe *afe)
        struct mtk_afe_tdm_priv *tdm_priv;
        struct mtk_base_afe_dai *dai;
 
-       dev_info(afe->dev, "%s()\n", __func__);
-
        dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
        if (!dai)
                return -ENOMEM;
index 16660eda577e23c05817b635e4b91d42493e222a..5e163e23a2076c519cbd74bbd7758c60ce8d7b98 100644 (file)
@@ -161,8 +161,6 @@ static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
        int counter;
        int mtkaif_calib_ok;
 
-       dev_info(afe->dev, "%s(), start\n", __func__);
-
        pm_runtime_get_sync(afe->dev);
        mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
        mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
@@ -294,11 +292,11 @@ static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
        mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 0);
        pm_runtime_put(afe->dev);
 
-       dev_info(afe->dev, "%s(), mtkaif_chosen_phase[0/1/2]:%d/%d/%d\n",
-                __func__,
-                afe_priv->mtkaif_chosen_phase[0],
-                afe_priv->mtkaif_chosen_phase[1],
-                afe_priv->mtkaif_chosen_phase[2]);
+       dev_dbg(afe->dev, "%s(), mtkaif_chosen_phase[0/1/2]:%d/%d/%d\n",
+               __func__,
+               afe_priv->mtkaif_chosen_phase[0],
+               afe_priv->mtkaif_chosen_phase[1],
+               afe_priv->mtkaif_chosen_phase[2]);
 
        return 0;
 }
index 72b2c6d629b937b27dad5f69775000787cbdd148..9e45efeada55c39b708ca96de60aecd90361919a 100644 (file)
@@ -3253,7 +3253,7 @@ err_pm_put:
        return ret;
 }
 
-static int mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
+static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        struct mtk_base_afe *afe = platform_get_drvdata(pdev);
 
@@ -3264,7 +3264,6 @@ static int mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
                mt8195_afe_runtime_suspend(&pdev->dev);
 
        mt8195_afe_deinit_clock(afe);
-       return 0;
 }
 
 static const struct of_device_id mt8195_afe_pcm_dt_match[] = {
@@ -3285,7 +3284,7 @@ static struct platform_driver mt8195_afe_pcm_driver = {
                   .pm = &mt8195_afe_pm_ops,
        },
        .probe = mt8195_afe_pcm_dev_probe,
-       .remove = mt8195_afe_pcm_dev_remove,
+       .remove_new = mt8195_afe_pcm_dev_remove,
 };
 
 module_platform_driver(mt8195_afe_pcm_driver);
index f04bd1781356a0c27fa6ee619b6330811af11fe9..0dd35255066bec82d13682dc1615044dd939dd3d 100644 (file)
@@ -704,13 +704,18 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_adda_priv *adda_priv = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_adda_priv *adda_priv;
        unsigned int rate = params_rate(params);
-       int id = dai->id;
-       int ret = 0;
+       int ret;
+
+       if (dai->id != MT8195_AFE_IO_DL_SRC &&
+           dai->id != MT8195_AFE_IO_UL_SRC1 &&
+           dai->id != MT8195_AFE_IO_UL_SRC2)
+               return -EINVAL;
+       adda_priv = afe_priv->dai_priv[dai->id];
 
        dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
-               __func__, id, substream->stream, rate);
+               __func__, dai->id, substream->stream, rate);
 
        if (rate > ADDA_HIRES_THRES)
                adda_priv->hires_required = 1;
@@ -718,9 +723,9 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
                adda_priv->hires_required = 0;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               ret = mtk_dai_da_configure(afe, rate, id);
+               ret = mtk_dai_da_configure(afe, rate, dai->id);
        else
-               ret = mtk_dai_ad_configure(afe, rate, id);
+               ret = mtk_dai_ad_configure(afe, rate, dai->id);
 
        return ret;
 }
index f2c9a1fdbe0d04c2b11f7f9f4ebcc3ba3680fb9c..eedb9165f91115294cc2b92d46c5b827d858adcf 100644 (file)
@@ -137,6 +137,38 @@ static const struct mtk_dai_etdm_rate mt8195_etdm_rates[] = {
        { .rate = 352800, .reg_value = 21, },
 };
 
+static bool mt8195_afe_etdm_is_valid(int id)
+{
+       switch (id) {
+       case MT8195_AFE_IO_ETDM1_IN:
+               fallthrough;
+       case MT8195_AFE_IO_ETDM2_IN:
+               fallthrough;
+       case MT8195_AFE_IO_ETDM1_OUT:
+               fallthrough;
+       case MT8195_AFE_IO_ETDM2_OUT:
+               fallthrough;
+       case MT8195_AFE_IO_DPTX:
+               fallthrough;
+       case MT8195_AFE_IO_ETDM3_OUT:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool mt8195_afe_hdmitx_dptx_is_valid(int id)
+{
+       switch (id) {
+       case MT8195_AFE_IO_DPTX:
+               fallthrough;
+       case MT8195_AFE_IO_ETDM3_OUT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int get_etdm_fs_timing(unsigned int rate)
 {
        int i;
@@ -236,8 +268,12 @@ static int is_cowork_mode(struct snd_soc_dai *dai)
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
 
+       if (!mt8195_afe_etdm_is_valid(dai->id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai->id];
        return (etdm_data->cowork_slv_count > 0 ||
                etdm_data->cowork_source_id != COWORK_ETDM_NONE);
 }
@@ -264,8 +300,14 @@ static int get_etdm_cowork_master_id(struct snd_soc_dai *dai)
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
-       int dai_id = etdm_data->cowork_source_id;
+       struct mtk_dai_etdm_priv *etdm_data;
+       int dai_id;
+
+       if (!mt8195_afe_etdm_is_valid(dai->id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai->id];
+       dai_id = etdm_data->cowork_source_id;
 
        if (dai_id == COWORK_ETDM_NONE)
                dai_id = dai->id;
@@ -1276,9 +1318,13 @@ static int mt8195_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id)
        int ret = 0;
        struct etdm_con_reg etdm_reg;
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        unsigned long flags;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
        spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
        etdm_data->en_ref_cnt++;
        if (etdm_data->en_ref_cnt == 1) {
@@ -1299,9 +1345,13 @@ static int mt8195_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id)
        int ret = 0;
        struct etdm_con_reg etdm_reg;
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        unsigned long flags;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
        spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
        if (etdm_data->en_ref_cnt > 0) {
                etdm_data->en_ref_cnt--;
@@ -1357,12 +1407,16 @@ static int etdm_cowork_slv_sel(int id, int slave_mode)
 static int mt8195_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        unsigned int reg = 0;
        unsigned int mask;
        unsigned int val;
        int cowork_source_sel;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
        if (etdm_data->cowork_source_id == COWORK_ETDM_NONE)
                return 0;
 
@@ -1532,8 +1586,10 @@ static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
 
        if (is_cowork_mode(dai)) {
                mst_dai_id = get_etdm_cowork_master_id(dai);
-               mtk_dai_etdm_enable_mclk(afe, mst_dai_id);
+               if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+                       return -EINVAL;
 
+               mtk_dai_etdm_enable_mclk(afe, mst_dai_id);
                cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
                if (cg_id >= 0)
                        mt8195_afe_enable_clk(afe, afe_priv->clk[cg_id]);
@@ -1571,6 +1627,9 @@ static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
 
        if (is_cowork_mode(dai)) {
                mst_dai_id = get_etdm_cowork_master_id(dai);
+               if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+                       return;
+
                cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
                if (cg_id >= 0)
                        mt8195_afe_disable_clk(afe, afe_priv->clk[cg_id]);
@@ -1631,16 +1690,24 @@ static int mtk_dai_etdm_in_configure(struct mtk_base_afe *afe,
                                     int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        struct etdm_con_reg etdm_reg;
-       bool slave_mode = etdm_data->slave_mode;
-       unsigned int data_mode = etdm_data->data_mode;
-       unsigned int lrck_width = etdm_data->lrck_width;
+       bool slave_mode;
+       unsigned int data_mode;
+       unsigned int lrck_width;
        unsigned int val = 0;
        unsigned int mask = 0;
        int i;
        int ret;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
+       slave_mode = etdm_data->slave_mode;
+       data_mode = etdm_data->data_mode;
+       lrck_width = etdm_data->lrck_width;
+
        dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
                __func__, rate, channels, dai_id);
 
@@ -1748,15 +1815,22 @@ static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
                                      int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        struct etdm_con_reg etdm_reg;
-       bool slave_mode = etdm_data->slave_mode;
-       unsigned int lrck_width = etdm_data->lrck_width;
+       bool slave_mode;
+       unsigned int lrck_width;
        unsigned int val = 0;
        unsigned int mask = 0;
        int ret;
        int fs = 0;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
+       slave_mode = etdm_data->slave_mode;
+       lrck_width = etdm_data->lrck_width;
+
        dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
                __func__, rate, channels, dai_id);
 
@@ -1837,7 +1911,7 @@ static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
 static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
        int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
        int apll;
@@ -1850,6 +1924,10 @@ static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id)
        if (clk_id < 0 || clkdiv_id < 0)
                return 0;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
        ret = get_etdm_reg(dai_id, &etdm_reg);
        if (ret < 0)
                return ret;
@@ -1888,9 +1966,9 @@ static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
                                  int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        struct etdm_con_reg etdm_reg;
-       bool slave_mode = etdm_data->slave_mode;
+       bool slave_mode;
        unsigned int etdm_channels;
        unsigned int val = 0;
        unsigned int mask = 0;
@@ -1898,6 +1976,11 @@ static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
        unsigned int wlen = get_etdm_wlen(bit_width);
        int ret;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
+       slave_mode = etdm_data->slave_mode;
        ret = get_etdm_reg(dai_id, &etdm_reg);
        if (ret < 0)
                return ret;
@@ -1973,6 +2056,8 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
 
        if (is_cowork_mode(dai)) {
                mst_dai_id = get_etdm_cowork_master_id(dai);
+               if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+                       return -EINVAL;
 
                ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id);
                if (ret)
@@ -2024,6 +2109,9 @@ static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_RESUME:
                if (is_cowork_mode(dai)) {
                        mst_dai_id = get_etdm_cowork_master_id(dai);
+                       if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+                               return -EINVAL;
+
                        mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
 
                        //open master first
@@ -2040,6 +2128,9 @@ static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_SUSPEND:
                if (is_cowork_mode(dai)) {
                        mst_dai_id = get_etdm_cowork_master_id(dai);
+                       if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+                               return -EINVAL;
+
                        mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
 
                        for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
@@ -2061,10 +2152,14 @@ static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
 static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id)
 {
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai_id];
+       struct mtk_dai_etdm_priv *etdm_data;
        int apll;
        int apll_rate;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai_id];
        if (freq == 0) {
                etdm_data->mclk_freq = freq;
                return 0;
@@ -2104,6 +2199,9 @@ static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai,
        else
                dai_id = dai->id;
 
+       if (!mt8195_afe_etdm_is_valid(dai_id))
+               return -EINVAL;
+
        etdm_data = afe_priv->dai_priv[dai_id];
        etdm_data->mclk_dir = dir;
        return mtk_dai_etdm_cal_mclk(afe, freq, dai_id);
@@ -2115,8 +2213,12 @@ static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai,
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
+
+       if (!mt8195_afe_etdm_is_valid(dai->id))
+               return -EINVAL;
 
+       etdm_data = afe_priv->dai_priv[dai->id];
        dev_dbg(dai->dev, "%s id %d slot_width %d\n",
                __func__, dai->id, slot_width);
 
@@ -2129,8 +2231,12 @@ static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
+
+       if (!mt8195_afe_etdm_is_valid(dai->id))
+               return -EINVAL;
 
+       etdm_data = afe_priv->dai_priv[dai->id];
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
@@ -2248,13 +2354,18 @@ static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream,
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
        unsigned int rate = params_rate(params);
        unsigned int channels = params_channels(params);
        snd_pcm_format_t format = params_format(params);
        int width = snd_pcm_format_physical_width(format);
        int ret = 0;
 
+       if (!mt8195_afe_hdmitx_dptx_is_valid(dai->id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai->id];
+
        /* dptx configure */
        if (dai->id == MT8195_AFE_IO_DPTX) {
                regmap_update_bits(afe->regmap, AFE_DPTX_CON,
@@ -2331,7 +2442,12 @@ static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
+
+       if (!mt8195_afe_hdmitx_dptx_is_valid(dai->id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai->id];
 
        dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
                __func__, dai->id, freq, dir);
@@ -2370,10 +2486,14 @@ static int mtk_dai_etdm_probe(struct snd_soc_dai *dai)
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_etdm_priv *etdm_data;
 
        dev_dbg(dai->dev, "%s id %d\n", __func__, dai->id);
 
+       if (!mt8195_afe_etdm_is_valid(dai->id))
+               return -EINVAL;
+
+       etdm_data = afe_priv->dai_priv[dai->id];
        if (etdm_data->mclk_freq) {
                dev_dbg(afe->dev, "MCLK always on, rate %d\n",
                        etdm_data->mclk_freq);
@@ -2477,6 +2597,11 @@ static void mt8195_etdm_update_sync_info(struct mtk_base_afe *afe)
                etdm_data = afe_priv->dai_priv[i];
                if (etdm_data->cowork_source_id != COWORK_ETDM_NONE) {
                        mst_dai_id = etdm_data->cowork_source_id;
+                       if (!mt8195_afe_etdm_is_valid(mst_dai_id)) {
+                               dev_err(afe->dev, "%s invalid dai id %d\n",
+                                       __func__, mst_dai_id);
+                               return;
+                       }
                        mst_data = afe_priv->dai_priv[mst_dai_id];
                        if (mst_data->cowork_source_id != COWORK_ETDM_NONE)
                                dev_info(afe->dev, "%s [%d] wrong sync source\n"
@@ -2513,6 +2638,12 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
 
        for (i = 0; i < MT8195_AFE_IO_ETDM_NUM; i++) {
                dai_id = ETDM_TO_DAI_ID(i);
+               if (!mt8195_afe_etdm_is_valid(dai_id)) {
+                       dev_err(afe->dev, "%s invalid dai id %d\n",
+                               __func__, dai_id);
+                       return;
+               }
+
                etdm_data = afe_priv->dai_priv[dai_id];
 
                ret = snprintf(prop, sizeof(prop),
index 051433689ff58ac198024dce5829c64461f25176..6d6d79300d5121afe124fcf5371fc9d1bf774f0d 100644 (file)
@@ -122,17 +122,26 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime * const runtime = substream->runtime;
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_pcmif_priv *pcmif_priv = afe_priv->dai_priv[dai->id];
-       unsigned int slave_mode = pcmif_priv->slave_mode;
-       unsigned int lrck_inv = pcmif_priv->lrck_inv;
-       unsigned int bck_inv = pcmif_priv->bck_inv;
-       unsigned int fmt = pcmif_priv->format;
+       struct mtk_dai_pcmif_priv *pcmif_priv;
+       unsigned int slave_mode;
+       unsigned int lrck_inv;
+       unsigned int bck_inv;
+       unsigned int fmt;
        unsigned int bit_width = dai->sample_bits;
        unsigned int val = 0;
        unsigned int mask = 0;
        int fs = 0;
        int mode = 0;
 
+       if (dai->id != MT8195_AFE_IO_PCM)
+               return -EINVAL;
+
+       pcmif_priv = afe_priv->dai_priv[dai->id];
+       slave_mode = pcmif_priv->slave_mode;
+       lrck_inv = pcmif_priv->lrck_inv;
+       bck_inv = pcmif_priv->bck_inv;
+       fmt = pcmif_priv->format;
+
        /* sync freq mode */
        fs = mt8195_afe_fs_timing(runtime->rate);
        if (fs < 0)
@@ -230,10 +239,15 @@ static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtk_dai_pcmif_priv *pcmif_priv = afe_priv->dai_priv[dai->id];
+       struct mtk_dai_pcmif_priv *pcmif_priv;
 
        dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt);
 
+       if (dai->id != MT8195_AFE_IO_PCM)
+               return -EINVAL;
+
+       pcmif_priv = afe_priv->dai_priv[dai->id];
+
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                pcmif_priv->format = MTK_DAI_PCM_FMT_I2S;
index 4682748d82bed46e649ad6406817f920f1e81547..ceca882ecff7b20a2d9cafb41d28c4ba3f58e647 100644 (file)
@@ -158,7 +158,7 @@ static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
        int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
        int mtkaif_calibration_num_phase;
        bool mtkaif_calibration_ok;
-       unsigned int monitor;
+       unsigned int monitor = 0;
        int counter;
        int phase;
        int i;
index 88e611e64d14f54fcd10caa5c5c0e49f43dec680..da351a60df0c40d406cf093012ce3469a42d16a3 100644 (file)
@@ -331,11 +331,9 @@ err:
        return ret;
 }
 
-static int aiu_remove(struct platform_device *pdev)
+static void aiu_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
-
-       return 0;
 }
 
 static const struct aiu_platform_data aiu_gxbb_pdata = {
@@ -364,7 +362,7 @@ MODULE_DEVICE_TABLE(of, aiu_of_match);
 
 static struct platform_driver aiu_pdrv = {
        .probe = aiu_probe,
-       .remove = aiu_remove,
+       .remove_new = aiu_remove,
        .driver = {
                .name = "meson-aiu",
                .of_match_table = aiu_of_match,
index 2b77010c2c5ce8398c330e8c8e13cc9248daa828..a25c397c66c5a8664f75f37b1237dcf7d982f851 100644 (file)
@@ -337,7 +337,8 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
                return ret;
 
        if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
-               dai_link->params = &codec_params;
+               dai_link->c2c_params = &codec_params;
+               dai_link->num_c2c_params = 1;
        } else {
                dai_link->no_pcm = 1;
                snd_soc_dai_link_set_capabilities(dai_link);
index 7624aafe9009a3d96bf4a8cfe05d9bddba64fe94..5e5e4c56d5051fffd07f1cfb4adab06454462533 100644 (file)
@@ -496,7 +496,7 @@ static int axg_tdm_iface_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct snd_soc_dai_driver *dai_drv;
        struct axg_tdm_iface *iface;
-       int ret, i;
+       int i;
 
        iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
        if (!iface)
@@ -533,14 +533,9 @@ static int axg_tdm_iface_probe(struct platform_device *pdev)
         * At this point, ignore the error if mclk is missing. We'll
         * throw an error if the cpu dai is master and mclk is missing
         */
-       iface->mclk = devm_clk_get(dev, "mclk");
-       if (IS_ERR(iface->mclk)) {
-               ret = PTR_ERR(iface->mclk);
-               if (ret == -ENOENT)
-                       iface->mclk = NULL;
-               else
-                       return dev_err_probe(dev, ret, "failed to get mclk\n");
-       }
+       iface->mclk = devm_clk_get_optional(dev, "mclk");
+       if (IS_ERR(iface->mclk))
+               return dev_err_probe(dev, PTR_ERR(iface->mclk), "failed to get mclk\n");
 
        return devm_snd_soc_register_component(dev,
                                        &axg_tdm_iface_component_drv, dai_drv,
index 5119434a81c4283ba69559bf44fa21b87ca72d58..58c411d3c48971eaa815d485463363ab9aa280c2 100644 (file)
@@ -104,7 +104,8 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
 
        /* Or apply codec to codec params if necessary */
        if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) {
-               dai_link->params = &codec_params;
+               dai_link->c2c_params = &codec_params;
+               dai_link->num_c2c_params = 1;
        } else {
                dai_link->no_pcm = 1;
                snd_soc_dai_link_set_capabilities(dai_link);
index 5913486c43ab1e59f3a2f3bfe5339449d21d8fdd..e702d408ee966cd4b43c606bf46653f552495388 100644 (file)
@@ -105,13 +105,14 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
        if (!in_data)
                return -ENODEV;
 
-       if (WARN_ON(!rtd->dai_link->params)) {
+       if (WARN_ON(!rtd->dai_link->c2c_params)) {
                dev_warn(dai->dev, "codec2codec link expected\n");
                return -EINVAL;
        }
 
        /* Replace link params with the input params */
-       rtd->dai_link->params = &in_data->params;
+       rtd->dai_link->c2c_params = &in_data->params;
+       rtd->dai_link->num_c2c_params = 1;
 
        return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt);
 }
index 746f409386751c820c294aedff12915cbf2eec1d..457c3a72a4147b47850360a4581463f77fb9d1eb 100644 (file)
@@ -150,7 +150,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       if (of_find_property(np, "audio-routing", NULL)) {
+       if (of_property_present(np, "audio-routing")) {
                card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
                card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
 
@@ -169,11 +169,9 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int mxs_sgtl5000_remove(struct platform_device *pdev)
+static void mxs_sgtl5000_remove(struct platform_device *pdev)
 {
        mxs_saif_put_mclk(0);
-
-       return 0;
 }
 
 static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
@@ -188,7 +186,7 @@ static struct platform_driver mxs_sgtl5000_audio_driver = {
                .of_match_table = mxs_sgtl5000_dt_ids,
        },
        .probe = mxs_sgtl5000_probe,
-       .remove = mxs_sgtl5000_remove,
+       .remove_new = mxs_sgtl5000_remove,
 };
 
 module_platform_driver(mxs_sgtl5000_audio_driver);
index b3c1744eff91aec505f34333dfa31b10a755eefb..a1ed141b8795cbe3a84b2c8943c55cbbd452c48b 100644 (file)
@@ -545,7 +545,7 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int asoc_mmp_sspa_remove(struct platform_device *pdev)
+static void asoc_mmp_sspa_remove(struct platform_device *pdev)
 {
        struct sspa_priv *sspa = platform_get_drvdata(pdev);
 
@@ -553,11 +553,10 @@ static int asoc_mmp_sspa_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
 
        if (pdev->dev.of_node)
-               return 0;
+               return;
 
        clk_put(sspa->audio_clk);
        clk_put(sspa->sysclk);
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -575,7 +574,7 @@ static struct platform_driver asoc_mmp_sspa_driver = {
                .of_match_table = of_match_ptr(mmp_sspa_of_match),
        },
        .probe = asoc_mmp_sspa_probe,
-       .remove = asoc_mmp_sspa_remove,
+       .remove_new = asoc_mmp_sspa_remove,
 };
 
 module_platform_driver(asoc_mmp_sspa_driver);
index 809ea34736edc9305ec339833c4c6a9339ff06b6..e73bd62c033c5330313cb9f8f7fb039eb02e9ca6 100644 (file)
@@ -263,13 +263,12 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
                                          pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver));
 }
 
-static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_dev_remove(struct platform_device *pdev)
 {
        struct ac97_controller *ctrl = platform_get_drvdata(pdev);
 
        snd_ac97_controller_unregister(ctrl);
        pxa2xx_ac97_hw_remove(pdev);
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -289,7 +288,7 @@ static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
 
 static struct platform_driver pxa2xx_ac97_driver = {
        .probe          = pxa2xx_ac97_dev_probe,
-       .remove         = pxa2xx_ac97_dev_remove,
+       .remove_new     = pxa2xx_ac97_dev_remove,
        .driver         = {
                .name   = "pxa2xx-ac97",
 #ifdef CONFIG_PM_SLEEP
index 96fe80241fb417d7eda94db912e3dbd8a9ac7a53..c1f24af17506c4e8fb6c4ea79f96dea3de7751e1 100644 (file)
@@ -180,6 +180,18 @@ err_put_np:
 }
 EXPORT_SYMBOL_GPL(qcom_snd_parse_of);
 
+static struct snd_soc_jack_pin qcom_headset_jack_pins[] = {
+       /* Headset */
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
 int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
                            struct snd_soc_jack *jack, bool *jack_setup)
 {
@@ -189,13 +201,14 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
        int rval, i;
 
        if (!*jack_setup) {
-               rval = snd_soc_card_jack_new(card, "Headset Jack",
+               rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
                                             SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                             SND_JACK_MECHANICAL |
                                             SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                             SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                             SND_JACK_BTN_4 | SND_JACK_BTN_5,
-                                            jack);
+                                            jack, qcom_headset_jack_pins,
+                                            ARRAY_SIZE(qcom_headset_jack_pins));
 
                if (rval < 0) {
                        dev_err(card->dev, "Unable to add Headphone Jack\n");
index 23d23bc6fbaa77888e2b74f4f8e67cacad4803a7..420e8aa11f4212e86c51eb02276afd9303d5d796 100644 (file)
@@ -130,6 +130,9 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s
        if (dai_data->is_port_started[dai->id]) {
                q6apm_graph_stop(dai_data->graph[dai->id]);
                dai_data->is_port_started[dai->id] = false;
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       q6apm_graph_close(dai_data->graph[dai->id]);
        }
 
        /**
index 994c9e823a8870ff1f5b96450518192f6bb2c15e..a7a3f973eb6d502876fe8bb6a55ea878d3c2ff9e 100644 (file)
@@ -676,7 +676,7 @@ static int apm_probe(gpr_device_t *gdev)
 
        ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
        if (ret < 0) {
-               dev_err(dev, "failed to get register q6apm: %d\n", ret);
+               dev_err(dev, "failed to register q6apm: %d\n", ret);
                return ret;
        }
 
index 928fd23e2c27271a49a2376ffdf86ad3e85fa82b..bba07899f8fc197ef9c77ce4b5ccd4b372649a9d 100644 (file)
@@ -1140,12 +1140,10 @@ static int q6pcm_routing_probe(struct platform_device *pdev)
                                          NULL, 0);
 }
 
-static int q6pcm_routing_remove(struct platform_device *pdev)
+static void q6pcm_routing_remove(struct platform_device *pdev)
 {
        kfree(routing_data);
        routing_data = NULL;
-
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -1162,7 +1160,7 @@ static struct platform_driver q6pcm_routing_platform_driver = {
                .of_match_table = of_match_ptr(q6pcm_routing_device_id),
        },
        .probe = q6pcm_routing_probe,
-       .remove = q6pcm_routing_remove,
+       .remove_new = q6pcm_routing_remove,
 };
 module_platform_driver(q6pcm_routing_platform_driver);
 
index 10249519a39e569954b68e3522d85cf955db17a8..1a41419c7eb8f7f7677e89768b052342632fc19e 100644 (file)
@@ -32,11 +32,8 @@ int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
                return 0;
        }
 
-       if (*stream_prepared) {
-               sdw_disable_stream(sruntime);
-               sdw_deprepare_stream(sruntime);
-               *stream_prepared = false;
-       }
+       if (*stream_prepared)
+               return 0;
 
        ret = sdw_prepare_stream(sruntime);
        if (ret)
index a8758ad68442daba7bdbe22dce06851e0c6fa96f..575a0b9b01e92928bb701c56543b36e3208b5e0f 100644 (file)
@@ -659,6 +659,7 @@ static const struct of_device_id rockchip_i2s_match[] __maybe_unused = {
        { .compatible = "rockchip,rk3366-i2s", },
        { .compatible = "rockchip,rk3368-i2s", },
        { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
+       { .compatible = "rockchip,rk3588-i2s", },
        { .compatible = "rockchip,rv1126-i2s", },
        {},
 };
@@ -850,7 +851,7 @@ err_clk:
        return ret;
 }
 
-static int rockchip_i2s_remove(struct platform_device *pdev)
+static void rockchip_i2s_remove(struct platform_device *pdev)
 {
        struct rk_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
 
@@ -859,8 +860,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
                i2s_runtime_suspend(&pdev->dev);
 
        clk_disable_unprepare(i2s->hclk);
-
-       return 0;
 }
 
 static const struct dev_pm_ops rockchip_i2s_pm_ops = {
@@ -870,7 +869,7 @@ static const struct dev_pm_ops rockchip_i2s_pm_ops = {
 
 static struct platform_driver rockchip_i2s_driver = {
        .probe = rockchip_i2s_probe,
-       .remove = rockchip_i2s_remove,
+       .remove_new = rockchip_i2s_remove,
        .driver = {
                .name = DRV_NAME,
                .of_match_table = of_match_ptr(rockchip_i2s_match),
index 6ce92b1db7900ca6098ef5d5bc6c64702d8542e8..52f9aae60be8ad5fd9029d1347581b16f182f502 100644 (file)
@@ -661,7 +661,7 @@ err_pm_disable:
        return ret;
 }
 
-static int rockchip_pdm_remove(struct platform_device *pdev)
+static void rockchip_pdm_remove(struct platform_device *pdev)
 {
        struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
 
@@ -671,8 +671,6 @@ static int rockchip_pdm_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(pdm->clk);
        clk_disable_unprepare(pdm->hclk);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -710,7 +708,7 @@ static const struct dev_pm_ops rockchip_pdm_pm_ops = {
 
 static struct platform_driver rockchip_pdm_driver = {
        .probe  = rockchip_pdm_probe,
-       .remove = rockchip_pdm_remove,
+       .remove_new = rockchip_pdm_remove,
        .driver = {
                .name = "rockchip-pdm",
                .of_match_table = of_match_ptr(rockchip_pdm_match),
index d07cc5c813f2543f0b3b8aaee65dd90c48f35d6e..e73a342b7953c67f571a6104db08c46abd39042c 100644 (file)
@@ -206,14 +206,12 @@ put_codec_of_node:
        return ret;
 }
 
-static int snd_rk_mc_remove(struct platform_device *pdev)
+static void snd_rk_mc_remove(struct platform_device *pdev)
 {
        of_node_put(rk_dailink.cpus->of_node);
        rk_dailink.cpus->of_node = NULL;
        of_node_put(rk_dailink.codecs->of_node);
        rk_dailink.codecs->of_node = NULL;
-
-       return 0;
 }
 
 static const struct of_device_id rockchip_rt5645_of_match[] = {
@@ -225,7 +223,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
 
 static struct platform_driver snd_rk_mc_driver = {
        .probe = snd_rk_mc_probe,
-       .remove = snd_rk_mc_remove,
+       .remove_new = snd_rk_mc_remove,
        .driver = {
                .name = DRV_NAME,
                .pm = &snd_soc_pm_ops,
index 2d937fcf357d4697ae6548500f79e2e54a7c90fd..0b73fe94e4bbdc5a15111d304ebf87a685dc2a98 100644 (file)
@@ -367,13 +367,11 @@ err_pm_runtime:
        return ret;
 }
 
-static int rk_spdif_remove(struct platform_device *pdev)
+static void rk_spdif_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                rk_spdif_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops rk_spdif_pm_ops = {
@@ -383,7 +381,7 @@ static const struct dev_pm_ops rk_spdif_pm_ops = {
 
 static struct platform_driver rk_spdif_driver = {
        .probe = rk_spdif_probe,
-       .remove = rk_spdif_remove,
+       .remove_new = rk_spdif_remove,
        .driver = {
                .name = "rockchip-spdif",
                .of_match_table = of_match_ptr(rk_spdif_match),
index 0fbbf3b02c09570fb531b86acb402868808ed5bf..7492bb41456c700744c00630351f9995540dfeae 100644 (file)
@@ -483,14 +483,16 @@ static struct snd_soc_dai_link aries_dai[] = {
                .name = "WM8994 AIF2",
                .stream_name = "Baseband",
                .init = &aries_baseband_init,
-               .params = &baseband_params,
+               .c2c_params = &baseband_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(baseband),
        },
        {
                .name = "WM8994 AIF3",
                .stream_name = "Bluetooth",
-               .params = &bluetooth_params,
+               .c2c_params = &bluetooth_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(bluetooth),
        },
index a5dc640d0d768b0250ad6799f424ef4708bbc1fe..fdff83e72d29acdecd5e29188ac8aa64d843334c 100644 (file)
@@ -185,12 +185,11 @@ err_put_of_nodes:
        return ret;
 }
 
-static int arndale_audio_remove(struct platform_device *pdev)
+static void arndale_audio_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        arndale_put_of_nodes(card);
-       return 0;
 }
 
 static const struct of_device_id arndale_audio_of_match[] = {
@@ -208,7 +207,7 @@ static struct platform_driver arndale_audio_driver = {
                .of_match_table = arndale_audio_of_match,
        },
        .probe = arndale_audio_probe,
-       .remove = arndale_audio_remove,
+       .remove_new = arndale_audio_remove,
 };
 
 module_platform_driver(arndale_audio_driver);
index 76998a4a4cad39abb5988807f06cbfebcc6c2677..70b63d4faa993bb0111212f4717f7e7a435fda61 100644 (file)
@@ -264,7 +264,8 @@ static struct snd_soc_dai_link bells_dai_wm2200[] = {
                .stream_name = "DSP-CODEC",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(wm2200_dsp_codec),
        },
@@ -300,7 +301,8 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = {
                .stream_name = "DSP-CODEC",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(wm5102_dsp_codec),
        },
@@ -310,7 +312,8 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
                .ignore_suspend = 1,
-               .params = &baseband_params,
+               .c2c_params = &baseband_params,
+               .num_c2c_params = 1,
                SND_SOC_DAILINK_REG(wm5102_baseband),
        },
        {
@@ -319,7 +322,8 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBS_CFS,
                .ignore_suspend = 1,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                SND_SOC_DAILINK_REG(wm5102_sub),
        },
 };
@@ -355,7 +359,8 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = {
                .stream_name = "DSP-CODEC",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(wm5110_dsp_codec),
        },
@@ -365,7 +370,8 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
                .ignore_suspend = 1,
-               .params = &baseband_params,
+               .c2c_params = &baseband_params,
+               .num_c2c_params = 1,
                SND_SOC_DAILINK_REG(wm5110_baseband),
        },
        {
@@ -374,7 +380,8 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBS_CFS,
                .ignore_suspend = 1,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                SND_SOC_DAILINK_REG(wm5110_sub),
        },
 };
index 6f96032090de96de6d974bd640f96d28d57ad6ed..f3d98abd5f0d3c088341019f3e26544bacf12967 100644 (file)
@@ -1289,7 +1289,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv)
        int ret, i;
 
        /* Register the clock provider only if it's expected in the DTB */
-       if (!of_find_property(dev->of_node, "#clock-cells", NULL))
+       if (!of_property_present(dev->of_node, "#clock-cells"))
                return 0;
 
        /* Get the RCLKSRC mux clock parent clock names */
@@ -1560,13 +1560,13 @@ err_disable_clk:
        return ret;
 }
 
-static int samsung_i2s_remove(struct platform_device *pdev)
+static void samsung_i2s_remove(struct platform_device *pdev)
 {
        struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev);
 
        /* The secondary device has no driver data assigned */
        if (!priv)
-               return 0;
+               return;
 
        pm_runtime_get_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -1576,8 +1576,6 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        clk_disable_unprepare(priv->clk);
 
        pm_runtime_put_noidle(&pdev->dev);
-
-       return 0;
 }
 
 static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream,
@@ -1746,7 +1744,7 @@ static const struct dev_pm_ops samsung_i2s_pm = {
 
 static struct platform_driver samsung_i2s_driver = {
        .probe  = samsung_i2s_probe,
-       .remove = samsung_i2s_remove,
+       .remove_new = samsung_i2s_remove,
        .id_table = samsung_i2s_driver_ids,
        .driver = {
                .name = "samsung-i2s",
index 411e25cec5913a823059fecd255b47b5043bda9c..5d8118e69359395a48d3b306fece8e6d46a67329 100644 (file)
@@ -167,7 +167,8 @@ static struct snd_soc_dai_link littlemill_dai[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
                .ignore_suspend = 1,
-               .params = &baseband_params,
+               .c2c_params = &baseband_params,
+               .num_c2c_params = 1,
                SND_SOC_DAILINK_REG(baseband),
        },
 };
index b44f5e92224f731ca4f02d0908ea9902f099e966..106770be6fc5afae3ba5a4aa0b69c99b7011b3b9 100644 (file)
@@ -119,7 +119,8 @@ static struct snd_soc_dai_link lowland_dai[] = {
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                                SND_SOC_DAIFMT_CBM_CFM,
                .ignore_suspend = 1,
-               .params = &sub_params,
+               .c2c_params = &sub_params,
+               .num_c2c_params = 1,
                .init = lowland_wm9081_init,
                SND_SOC_DAILINK_REG(speaker),
        },
index 1e0fefa89ad5e2196915efc2087ed59a8c75e6af..fd95a79cc9fa65ae51efd9f40ad8f186a2d030f5 100644 (file)
@@ -331,15 +331,13 @@ err_put_node:
        return ret;
 }
 
-static int odroid_audio_remove(struct platform_device *pdev)
+static void odroid_audio_remove(struct platform_device *pdev)
 {
        struct odroid_priv *priv = platform_get_drvdata(pdev);
 
        snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
        clk_put(priv->sclk_i2s);
        clk_put(priv->clk_i2s_bus);
-
-       return 0;
 }
 
 static const struct of_device_id odroid_audio_of_match[] = {
@@ -358,7 +356,7 @@ static struct platform_driver odroid_audio_driver = {
                .pm             = &snd_soc_pm_ops,
        },
        .probe  = odroid_audio_probe,
-       .remove = odroid_audio_remove,
+       .remove_new = odroid_audio_remove,
 };
 module_platform_driver(odroid_audio_driver);
 
index e859252ae5e6eb87b29c0a239a4590ca5f43b53f..335fe5cb9cfc67c88b0f13cc196cb7107f1f4f63 100644 (file)
@@ -579,20 +579,18 @@ err_dis_cclk:
        return ret;
 }
 
-static int s3c_pcm_dev_remove(struct platform_device *pdev)
+static void s3c_pcm_dev_remove(struct platform_device *pdev)
 {
        struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
 
        pm_runtime_disable(&pdev->dev);
        clk_disable_unprepare(pcm->cclk);
        clk_disable_unprepare(pcm->pclk);
-
-       return 0;
 }
 
 static struct platform_driver s3c_pcm_driver = {
        .probe  = s3c_pcm_dev_probe,
-       .remove = s3c_pcm_dev_remove,
+       .remove_new = s3c_pcm_dev_remove,
        .driver = {
                .name = "samsung-pcm",
        },
index da342da038804cf3b19053317f28ea7eac174e14..334080e631af06ffeca78d1e26f0bb23e051e41a 100644 (file)
@@ -219,7 +219,7 @@ static int snow_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int snow_remove(struct platform_device *pdev)
+static void snow_remove(struct platform_device *pdev)
 {
        struct snow_priv *priv = platform_get_drvdata(pdev);
        struct snd_soc_dai_link *link = &priv->dai_link;
@@ -229,8 +229,6 @@ static int snow_remove(struct platform_device *pdev)
        snd_soc_of_put_dai_link_codecs(link);
 
        clk_put(priv->clk_i2s_bus);
-
-       return 0;
 }
 
 static const struct of_device_id snow_of_match[] = {
@@ -248,7 +246,7 @@ static struct platform_driver snow_driver = {
                .of_match_table = snow_of_match,
        },
        .probe = snow_probe,
-       .remove = snow_remove,
+       .remove_new = snow_remove,
 };
 
 module_platform_driver(snow_driver);
index 7d815e237e5c6f92859d2b302d93410403ef72ec..28dc1bbfc8e77cd432271dc08bcfa9e5aca7aa0d 100644 (file)
@@ -460,7 +460,7 @@ err0:
        return ret;
 }
 
-static int spdif_remove(struct platform_device *pdev)
+static void spdif_remove(struct platform_device *pdev)
 {
        struct samsung_spdif_info *spdif = &spdif_info;
        struct resource *mem_res;
@@ -472,13 +472,11 @@ static int spdif_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(spdif->sclk);
        clk_disable_unprepare(spdif->pclk);
-
-       return 0;
 }
 
 static struct platform_driver samsung_spdif_driver = {
        .probe  = spdif_probe,
-       .remove = spdif_remove,
+       .remove_new = spdif_remove,
        .driver = {
                .name   = "samsung-spdif",
        },
index 69d7b0115b38d3e1de9fc892ad7162abe10ad2b4..22e2ad63d64dac1b325ac1aa0d8550351357dc55 100644 (file)
@@ -219,7 +219,8 @@ static struct snd_soc_dai_link speyside_dai[] = {
                .init = speyside_wm8996_init,
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                | SND_SOC_DAIFMT_CBM_CFM,
-               .params = &dsp_codec_params,
+               .c2c_params = &dsp_codec_params,
+               .num_c2c_params = 1,
                .ignore_suspend = 1,
                SND_SOC_DAILINK_REG(dsp_codec),
        },
index f3edc2e3d9d7c492391e2c1f8eb682b87feeb410..1051c306292f88bb008cffc22ccf4ffc3e57c196 100644 (file)
@@ -1855,7 +1855,7 @@ static void fsi_of_parse(char *name,
 
        for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
                sprintf(prop, "%s,%s", name, of_parse_property[i].name);
-               if (of_get_property(np, prop, NULL))
+               if (of_property_present(np, prop))
                        flags |= of_parse_property[i].val;
        }
        info->flags = flags;
@@ -2030,7 +2030,7 @@ exit_fsia:
        return ret;
 }
 
-static int fsi_remove(struct platform_device *pdev)
+static void fsi_remove(struct platform_device *pdev)
 {
        struct fsi_master *master;
 
@@ -2040,8 +2040,6 @@ static int fsi_remove(struct platform_device *pdev)
 
        fsi_stream_remove(&master->fsia);
        fsi_stream_remove(&master->fsib);
-
-       return 0;
 }
 
 static void __fsi_suspend(struct fsi_priv *fsi,
@@ -2108,7 +2106,7 @@ static struct platform_driver fsi_driver = {
                .of_match_table = fsi_of_match,
        },
        .probe          = fsi_probe,
-       .remove         = fsi_remove,
+       .remove_new     = fsi_remove,
        .id_table       = fsi_id_table,
 };
 
index 46d145cbaf29708e26cf03ef1a1a5f8d56897037..cc200f45826c3e5136db53bb8484aee5bcc1ad6e 100644 (file)
@@ -323,10 +323,9 @@ static int hac_soc_platform_probe(struct platform_device *pdev)
                                          sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
 }
 
-static int hac_soc_platform_remove(struct platform_device *pdev)
+static void hac_soc_platform_remove(struct platform_device *pdev)
 {
        snd_soc_set_ac97_ops(NULL);
-       return 0;
 }
 
 static struct platform_driver hac_pcm_driver = {
@@ -335,7 +334,7 @@ static struct platform_driver hac_pcm_driver = {
        },
 
        .probe = hac_soc_platform_probe,
-       .remove = hac_soc_platform_remove,
+       .remove_new = hac_soc_platform_remove,
 };
 
 module_platform_driver(hac_pcm_driver);
index cb17f7d0cf0c197afe672997e56c4e764b2b7116..6a522e6dd85ab27c2e90faa091fd8bf6a1ada274 100644 (file)
@@ -1987,7 +1987,7 @@ exit_snd_probe:
        return ret;
 }
 
-static int rsnd_remove(struct platform_device *pdev)
+static void rsnd_remove(struct platform_device *pdev)
 {
        struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
        struct rsnd_dai *rdai;
@@ -2019,8 +2019,6 @@ static int rsnd_remove(struct platform_device *pdev)
 
        for (i = 0; i < ARRAY_SIZE(remove_func); i++)
                remove_func[i](priv);
-
-       return 0;
 }
 
 static int __maybe_unused rsnd_suspend(struct device *dev)
@@ -2052,7 +2050,7 @@ static struct platform_driver rsnd_driver = {
                .of_match_table = rsnd_of_match,
        },
        .probe          = rsnd_probe,
-       .remove         = rsnd_remove,
+       .remove_new     = rsnd_remove,
 };
 module_platform_driver(rsnd_driver);
 
index 8ddee5b03ece0d3b43a3e7aeeef1d117b4223769..690ac0d6ef41a8f3a77ddd753dfb0db63e2dbabe 100644 (file)
@@ -1211,10 +1211,10 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
                        goto rsnd_ssi_probe_done;
                }
 
-               if (of_get_property(np, "shared-pin", NULL))
+               if (of_property_read_bool(np, "shared-pin"))
                        rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE);
 
-               if (of_get_property(np, "no-busif", NULL))
+               if (of_property_read_bool(np, "no-busif"))
                        rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF);
 
                ssi->irq = irq_of_parse_and_map(np, 0);
index 5d6bae33ae34ca64ce079a771c2624ae7274a14b..fe79eb90e1e5cf517a1f1d222b2a11cae0effcb0 100644 (file)
@@ -109,6 +109,7 @@ struct rz_ssi_priv {
        int irq_int;
        int irq_tx;
        int irq_rx;
+       int irq_rt;
 
        spinlock_t lock;
 
@@ -565,6 +566,17 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
                rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
        }
 
+       if (irq == ssi->irq_rt) {
+               struct snd_pcm_substream *substream = strm->substream;
+
+               if (rz_ssi_stream_is_play(ssi, substream)) {
+                       strm->transfer(ssi, &ssi->playback);
+               } else {
+                       strm->transfer(ssi, &ssi->capture);
+                       rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
+               }
+       }
+
        return IRQ_HANDLED;
 }
 
@@ -993,26 +1005,39 @@ static int rz_ssi_probe(struct platform_device *pdev)
        if (!rz_ssi_is_dma_enabled(ssi)) {
                /* Tx and Rx interrupts (pio only) */
                ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx");
-               if (ssi->irq_tx < 0)
-                       return ssi->irq_tx;
-
-               ret = devm_request_irq(&pdev->dev, ssi->irq_tx,
-                                      &rz_ssi_interrupt, 0,
-                                      dev_name(&pdev->dev), ssi);
-               if (ret < 0)
-                       return dev_err_probe(&pdev->dev, ret,
-                                            "irq request error (dma_tx)\n");
-
                ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx");
-               if (ssi->irq_rx < 0)
-                       return ssi->irq_rx;
-
-               ret = devm_request_irq(&pdev->dev, ssi->irq_rx,
-                                      &rz_ssi_interrupt, 0,
-                                      dev_name(&pdev->dev), ssi);
-               if (ret < 0)
-                       return dev_err_probe(&pdev->dev, ret,
-                                            "irq request error (dma_rx)\n");
+               if (ssi->irq_tx == -ENXIO && ssi->irq_rx == -ENXIO) {
+                       ssi->irq_rt = platform_get_irq_byname(pdev, "dma_rt");
+                       if (ssi->irq_rt < 0)
+                               return ssi->irq_rt;
+
+                       ret = devm_request_irq(&pdev->dev, ssi->irq_rt,
+                                              &rz_ssi_interrupt, 0,
+                                              dev_name(&pdev->dev), ssi);
+                       if (ret < 0)
+                               return dev_err_probe(&pdev->dev, ret,
+                                               "irq request error (dma_tx)\n");
+               } else {
+                       if (ssi->irq_tx < 0)
+                               return ssi->irq_tx;
+
+                       if (ssi->irq_rx < 0)
+                               return ssi->irq_rx;
+
+                       ret = devm_request_irq(&pdev->dev, ssi->irq_tx,
+                                              &rz_ssi_interrupt, 0,
+                                              dev_name(&pdev->dev), ssi);
+                       if (ret < 0)
+                               return dev_err_probe(&pdev->dev, ret,
+                                               "irq request error (dma_tx)\n");
+
+                       ret = devm_request_irq(&pdev->dev, ssi->irq_rx,
+                                              &rz_ssi_interrupt, 0,
+                                              dev_name(&pdev->dev), ssi);
+                       if (ret < 0)
+                               return dev_err_probe(&pdev->dev, ret,
+                                               "irq request error (dma_rx)\n");
+               }
        }
 
        ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
@@ -1050,7 +1075,7 @@ err_reset:
        return ret;
 }
 
-static int rz_ssi_remove(struct platform_device *pdev)
+static void rz_ssi_remove(struct platform_device *pdev)
 {
        struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev);
 
@@ -1059,8 +1084,6 @@ static int rz_ssi_remove(struct platform_device *pdev)
        pm_runtime_put(ssi->dev);
        pm_runtime_disable(ssi->dev);
        reset_control_assert(ssi->rstc);
-
-       return 0;
 }
 
 static const struct of_device_id rz_ssi_of_match[] = {
@@ -1075,7 +1098,7 @@ static struct platform_driver rz_ssi_driver = {
                .of_match_table = rz_ssi_of_match,
        },
        .probe          = rz_ssi_probe,
-       .remove         = rz_ssi_remove,
+       .remove_new     = rz_ssi_remove,
 };
 
 module_platform_driver(rz_ssi_driver);
index f2a386fcd92e4da317556ae333de646bb6dcd1f5..84e1b14e68e424bd1b2bdd4b262d2eb6b0bf1b0c 100644 (file)
@@ -778,10 +778,9 @@ static int siu_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int siu_remove(struct platform_device *pdev)
+static void siu_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-       return 0;
 }
 
 static struct platform_driver siu_driver = {
@@ -789,7 +788,7 @@ static struct platform_driver siu_driver = {
                .name   = "siu-pcm-audio",
        },
        .probe          = siu_probe,
-       .remove         = siu_remove,
+       .remove_new     = siu_remove,
 };
 
 module_platform_driver(siu_driver);
index 3cd6952212e1f69e7d3aa42d2db5f03ff03241dd..ff25718ff2e887bd1ff370b25dc59688d26c2e41 100644 (file)
@@ -550,7 +550,7 @@ int snd_soc_component_compr_get_caps(struct snd_compr_stream *cstream,
        struct snd_soc_component *component;
        int i, ret = 0;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        for_each_rtd_components(rtd, i, component) {
                if (component->driver->compress_ops &&
@@ -561,7 +561,7 @@ int snd_soc_component_compr_get_caps(struct snd_compr_stream *cstream,
                }
        }
 
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 
        return soc_component_ret(component, ret);
 }
@@ -574,7 +574,7 @@ int snd_soc_component_compr_get_codec_caps(struct snd_compr_stream *cstream,
        struct snd_soc_component *component;
        int i, ret = 0;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        for_each_rtd_components(rtd, i, component) {
                if (component->driver->compress_ops &&
@@ -585,7 +585,7 @@ int snd_soc_component_compr_get_codec_caps(struct snd_compr_stream *cstream,
                }
        }
 
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 
        return soc_component_ret(component, ret);
 }
@@ -638,7 +638,7 @@ int snd_soc_component_compr_copy(struct snd_compr_stream *cstream,
        struct snd_soc_component *component;
        int i, ret = 0;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        for_each_rtd_components(rtd, i, component) {
                if (component->driver->compress_ops &&
@@ -649,7 +649,7 @@ int snd_soc_component_compr_copy(struct snd_compr_stream *cstream,
                }
        }
 
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 
        return soc_component_ret(component, ret);
 }
index e7aa6f360cabefb06521b35b3d8a8d13b549c606..d8715db5e415e7d7cdceb67be72d1682f484f98f 100644 (file)
@@ -62,7 +62,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        if (!rollback)
                snd_soc_runtime_deactivate(rtd, stream);
@@ -84,7 +84,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
        if (!rollback)
                snd_soc_dapm_stream_stop(rtd, stream);
 
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 
        snd_soc_pcm_component_pm_runtime_put(rtd, cstream, rollback);
 
@@ -107,7 +107,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        if (ret < 0)
                goto err_no_lock;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        ret = snd_soc_dai_compr_startup(cpu_dai, cstream);
        if (ret < 0)
@@ -123,7 +123,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
 
        snd_soc_runtime_activate(rtd, stream);
 err:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 err_no_lock:
        if (ret < 0)
                soc_compr_clean(cstream, 1);
@@ -134,26 +134,22 @@ err_no_lock:
 static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 {
        struct snd_soc_pcm_runtime *fe = cstream->private_data;
-       struct snd_pcm_substream *fe_substream =
-                fe->pcm->streams[cstream->direction].substream;
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0);
        struct snd_soc_dpcm *dpcm;
        struct snd_soc_dapm_widget_list *list;
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
        int ret;
 
-       mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-       fe->dpcm[stream].runtime = fe_substream->runtime;
+       snd_soc_card_mutex_lock(fe->card);
 
        ret = dpcm_path_get(fe, stream, &list);
        if (ret < 0)
                goto be_err;
 
-       mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(fe);
 
        /* calculate valid and active FE <-> BE dpcms */
        dpcm_process_paths(fe, stream, &list, 1);
-       fe->dpcm[stream].runtime = fe_substream->runtime;
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
@@ -164,7 +160,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
                        dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
 
                dpcm_be_disconnect(fe, stream);
-               fe->dpcm[stream].runtime = NULL;
                goto out;
        }
 
@@ -187,9 +182,9 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
 
        snd_soc_runtime_activate(fe, stream);
-       mutex_unlock(&fe->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(fe);
 
-       mutex_unlock(&fe->card->mutex);
+       snd_soc_card_mutex_unlock(fe->card);
 
        return 0;
 
@@ -201,7 +196,7 @@ out:
        dpcm_path_put(&list);
 be_err:
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
-       mutex_unlock(&fe->card->mutex);
+       snd_soc_card_mutex_unlock(fe->card);
        return ret;
 }
 
@@ -212,9 +207,9 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
        struct snd_soc_dpcm *dpcm;
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
 
-       mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+       snd_soc_card_mutex_lock(fe->card);
 
-       mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(fe);
        snd_soc_runtime_deactivate(fe, stream);
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
@@ -234,9 +229,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 
        dpcm_be_disconnect(fe, stream);
 
-       mutex_unlock(&fe->card->pcm_mutex);
-
-       fe->dpcm[stream].runtime = NULL;
+       snd_soc_dpcm_mutex_unlock(fe);
 
        snd_soc_link_compr_shutdown(cstream, 0);
 
@@ -244,7 +237,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 
        snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0);
 
-       mutex_unlock(&fe->card->mutex);
+       snd_soc_card_mutex_unlock(fe->card);
        return 0;
 }
 
@@ -256,7 +249,7 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
        int ret;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        ret = snd_soc_component_compr_trigger(cstream, cmd);
        if (ret < 0)
@@ -276,7 +269,7 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        }
 
 out:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
        return ret;
 }
 
@@ -291,7 +284,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
            cmd == SND_COMPR_TRIGGER_DRAIN)
                return snd_soc_component_compr_trigger(cstream, cmd);
 
-       mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+       snd_soc_card_mutex_lock(fe->card);
 
        ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
        if (ret < 0)
@@ -322,7 +315,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
 
 out:
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
-       mutex_unlock(&fe->card->mutex);
+       snd_soc_card_mutex_unlock(fe->card);
        return ret;
 }
 
@@ -334,7 +327,7 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
        int ret;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        /*
         * First we call set_params for the CPU DAI, then the component
@@ -359,14 +352,14 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
 
        /* cancel any delayed stream shutdown that is pending */
        rtd->pop_wait = 0;
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 
        cancel_delayed_work_sync(&rtd->delayed_work);
 
        return 0;
 
 err:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
        return ret;
 }
 
@@ -380,7 +373,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
        int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
        int ret;
 
-       mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+       snd_soc_card_mutex_lock(fe->card);
 
        /*
         * Create an empty hw_params for the BE as the machine driver must
@@ -411,14 +404,14 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
        ret = snd_soc_link_compr_set_params(cstream);
        if (ret < 0)
                goto out;
-       mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(fe);
        dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-       mutex_unlock(&fe->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(fe);
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
 
 out:
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
-       mutex_unlock(&fe->card->mutex);
+       snd_soc_card_mutex_unlock(fe->card);
        return ret;
 }
 
@@ -429,7 +422,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        int ret = 0;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        ret = snd_soc_dai_compr_get_params(cpu_dai, cstream, params);
        if (ret < 0)
@@ -437,7 +430,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
 
        ret = snd_soc_component_compr_get_params(cstream, params);
 err:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
        return ret;
 }
 
@@ -447,7 +440,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        int ret;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        ret = snd_soc_dai_compr_ack(cpu_dai, cstream, bytes);
        if (ret < 0)
@@ -455,7 +448,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
 
        ret = snd_soc_component_compr_ack(cstream, bytes);
 err:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
        return ret;
 }
 
@@ -466,7 +459,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
        int ret;
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        ret = snd_soc_dai_compr_pointer(cpu_dai, cstream, tstamp);
        if (ret < 0)
@@ -474,7 +467,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
 
        ret = snd_soc_component_compr_pointer(cstream, tstamp);
 out:
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
        return ret;
 }
 
@@ -622,6 +615,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
                        return ret;
                }
 
+               /* inherit atomicity from DAI link */
+               be_pcm->nonatomic = rtd->dai_link->nonatomic;
+
                rtd->pcm = be_pcm;
                rtd->fe_compr = 1;
                if (rtd->dai_link->dpcm_playback)
index 71b022f7edfd5b6bd3c85030ae813865e9ff906d..b48efc3a08d2614eee452518eca1f72033d4e3a3 100644 (file)
@@ -348,7 +348,7 @@ void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
        int playback = SNDRV_PCM_STREAM_PLAYBACK;
 
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(rtd);
 
        dev_dbg(rtd->dev,
                "ASoC: pop wq checking: %s status: %s waiting: %s\n",
@@ -364,7 +364,7 @@ void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
                                          SND_SOC_DAPM_STREAM_STOP);
        }
 
-       mutex_unlock(&rtd->card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(rtd);
 }
 EXPORT_SYMBOL_GPL(snd_soc_close_delayed_work);
 
@@ -959,8 +959,8 @@ EXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime);
  * topology component. And machine drivers can still define static
  * DAI links in dai_link array.
  */
-int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
-                           struct snd_soc_dai_link *dai_link)
+static int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
+                                  struct snd_soc_dai_link *dai_link)
 {
        struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_dai_link_component *codec, *platform, *cpu;
@@ -1027,13 +1027,26 @@ _err_defer:
        snd_soc_remove_pcm_runtime(card, rtd);
        return -EPROBE_DEFER;
 }
-EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime);
+
+int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
+                            struct snd_soc_dai_link *dai_link,
+                            int num_dai_link)
+{
+       for (int i = 0; i < num_dai_link; i++) {
+               int ret = snd_soc_add_pcm_runtime(card, dai_link + i);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtimes);
 
 static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai_link *dai_link = rtd->dai_link;
        struct snd_soc_dai *dai, *not_used;
-       struct device *dev = rtd->dev;
        u64 pos, possible_fmt;
        unsigned int mask = 0, dai_fmt = 0;
        int i, j, priority, pri, until;
@@ -1075,8 +1088,6 @@ static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
         */
        until = snd_soc_dai_get_fmt_max_priority(rtd);
        for (priority = 1; priority <= until; priority++) {
-
-               dev_dbg(dev, "priority = %d\n", priority);
                for_each_rtd_dais(rtd, j, not_used) {
 
                        possible_fmt = ULLONG_MAX;
@@ -1085,7 +1096,6 @@ static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
 
                                pri = (j >= i) ? priority : priority - 1;
                                fmt = snd_soc_dai_get_fmt(dai, pri);
-                               dev_dbg(dev, "%s: (pri, fmt) = (%d, %016llX)\n", dai->name, pri, fmt);
                                possible_fmt &= fmt;
                        }
                        if (possible_fmt)
@@ -1095,8 +1105,6 @@ static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
        /* Not Found */
        return;
 found:
-       dev_dbg(dev, "found auto selected format: %016llX\n", possible_fmt);
-
        /*
         * convert POSSIBLE_DAIFMT to DAIFMT
         *
@@ -1457,11 +1465,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card)
 
        for_each_comp_order(order) {
                for_each_card_rtds(card, rtd) {
-
-                       dev_dbg(card->dev,
-                               "ASoC: probe %s dai link %d late %d\n",
-                               card->name, rtd->num, order);
-
                        /* probe all rtd connected DAIs in good order */
                        ret = snd_soc_pcm_dai_probe(rtd, order);
                        if (ret)
@@ -1932,11 +1935,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_component *component;
-       struct snd_soc_dai_link *dai_link;
-       int ret, i;
+       int ret;
 
        mutex_lock(&client_mutex);
-       mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
+       snd_soc_card_mutex_lock_root(card);
 
        snd_soc_dapm_init(&card->dapm, card, NULL);
 
@@ -1950,11 +1952,9 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
 
        /* add predefined DAI links to the list */
        card->num_rtd = 0;
-       for_each_card_prelinks(card, i, dai_link) {
-               ret = snd_soc_add_pcm_runtime(card, dai_link);
-               if (ret < 0)
-                       goto probe_end;
-       }
+       ret = snd_soc_add_pcm_runtimes(card, card->dai_link, card->num_links);
+       if (ret < 0)
+               goto probe_end;
 
        /* card bind complete so register a sound card */
        ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
@@ -2093,7 +2093,7 @@ probe_end:
        if (ret < 0)
                soc_cleanup_card_resources(card);
 
-       mutex_unlock(&card->mutex);
+       snd_soc_card_mutex_unlock(card);
        mutex_unlock(&client_mutex);
 
        return ret;
@@ -2421,8 +2421,6 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
        struct device *dev = component->dev;
        struct snd_soc_dai *dai;
 
-       dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));
-
        lockdep_assert_held(&client_mutex);
 
        dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
index 0119afbd01fc0449c88eb90fbf0aecf44c86761d..02dd64dea17925b45c69654f99ef7399674604b8 100644 (file)
@@ -542,6 +542,9 @@ int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order)
                if (dai->driver->probe_order != order)
                        continue;
 
+               if (dai->probed)
+                       continue;
+
                if (dai->driver->probe) {
                        int ret = dai->driver->probe(dai);
 
index 5d9a671e50f1947f43b37a3c51c07befbce25bc1..f2f04ce693a1ae37a3ee922b65d6b0e7b47136db 100644 (file)
@@ -99,58 +99,58 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_adc] = 11,
        [snd_soc_dapm_out_drv] = 12,
        [snd_soc_dapm_hp] = 12,
-       [snd_soc_dapm_spk] = 12,
        [snd_soc_dapm_line] = 12,
        [snd_soc_dapm_sink] = 12,
-       [snd_soc_dapm_kcontrol] = 13,
-       [snd_soc_dapm_post] = 14,
+       [snd_soc_dapm_spk] = 13,
+       [snd_soc_dapm_kcontrol] = 14,
+       [snd_soc_dapm_post] = 15,
 };
 
 static int dapm_down_seq[] = {
        [snd_soc_dapm_pre] = 1,
        [snd_soc_dapm_kcontrol] = 2,
        [snd_soc_dapm_adc] = 3,
-       [snd_soc_dapm_hp] = 4,
        [snd_soc_dapm_spk] = 4,
-       [snd_soc_dapm_line] = 4,
-       [snd_soc_dapm_out_drv] = 4,
-       [snd_soc_dapm_sink] = 4,
-       [snd_soc_dapm_pga] = 5,
-       [snd_soc_dapm_buffer] = 5,
-       [snd_soc_dapm_scheduler] = 5,
-       [snd_soc_dapm_effect] = 5,
-       [snd_soc_dapm_src] = 5,
-       [snd_soc_dapm_asrc] = 5,
-       [snd_soc_dapm_encoder] = 5,
-       [snd_soc_dapm_decoder] = 5,
-       [snd_soc_dapm_switch] = 6,
-       [snd_soc_dapm_mixer_named_ctl] = 6,
-       [snd_soc_dapm_mixer] = 6,
-       [snd_soc_dapm_dac] = 7,
-       [snd_soc_dapm_mic] = 8,
-       [snd_soc_dapm_siggen] = 8,
-       [snd_soc_dapm_input] = 8,
-       [snd_soc_dapm_output] = 8,
-       [snd_soc_dapm_micbias] = 9,
-       [snd_soc_dapm_vmid] = 9,
-       [snd_soc_dapm_mux] = 10,
-       [snd_soc_dapm_demux] = 10,
-       [snd_soc_dapm_aif_in] = 11,
-       [snd_soc_dapm_aif_out] = 11,
-       [snd_soc_dapm_dai_in] = 11,
-       [snd_soc_dapm_dai_out] = 11,
-       [snd_soc_dapm_dai_link] = 12,
-       [snd_soc_dapm_supply] = 13,
-       [snd_soc_dapm_clock_supply] = 14,
-       [snd_soc_dapm_pinctrl] = 14,
-       [snd_soc_dapm_regulator_supply] = 14,
-       [snd_soc_dapm_post] = 15,
+       [snd_soc_dapm_hp] = 5,
+       [snd_soc_dapm_line] = 5,
+       [snd_soc_dapm_out_drv] = 5,
+       [snd_soc_dapm_sink] = 6,
+       [snd_soc_dapm_pga] = 6,
+       [snd_soc_dapm_buffer] = 6,
+       [snd_soc_dapm_scheduler] = 6,
+       [snd_soc_dapm_effect] = 6,
+       [snd_soc_dapm_src] = 6,
+       [snd_soc_dapm_asrc] = 6,
+       [snd_soc_dapm_encoder] = 6,
+       [snd_soc_dapm_decoder] = 6,
+       [snd_soc_dapm_switch] = 7,
+       [snd_soc_dapm_mixer_named_ctl] = 7,
+       [snd_soc_dapm_mixer] = 7,
+       [snd_soc_dapm_dac] = 8,
+       [snd_soc_dapm_mic] = 9,
+       [snd_soc_dapm_siggen] = 9,
+       [snd_soc_dapm_input] = 9,
+       [snd_soc_dapm_output] = 9,
+       [snd_soc_dapm_micbias] = 10,
+       [snd_soc_dapm_vmid] = 10,
+       [snd_soc_dapm_mux] = 11,
+       [snd_soc_dapm_demux] = 11,
+       [snd_soc_dapm_aif_in] = 12,
+       [snd_soc_dapm_aif_out] = 12,
+       [snd_soc_dapm_dai_in] = 12,
+       [snd_soc_dapm_dai_out] = 12,
+       [snd_soc_dapm_dai_link] = 13,
+       [snd_soc_dapm_supply] = 14,
+       [snd_soc_dapm_clock_supply] = 15,
+       [snd_soc_dapm_pinctrl] = 15,
+       [snd_soc_dapm_regulator_supply] = 15,
+       [snd_soc_dapm_post] = 16,
 };
 
 static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
 {
        if (snd_soc_card_is_instantiated(dapm->card))
-               lockdep_assert_held(&dapm->card->dapm_mutex);
+               snd_soc_dapm_mutex_assert_held(dapm);
 }
 
 static void pop_wait(u32 pop_time)
@@ -302,7 +302,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *w;
 
-       mutex_lock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_lock_root(card);
 
        for_each_card_widgets(card, w) {
                if (w->is_ep) {
@@ -314,7 +314,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
                }
        }
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 }
 EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
 
@@ -604,7 +604,7 @@ static void dapm_reset(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *w;
 
-       lockdep_assert_held(&card->dapm_mutex);
+       snd_soc_dapm_mutex_assert_held(card);
 
        memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
 
@@ -1075,7 +1075,7 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
        struct snd_soc_pcm_runtime *rtd = w->priv;
 
        /* create control for links with > 1 config */
-       if (rtd->dai_link->num_params <= 1)
+       if (rtd->dai_link->num_c2c_params <= 1)
                return 0;
 
        /* add kcontrol */
@@ -1302,7 +1302,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        int paths;
        int ret;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
@@ -1322,7 +1322,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
                paths = ret;
 
        trace_snd_soc_dapm_connected(paths, stream);
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 
        return paths;
 }
@@ -1952,7 +1952,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
        enum snd_soc_bias_level bias;
        int ret;
 
-       lockdep_assert_held(&card->dapm_mutex);
+       snd_soc_dapm_mutex_assert_held(card);
 
        trace_snd_soc_dapm_start(card);
 
@@ -2090,7 +2090,6 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                           size_t count, loff_t *ppos)
 {
        struct snd_soc_dapm_widget *w = file->private_data;
-       struct snd_soc_card *card = w->dapm->card;
        enum snd_soc_dapm_direction dir, rdir;
        char *buf;
        int in, out;
@@ -2101,7 +2100,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       mutex_lock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_lock_root(w->dapm);
 
        /* Supply widgets are not handled by is_connected_{input,output}_ep() */
        if (w->is_supply) {
@@ -2145,7 +2144,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                }
        }
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(w->dapm);
 
        ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
 
@@ -2266,7 +2265,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card,
        int found = 0;
        bool connect;
 
-       lockdep_assert_held(&card->dapm_mutex);
+       snd_soc_dapm_mutex_assert_held(card);
 
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
@@ -2293,11 +2292,11 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
        struct snd_soc_card *card = dapm->card;
        int ret;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
        card->update = update;
        ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
        card->update = NULL;
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
        if (ret > 0)
                snd_soc_dpcm_runtime_update(card);
        return ret;
@@ -2312,7 +2311,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
        struct snd_soc_dapm_path *path;
        int found = 0;
 
-       lockdep_assert_held(&card->dapm_mutex);
+       snd_soc_dapm_mutex_assert_held(card);
 
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
@@ -2358,11 +2357,11 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
        struct snd_soc_card *card = dapm->card;
        int ret;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
        card->update = update;
        ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);
        card->update = NULL;
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
        if (ret > 0)
                snd_soc_dpcm_runtime_update(card);
        return ret;
@@ -2441,7 +2440,7 @@ static ssize_t dapm_widget_show(struct device *dev,
        struct snd_soc_dai *codec_dai;
        int i, count = 0;
 
-       mutex_lock(&rtd->card->dapm_mutex);
+       snd_soc_dapm_mutex_lock_root(rtd->card);
 
        for_each_rtd_codec_dais(rtd, i, codec_dai) {
                struct snd_soc_component *cmpnt = codec_dai->component;
@@ -2449,7 +2448,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                count = dapm_widget_show_component(cmpnt, buf, count);
        }
 
-       mutex_unlock(&rtd->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(rtd->card);
 
        return count;
 }
@@ -2632,9 +2631,9 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 {
        int ret;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        ret = snd_soc_dapm_sync_unlocked(dapm);
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
@@ -2703,9 +2702,9 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        int ret;
 
-       mutex_lock_nested(&rtd->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(rtd->card);
        ret = dapm_update_dai_unlocked(substream, params, dai);
-       mutex_unlock(&rtd->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(rtd->card);
 
        return ret;
 }
@@ -3090,14 +3089,14 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 {
        int i, ret = 0;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        for (i = 0; i < num; i++) {
                int r = snd_soc_dapm_add_route(dapm, route);
                if (r < 0)
                        ret = r;
                route++;
        }
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -3116,12 +3115,12 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
 {
        int i;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        for (i = 0; i < num; i++) {
                snd_soc_dapm_del_route(dapm, route);
                route++;
        }
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return 0;
 }
@@ -3194,14 +3193,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
        int i;
        int ret = 0;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+       snd_soc_dapm_mutex_lock_root(dapm);
        for (i = 0; i < num; i++) {
                int err = snd_soc_dapm_weak_route(dapm, route);
                if (err)
                        ret = err;
                route++;
        }
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -3220,7 +3219,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
        struct snd_soc_dapm_widget *w;
        unsigned int val;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+       snd_soc_dapm_mutex_lock_root(card);
 
        for_each_card_widgets(card, w)
        {
@@ -3232,7 +3231,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
                                                sizeof(struct snd_kcontrol *),
                                                GFP_KERNEL);
                        if (!w->kcontrols) {
-                               mutex_unlock(&card->dapm_mutex);
+                               snd_soc_dapm_mutex_unlock(card);
                                return -ENOMEM;
                        }
                }
@@ -3275,7 +3274,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
        }
 
        dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -3293,7 +3292,6 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
-       struct snd_soc_card *card = dapm->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int reg = mc->reg;
@@ -3304,7 +3302,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        unsigned int invert = mc->invert;
        unsigned int reg_val, val, rval = 0;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
                reg_val = soc_dapm_read(dapm, reg);
                val = (reg_val >> shift) & mask;
@@ -3321,7 +3319,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
                if (snd_soc_volsw_is_stereo(mc))
                        rval = (reg_val >> width) & mask;
        }
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        if (invert)
                ucontrol->value.integer.value[0] = max - val;
@@ -3379,7 +3377,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                        rval = max - rval;
        }
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
 
        /* This assumes field width < (bits in unsigned int / 2) */
        if (width > sizeof(unsigned int) * 8 / 2)
@@ -3421,7 +3419,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                card->update = NULL;
        }
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 
        if (ret > 0)
                snd_soc_dpcm_runtime_update(card);
@@ -3443,17 +3441,16 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
-       struct snd_soc_card *card = dapm->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg_val, val;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {
                reg_val = soc_dapm_read(dapm, e->reg);
        } else {
                reg_val = dapm_kcontrol_get_value(kcontrol);
        }
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        val = (reg_val >> e->shift_l) & e->mask;
        ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
@@ -3500,7 +3497,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
 
        change = dapm_kcontrol_set_value(kcontrol, val);
 
@@ -3521,7 +3518,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                card->update = NULL;
        }
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 
        if (ret > 0)
                snd_soc_dpcm_runtime_update(card);
@@ -3562,12 +3559,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
        const char *pin = (const char *)kcontrol->private_value;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
 
        ucontrol->value.integer.value[0] =
                snd_soc_dapm_get_pin_status(&card->dapm, pin);
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 
        return 0;
 }
@@ -3586,10 +3583,10 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
        const char *pin = (const char *)kcontrol->private_value;
        int ret;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
        ret = __snd_soc_dapm_set_pin(&card->dapm, pin,
                                     !!ucontrol->value.integer.value[0]);
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 
        snd_soc_dapm_sync(&card->dapm);
        return ret;
@@ -3762,9 +3759,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 {
        struct snd_soc_dapm_widget *w;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
        w = snd_soc_dapm_new_control_unlocked(dapm, widget);
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return w;
 }
@@ -3787,7 +3784,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        int i;
        int ret = 0;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+       snd_soc_dapm_mutex_lock_root(dapm);
        for (i = 0; i < num; i++) {
                struct snd_soc_dapm_widget *w = snd_soc_dapm_new_control_unlocked(dapm, widget);
                if (IS_ERR(w)) {
@@ -3796,7 +3793,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
                }
                widget++;
        }
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
@@ -3864,7 +3861,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
         * either party on the link to alter the configuration if
         * necessary
         */
-       config = rtd->dai_link->params + rtd->params_select;
+       config = rtd->dai_link->c2c_params + rtd->c2c_params_select;
        if (!config) {
                dev_err(w->dapm->dev, "ASoC: link config missing\n");
                ret = -EINVAL;
@@ -4010,7 +4007,7 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
        struct snd_soc_pcm_runtime *rtd = w->priv;
 
-       ucontrol->value.enumerated.item[0] = rtd->params_select;
+       ucontrol->value.enumerated.item[0] = rtd->c2c_params_select;
 
        return 0;
 }
@@ -4025,13 +4022,13 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
        if (w->power)
                return -EBUSY;
 
-       if (ucontrol->value.enumerated.item[0] == rtd->params_select)
+       if (ucontrol->value.enumerated.item[0] == rtd->c2c_params_select)
                return 0;
 
-       if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_params)
+       if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_c2c_params)
                return -EINVAL;
 
-       rtd->params_select = ucontrol->value.enumerated.item[0];
+       rtd->c2c_params_select = ucontrol->value.enumerated.item[0];
 
        return 1;
 }
@@ -4039,7 +4036,7 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
 static void
 snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
                        unsigned long *private_value,
-                       int num_params,
+                       int num_c2c_params,
                        const char **w_param_text)
 {
        int count;
@@ -4049,7 +4046,7 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
        if (!w_param_text)
                return;
 
-       for (count = 0 ; count < num_params; count++)
+       for (count = 0 ; count < num_c2c_params; count++)
                devm_kfree(card->dev, (void *)w_param_text[count]);
        devm_kfree(card->dev, w_param_text);
 }
@@ -4057,8 +4054,8 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
 static struct snd_kcontrol_new *
 snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
                        char *link_name,
-                       const struct snd_soc_pcm_stream *params,
-                       int num_params, const char **w_param_text,
+                       const struct snd_soc_pcm_stream *c2c_params,
+                       int num_c2c_params, const char **w_param_text,
                        unsigned long *private_value)
 {
        struct soc_enum w_param_enum[] = {
@@ -4070,10 +4067,10 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
                             snd_soc_dapm_dai_link_put),
        };
        struct snd_kcontrol_new *kcontrol_news;
-       const struct snd_soc_pcm_stream *config = params;
+       const struct snd_soc_pcm_stream *config = c2c_params;
        int count;
 
-       for (count = 0 ; count < num_params; count++) {
+       for (count = 0 ; count < num_c2c_params; count++) {
                if (!config->stream_name) {
                        dev_warn(card->dapm.dev,
                                "ASoC: anonymous config %d for dai link %s\n",
@@ -4093,7 +4090,7 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
                config++;
        }
 
-       w_param_enum[0].items = num_params;
+       w_param_enum[0].items = num_c2c_params;
        w_param_enum[0].texts = w_param_text;
 
        *private_value =
@@ -4118,7 +4115,7 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
        return kcontrol_news;
 
 outfree_w_param:
-       snd_soc_dapm_free_kcontrol(card, private_value, num_params, w_param_text);
+       snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);
        return NULL;
 }
 
@@ -4146,17 +4143,17 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
        w_param_text    = NULL;
        kcontrol_news   = NULL;
        num_kcontrols   = 0;
-       if (rtd->dai_link->num_params > 1) {
+       if (rtd->dai_link->num_c2c_params > 1) {
                w_param_text = devm_kcalloc(card->dev,
-                                           rtd->dai_link->num_params,
+                                           rtd->dai_link->num_c2c_params,
                                            sizeof(char *), GFP_KERNEL);
                if (!w_param_text)
                        goto param_fail;
 
                num_kcontrols = 1;
                kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name,
-                                                           rtd->dai_link->params,
-                                                           rtd->dai_link->num_params,
+                                                           rtd->dai_link->c2c_params,
+                                                           rtd->dai_link->num_c2c_params,
                                                            w_param_text, &private_value);
                if (!kcontrol_news)
                        goto param_fail;
@@ -4187,7 +4184,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
 outfree_kcontrol_news:
        devm_kfree(card->dev, (void *)template.kcontrol_news);
        snd_soc_dapm_free_kcontrol(card, &private_value,
-                                  rtd->dai_link->num_params, w_param_text);
+                                  rtd->dai_link->num_c2c_params, w_param_text);
 param_fail:
        devm_kfree(card->dev, link_name);
 name_fail:
@@ -4325,60 +4322,81 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
        snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
 }
 
+static int get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream)
+{
+       /*
+        * [Normal]
+        *
+        * Playback
+        *      CPU  : SNDRV_PCM_STREAM_PLAYBACK
+        *      Codec: SNDRV_PCM_STREAM_PLAYBACK
+        *
+        * Playback
+        *      CPU  : SNDRV_PCM_STREAM_CAPTURE
+        *      Codec: SNDRV_PCM_STREAM_CAPTURE
+        */
+       if (!dai_link->c2c_params)
+               return stream;
+
+       /*
+        * [Codec2Codec]
+        *
+        * Playback
+        *      CPU  : SNDRV_PCM_STREAM_CAPTURE
+        *      Codec: SNDRV_PCM_STREAM_PLAYBACK
+        *
+        * Capture
+        *      CPU  : SNDRV_PCM_STREAM_PLAYBACK
+        *      Codec: SNDRV_PCM_STREAM_CAPTURE
+        */
+       if (stream == SNDRV_PCM_STREAM_CAPTURE)
+               return SNDRV_PCM_STREAM_PLAYBACK;
+
+       return SNDRV_PCM_STREAM_CAPTURE;
+}
+
 static void dapm_connect_dai_pair(struct snd_soc_card *card,
                                  struct snd_soc_pcm_runtime *rtd,
                                  struct snd_soc_dai *codec_dai,
                                  struct snd_soc_dai *cpu_dai)
 {
        struct snd_soc_dai_link *dai_link = rtd->dai_link;
-       struct snd_soc_dapm_widget *dai, *codec, *playback_cpu, *capture_cpu;
-       struct snd_pcm_substream *substream;
-       struct snd_pcm_str *streams = rtd->pcm->streams;
+       struct snd_soc_dapm_widget *codec, *cpu;
+       struct snd_soc_dai *src_dai[]           = { cpu_dai,    codec_dai };
+       struct snd_soc_dai *sink_dai[]          = { codec_dai,  cpu_dai };
+       struct snd_soc_dapm_widget **src[]      = { &cpu,       &codec };
+       struct snd_soc_dapm_widget **sink[]     = { &codec,     &cpu };
+       char *widget_name[]                     = { "playback", "capture" };
        int stream;
 
-       if (dai_link->params) {
-               playback_cpu    = snd_soc_dai_get_widget_capture(cpu_dai);
-               capture_cpu     = snd_soc_dai_get_widget_playback(cpu_dai);
-       } else {
-               playback_cpu    = snd_soc_dai_get_widget_playback(cpu_dai);
-               capture_cpu     = snd_soc_dai_get_widget_capture(cpu_dai);
-       }
+       for_each_pcm_streams(stream) {
+               int stream_cpu, stream_codec;
 
-       /* connect BE DAI playback if widgets are valid */
-       stream = SNDRV_PCM_STREAM_PLAYBACK;
-       codec = snd_soc_dai_get_widget(codec_dai, stream);
+               stream_cpu      = get_stream_cpu(dai_link, stream);
+               stream_codec    = stream;
 
-       if (playback_cpu && codec) {
-               if (dai_link->params && !rtd->c2c_widget[stream]) {
-                       substream = streams[stream].substream;
-                       dai = snd_soc_dapm_new_dai(card, substream, "playback");
-                       if (IS_ERR(dai))
-                               goto capture;
-                       rtd->c2c_widget[stream] = dai;
-               }
+               /* connect BE DAI playback if widgets are valid */
+               cpu     = snd_soc_dai_get_widget(cpu_dai,       stream_cpu);
+               codec   = snd_soc_dai_get_widget(codec_dai,     stream_codec);
 
-               dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu,
-                                       rtd->c2c_widget[stream],
-                                       codec_dai, codec);
-       }
+               if (!cpu || !codec)
+                       continue;
 
-capture:
-       /* connect BE DAI capture if widgets are valid */
-       stream = SNDRV_PCM_STREAM_CAPTURE;
-       codec = snd_soc_dai_get_widget(codec_dai, stream);
+               /* special handling for [Codec2Codec] */
+               if (dai_link->c2c_params && !rtd->c2c_widget[stream]) {
+                       struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream;
+                       struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream,
+                                                                              widget_name[stream]);
 
-       if (codec && capture_cpu) {
-               if (dai_link->params && !rtd->c2c_widget[stream]) {
-                       substream = streams[stream].substream;
-                       dai = snd_soc_dapm_new_dai(card, substream, "capture");
                        if (IS_ERR(dai))
-                               return;
+                               continue;
+
                        rtd->c2c_widget[stream] = dai;
                }
 
-               dapm_connect_dai_routes(&card->dapm, codec_dai, codec,
+               dapm_connect_dai_routes(&card->dapm, src_dai[stream], *src[stream],
                                        rtd->c2c_widget[stream],
-                                       cpu_dai, capture_cpu);
+                                       sink_dai[stream], *sink[stream]);
        }
 }
 
@@ -4478,9 +4496,9 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
 {
        struct snd_soc_card *card = rtd->card;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(card);
        soc_dapm_stream_event(rtd, stream, event);
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 }
 
 void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream)
@@ -4541,11 +4559,11 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
        int ret;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_dapm_set_pin(dapm, pin, 1);
 
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -4609,11 +4627,11 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
 {
        int ret;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
 
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -4653,11 +4671,11 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
 {
        int ret;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_dapm_set_pin(dapm, pin, 0);
 
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -4704,11 +4722,11 @@ int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
        int ret;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_dapm_set_pin(dapm, pin, 0);
 
-       mutex_unlock(&dapm->card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -4805,7 +4823,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
        LIST_HEAD(down_list);
        int powerdown = 0;
 
-       mutex_lock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_lock_root(card);
 
        for_each_card_widgets(dapm->card, w) {
                if (w->dapm != dapm)
@@ -4830,7 +4848,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
                                                    SND_SOC_BIAS_STANDBY);
        }
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(card);
 }
 
 /*
index fcece5ca38c6969b01f24cb62188df04fc9e653e..f951acb2ce362a82ccaf4b247909cadce3194183 100644 (file)
@@ -367,6 +367,7 @@ got_gpio:
 
                ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc),
                                              gpio_handler,
+                                             IRQF_SHARED |
                                              IRQF_TRIGGER_RISING |
                                              IRQF_TRIGGER_FALLING,
                                              gpios[i].name,
index 7958c9defd4922fe8a337008f09ae00e0ff9b8ae..adb69d7820a809f14a38a9bc608b1e7f609fc39b 100644 (file)
@@ -49,19 +49,6 @@ static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd,
        return ret;
 }
 
-static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd)
-{
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
-}
-
-static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd)
-{
-       mutex_unlock(&rtd->card->pcm_mutex);
-}
-
-#define snd_soc_dpcm_mutex_assert_held(rtd) \
-       lockdep_assert_held(&(rtd)->card->pcm_mutex)
-
 static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
                                                int stream)
 {
@@ -1230,7 +1217,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
 
        dpcm->be = be;
        dpcm->fe = fe;
-       be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
        dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
        snd_soc_dpcm_stream_lock_irq(fe, stream);
        list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
@@ -1465,10 +1451,11 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
        struct snd_soc_dapm_widget_list *list = *list_;
        struct snd_soc_pcm_runtime *be;
        struct snd_soc_dapm_widget *widget;
+       struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
        int i, new = 0, err;
 
        /* don't connect if FE is not running */
-       if (!fe->dpcm[stream].runtime && !fe->fe_compr)
+       if (!fe_substream->runtime && !fe->fe_compr)
                return new;
 
        /* Create any new FE <--> BE connections */
@@ -1590,6 +1577,7 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
 
 int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
 {
+       struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
        struct snd_soc_pcm_runtime *be;
        struct snd_soc_dpcm *dpcm;
        int err, count = 0;
@@ -1629,7 +1617,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
                dev_dbg(be->dev, "ASoC: open %s BE %s\n",
                        stream ? "capture" : "playback", be->dai_link->name);
 
-               be_substream->runtime = be->dpcm[stream].runtime;
+               be_substream->runtime = fe_substream->runtime;
                err = __soc_pcm_open(be, be_substream);
                if (err < 0) {
                        be->dpcm[stream].users--;
@@ -2663,7 +2651,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
        struct snd_soc_pcm_runtime *fe;
        int ret = 0;
 
-       mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass);
+       snd_soc_dpcm_mutex_lock(card);
        /* shutdown all old paths first */
        for_each_card_rtds(card, fe) {
                ret = soc_dpcm_fe_runtime_update(fe, 0);
@@ -2679,7 +2667,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
        }
 
 out:
-       mutex_unlock(&card->pcm_mutex);
+       snd_soc_dpcm_mutex_unlock(card);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update);
@@ -2697,8 +2685,6 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream)
                dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
 
        dpcm_be_disconnect(fe, stream);
-
-       fe->dpcm[stream].runtime = NULL;
 }
 
 static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
@@ -2723,7 +2709,6 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
        int stream = fe_substream->stream;
 
        snd_soc_dpcm_mutex_lock(fe);
-       fe->dpcm[stream].runtime = fe_substream->runtime;
 
        ret = dpcm_path_get(fe, stream, &list);
        if (ret < 0)
@@ -2795,9 +2780,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
                struct snd_soc_dai *codec_dai;
 
                /* Adapt stream for codec2codec links */
-               int cpu_capture = rtd->dai_link->params ?
+               int cpu_capture = rtd->dai_link->c2c_params ?
                        SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
-               int cpu_playback = rtd->dai_link->params ?
+               int cpu_playback = rtd->dai_link->c2c_params ?
                        SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
 
                for_each_rtd_codec_dais(rtd, i, codec_dai) {
@@ -2841,7 +2826,7 @@ static int soc_create_pcm(struct snd_pcm **pcm,
        int ret;
 
        /* create the PCM */
-       if (rtd->dai_link->params) {
+       if (rtd->dai_link->c2c_params) {
                snprintf(new_name, sizeof(new_name), "codec2codec(%s)",
                         rtd->dai_link->stream_name);
 
@@ -2898,7 +2883,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
         * don't interface with the outside world or application layer
         * we don't have to do any special handling on close.
         */
-       if (!rtd->dai_link->params)
+       if (!rtd->dai_link->c2c_params)
                rtd->close_delayed_work_func = snd_soc_close_delayed_work;
 
        rtd->pcm = pcm;
@@ -2906,7 +2891,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        pcm->private_data = rtd;
        pcm->no_device_suspend = true;
 
-       if (rtd->dai_link->no_pcm || rtd->dai_link->params) {
+       if (rtd->dai_link->no_pcm || rtd->dai_link->c2c_params) {
                if (playback)
                        pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
                if (capture)
index 07421f5d4ebd8b9d9a95ebae6abd070cd0f7b6ec..d0aca6b9058b28bc64cfb3a88ef77c208726b9b7 100644 (file)
@@ -1694,11 +1694,9 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
 
        link->cpus      = &dlc[0];
        link->codecs    = &dlc[1];
-       link->platforms = &dlc[2];
 
        link->num_cpus   = 1;
        link->num_codecs = 1;
-       link->num_platforms = 1;
 
        link->dobj.index = tplg->index;
        link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
@@ -1726,7 +1724,13 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
        link->codecs->name = "snd-soc-dummy";
        link->codecs->dai_name = "snd-soc-dummy-dai";
 
+       /*
+        * Many topology is assuming link has Platform.
+        * This might be overwritten at soc_tplg_dai_link_load().
+        */
+       link->platforms = &dlc[2];
        link->platforms->name = "snd-soc-dummy";
+       link->num_platforms = 1;
 
        /* enable DPCM */
        link->dynamic = 1;
@@ -1745,7 +1749,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
                goto err;
        }
 
-       ret = snd_soc_add_pcm_runtime(tplg->comp->card, link);
+       ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1);
        if (ret < 0) {
                dev_err(tplg->dev, "ASoC: adding FE link failed\n");
                goto err;
index bd6c1b198736c031b1454e49206653c6bd5603e6..df36b411a12e66eb0e0bb8a4ab84a13775470491 100644 (file)
 #include "acp-dsp-offset.h"
 #include <sound/sof/xtensa.h>
 
-int acp_dai_probe(struct snd_soc_dai *dai)
-{
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
-       const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
-       unsigned int val;
-
-       val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->i2s_pin_config_offset);
-       if (val != desc->i2s_mode) {
-               dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_NS(acp_dai_probe, SND_SOC_SOF_AMD_COMMON);
-
 /**
  * amd_sof_ipc_dump() - This function is called when IPC tx times out.
  * @sdev: SOF device.
@@ -187,6 +171,7 @@ struct snd_sof_dsp_ops sof_acp_common_ops = {
        .pcm_open               = acp_pcm_open,
        .pcm_close              = acp_pcm_close,
        .pcm_hw_params          = acp_pcm_hw_params,
+       .pcm_pointer            = acp_pcm_pointer,
 
        .hw_info                = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
index 727c3a784a204f105c185c704b23c7c95933c9a2..0828245bbb997de398414bf36e0bb5cbcb046314 100644 (file)
@@ -39,6 +39,7 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr
        platform_params->use_phy_address = true;
        platform_params->phy_addr = stream->reg_offset;
        platform_params->stream_tag = stream->stream_tag;
+       platform_params->cont_update_posn = 1;
 
        /* write buffer size of stream in scratch memory */
 
@@ -84,3 +85,36 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
        return acp_dsp_stream_put(sdev, stream);
 }
 EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON);
+
+snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
+                                 struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_component *scomp = sdev->component;
+       struct snd_sof_pcm_stream *stream;
+       struct sof_ipc_stream_posn posn;
+       struct snd_sof_pcm *spcm;
+       snd_pcm_uframes_t pos;
+       int ret;
+
+       spcm = snd_sof_find_spcm_dai(scomp, rtd);
+       if (!spcm) {
+               dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n",
+                                    rtd->dai_link->id);
+               return 0;
+       }
+
+       stream = &spcm->stream[substream->stream];
+       ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
+       if (ret < 0) {
+               dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
+               return 0;
+       }
+
+       memcpy(&stream->posn, &posn, sizeof(posn));
+       pos = spcm->stream[substream->stream].posn.host_posn;
+       pos = bytes_to_frames(substream->runtime, pos);
+
+       return pos;
+}
+EXPORT_SYMBOL_NS(acp_pcm_pointer, SND_SOC_SOF_AMD_COMMON);
index d5ccd4d09278fb062854597985f39f0eada05525..2ae76bcd3590c3b31c2be257fa4ff733a152c035 100644 (file)
@@ -470,33 +470,39 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        unsigned int addr;
        int ret;
 
+       chip = get_chip_info(sdev->pdata);
+       if (!chip) {
+               dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
+               return -EIO;
+       }
        adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
                             GFP_KERNEL);
        if (!adata)
                return -ENOMEM;
 
        adata->dev = sdev;
+       adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
+                                                       PLATFORM_DEVID_NONE, NULL, 0);
+       if (IS_ERR(adata->dmic_dev)) {
+               dev_err(sdev->dev, "failed to register platform for dmic codec\n");
+               return PTR_ERR(adata->dmic_dev);
+       }
        addr = pci_resource_start(pci, ACP_DSP_BAR);
        sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR));
        if (!sdev->bar[ACP_DSP_BAR]) {
                dev_err(sdev->dev, "ioremap error\n");
-               return -ENXIO;
+               ret = -ENXIO;
+               goto unregister_dev;
        }
 
        pci_set_master(pci);
 
        sdev->pdata->hw_pdata = adata;
-
-       chip = get_chip_info(sdev->pdata);
-       if (!chip) {
-               dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
-               return -EIO;
-       }
-
        adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
        if (!adata->smn_dev) {
                dev_err(sdev->dev, "Failed to get host bridge device\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto unregister_dev;
        }
 
        sdev->ipc_irq = pci->irq;
@@ -505,16 +511,12 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        if (ret < 0) {
                dev_err(sdev->dev, "failed to register IRQ %d\n",
                        sdev->ipc_irq);
-               pci_dev_put(adata->smn_dev);
-               return ret;
+               goto free_smn_dev;
        }
 
        ret = acp_init(sdev);
-       if (ret < 0) {
-               free_irq(sdev->ipc_irq, sdev);
-               pci_dev_put(adata->smn_dev);
-               return ret;
-       }
+       if (ret < 0)
+               goto free_ipc_irq;
 
        sdev->dsp_box.offset = 0;
        sdev->dsp_box.size = BOX_SIZE_512;
@@ -530,6 +532,14 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        acp_dsp_stream_init(sdev);
 
        return 0;
+
+free_ipc_irq:
+       free_irq(sdev->ipc_irq, sdev);
+free_smn_dev:
+       pci_dev_put(adata->smn_dev);
+unregister_dev:
+       platform_device_unregister(adata->dmic_dev);
+       return ret;
 }
 EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
 
@@ -543,6 +553,9 @@ int amd_sof_acp_remove(struct snd_sof_dev *sdev)
        if (sdev->ipc_irq)
                free_irq(sdev->ipc_irq, sdev);
 
+       if (adata->dmic_dev)
+               platform_device_unregister(adata->dmic_dev);
+
        return acp_reset(sdev);
 }
 EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
index 39165ebf684b68e6f9a37a46b12d1b4e59dfd1c7..1c535cc6c3a95e455e722bb78da466ee0a770158 100644 (file)
@@ -158,12 +158,10 @@ struct acp_dsp_stream {
 struct sof_amd_acp_desc {
        unsigned int rev;
        unsigned int host_bridge_id;
-       unsigned int i2s_mode;
        u32 pgfsm_base;
        u32 ext_intr_stat;
        u32 dsp_intr_base;
        u32 sram_pte_offset;
-       u32 i2s_pin_config_offset;
        u32 hw_semaphore_offset;
        u32 acp_clkmux_sel;
        u32 fusion_dsp_offset;
@@ -172,6 +170,8 @@ struct sof_amd_acp_desc {
 /* Common device data struct for ACP devices */
 struct acp_dev_data {
        struct snd_sof_dev  *dev;
+       /* DMIC device */
+       struct platform_device *dmic_dev;
        unsigned int fw_bin_size;
        unsigned int fw_data_bin_size;
        u32 fw_bin_page_count;
@@ -238,6 +238,8 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
 int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
                      struct snd_pcm_hw_params *params,
                      struct snd_sof_platform_stream_params *platform_params);
+snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
+                                 struct snd_pcm_substream *substream);
 
 extern struct snd_sof_dsp_ops sof_acp_common_ops;
 
@@ -246,7 +248,6 @@ int sof_renoir_ops_init(struct snd_sof_dev *sdev);
 extern struct snd_sof_dsp_ops sof_rembrandt_ops;
 int sof_rembrandt_ops_init(struct snd_sof_dev *sdev);
 
-int acp_dai_probe(struct snd_soc_dai *dai);
 struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev);
 /* Machine configuration */
 int snd_amd_acp_find_config(struct pci_dev *pci);
index 4e1de462b431b8ba251cfc27f888d127f26d6f2c..eaf70ea6e556e9b54a3d258de17b9b11d5361666 100644 (file)
 #define ACP6x_REG_START                0x1240000
 #define ACP6x_REG_END          0x125C000
 
-static struct platform_device *dmic_dev;
-static struct platform_device *pdev;
-
-static const struct resource rembrandt_res[] = {
-       {
-               .start = 0,
-               .end = ACP6x_REG_END - ACP6x_REG_START,
-               .name = "acp_mem",
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .start = 0,
-               .end = 0,
-               .name = "acp_dai_irq",
-               .flags = IORESOURCE_IRQ,
-       },
-};
-
 static const struct sof_amd_acp_desc rembrandt_chip_info = {
        .rev            = 6,
        .host_bridge_id = HOST_BRIDGE_RMB,
-       .i2s_mode       = 0x0a,
        .pgfsm_base     = ACP6X_PGFSM_BASE,
        .ext_intr_stat  = ACP6X_EXT_INTR_STAT,
        .dsp_intr_base  = ACP6X_DSP_SW_INTR_BASE,
        .sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
-       .i2s_pin_config_offset = ACP6X_I2S_PIN_CONFIG,
        .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
        .acp_clkmux_sel = ACP6X_CLKMUX_SEL,
        .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
@@ -83,84 +63,17 @@ static const struct sof_dev_desc rembrandt_desc = {
 
 static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 {
-       struct platform_device_info pdevinfo;
-       struct device *dev = &pci->dev;
-       const struct resource *res_i2s;
-       struct resource *res;
-       unsigned int flag, i, addr;
-       int ret;
+       unsigned int flag;
 
        flag = snd_amd_acp_find_config(pci);
        if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
                return -ENODEV;
 
-       ret = sof_pci_probe(pci, pci_id);
-       if (ret != 0)
-               return ret;
-
-       dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
-       if (IS_ERR(dmic_dev)) {
-               dev_err(dev, "failed to create DMIC device\n");
-               sof_pci_remove(pci);
-               return PTR_ERR(dmic_dev);
-       }
-
-       /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
-       if (flag != FLAG_AMD_SOF_ONLY_DMIC)
-               return 0;
-
-       addr = pci_resource_start(pci, 0);
-       res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(rembrandt_res),
-                          GFP_KERNEL);
-       if (!res) {
-               platform_device_unregister(dmic_dev);
-               sof_pci_remove(pci);
-               return -ENOMEM;
-       }
-
-       res_i2s = rembrandt_res;
-       for (i = 0; i < ARRAY_SIZE(rembrandt_res); i++, res_i2s++) {
-               res[i].name = res_i2s->name;
-               res[i].flags = res_i2s->flags;
-               res[i].start = addr + res_i2s->start;
-               res[i].end = addr + res_i2s->end;
-               if (res_i2s->flags == IORESOURCE_IRQ) {
-                       res[i].start = pci->irq;
-                       res[i].end = res[i].start;
-               }
-       }
-
-       memset(&pdevinfo, 0, sizeof(pdevinfo));
-
-       /*
-        * We have common PCI driver probe for ACP device but we have to support I2S without SOF
-        * for some distributions. Register platform device that will be used to support non dsp
-        * ACP's audio ends points on some machines.
-        */
-       pdevinfo.name = "acp_asoc_rembrandt";
-       pdevinfo.id = 0;
-       pdevinfo.parent = &pci->dev;
-       pdevinfo.num_res = ARRAY_SIZE(rembrandt_res);
-       pdevinfo.res = &res[0];
-
-       pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev)) {
-               dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
-               platform_device_unregister(dmic_dev);
-               sof_pci_remove(pci);
-               ret = PTR_ERR(pdev);
-       }
-
-       return ret;
+       return sof_pci_probe(pci, pci_id);
 };
 
 static void acp_pci_rmb_remove(struct pci_dev *pci)
 {
-       if (dmic_dev)
-               platform_device_unregister(dmic_dev);
-       if (pdev)
-               platform_device_unregister(pdev);
-
        sof_pci_remove(pci);
 }
 
index fca40b261671bd901b5b51f526c0d4d11e072082..4809cb644152b12590756f3c9ec84422a693cebc 100644 (file)
 #define ACP3x_REG_START                0x1240000
 #define ACP3x_REG_END          0x125C000
 
-static struct platform_device *dmic_dev;
-static struct platform_device *pdev;
-
-static const struct resource renoir_res[] = {
-       {
-               .start = 0,
-               .end = ACP3x_REG_END - ACP3x_REG_START,
-               .name = "acp_mem",
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .start = 0,
-               .end = 0,
-               .name = "acp_dai_irq",
-               .flags = IORESOURCE_IRQ,
-       },
-};
-
 static const struct sof_amd_acp_desc renoir_chip_info = {
        .rev            = 3,
        .host_bridge_id = HOST_BRIDGE_CZN,
-       .i2s_mode       = 0x04,
        .pgfsm_base     = ACP3X_PGFSM_BASE,
        .ext_intr_stat  = ACP3X_EXT_INTR_STAT,
        .dsp_intr_base  = ACP3X_DSP_SW_INTR_BASE,
        .sram_pte_offset = ACP3X_SRAM_PTE_OFFSET,
-       .i2s_pin_config_offset = ACP3X_I2S_PIN_CONFIG,
        .hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0,
        .acp_clkmux_sel = ACP3X_CLKMUX_SEL,
 };
@@ -83,84 +63,17 @@ static const struct sof_dev_desc renoir_desc = {
 
 static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 {
-       struct platform_device_info pdevinfo;
-       struct device *dev = &pci->dev;
-       const struct resource *res_i2s;
-       struct resource *res;
-       unsigned int flag, i, addr;
-       int ret;
+       unsigned int flag;
 
        flag = snd_amd_acp_find_config(pci);
        if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
                return -ENODEV;
 
-       ret = sof_pci_probe(pci, pci_id);
-       if (ret != 0)
-               return ret;
-
-       dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
-       if (IS_ERR(dmic_dev)) {
-               dev_err(dev, "failed to create DMIC device\n");
-               sof_pci_remove(pci);
-               return PTR_ERR(dmic_dev);
-       }
-
-       /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
-       if (flag != FLAG_AMD_SOF_ONLY_DMIC)
-               return 0;
-
-       addr = pci_resource_start(pci, 0);
-       res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
-       if (!res) {
-               sof_pci_remove(pci);
-               platform_device_unregister(dmic_dev);
-               return -ENOMEM;
-       }
-
-       res_i2s = renoir_res;
-       for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) {
-               res[i].name = res_i2s->name;
-               res[i].flags = res_i2s->flags;
-               res[i].start = addr + res_i2s->start;
-               res[i].end = addr + res_i2s->end;
-               if (res_i2s->flags == IORESOURCE_IRQ) {
-                       res[i].start = pci->irq;
-                       res[i].end = res[i].start;
-               }
-       }
-
-       memset(&pdevinfo, 0, sizeof(pdevinfo));
-
-       /*
-        * We have common PCI driver probe for ACP device but we have to support I2S without SOF
-        * for some distributions. Register platform device that will be used to support non dsp
-        * ACP's audio ends points on some machines.
-        */
-
-       pdevinfo.name = "acp_asoc_renoir";
-       pdevinfo.id = 0;
-       pdevinfo.parent = &pci->dev;
-       pdevinfo.num_res = ARRAY_SIZE(renoir_res);
-       pdevinfo.res = &res[0];
-
-       pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev)) {
-               dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
-               sof_pci_remove(pci);
-               platform_device_unregister(dmic_dev);
-               ret = PTR_ERR(pdev);
-       }
-
-       return ret;
+       return sof_pci_probe(pci, pci_id);
 };
 
 static void acp_pci_rn_remove(struct pci_dev *pci)
 {
-       if (dmic_dev)
-               platform_device_unregister(dmic_dev);
-       if (pdev)
-               platform_device_unregister(pdev);
-
        return sof_pci_remove(pci);
 }
 
index 5288ab882fc94c80f9fbb63b2ce64260e9173b79..f1d1ba57ab3a0d716daa42ede437d5cf36518e70 100644 (file)
@@ -48,7 +48,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 48000,
                },
-               .probe = &acp_dai_probe,
        },
 
        [I2S_BT_INSTANCE] = {
@@ -73,7 +72,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 48000,
                },
-               .probe = &acp_dai_probe,
        },
 
        [I2S_SP_INSTANCE] = {
@@ -98,7 +96,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 48000,
                },
-               .probe = &acp_dai_probe,
        },
 
        [PDM_DMIC_INSTANCE] = {
@@ -126,7 +123,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 96000,
                },
-               .probe = &acp_dai_probe,
        },
 };
 
index adade2e3d3be6ca65a008be1b7d53fa31bc448d6..47b863f6258c6d5d922411251155e5eb664151b5 100644 (file)
@@ -47,7 +47,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 48000,
                },
-               .probe = &acp_dai_probe,
        },
 
        [I2S_SP_INSTANCE] = {
@@ -72,7 +71,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 48000,
                },
-               .probe = &acp_dai_probe,
        },
 
        [PDM_DMIC_INSTANCE] = {
@@ -100,7 +98,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
                        .rate_min = 8000,
                        .rate_max = 96000,
                },
-               .probe = &acp_dai_probe,
        },
 };
 
index 8d205eb16d2f312ce3d0329de5b07c632f8f832e..d7b044f33d79633afa2f3a3d8f58602a6c7565dc 100644 (file)
@@ -135,7 +135,6 @@ static int sof_compr_free(struct snd_soc_component *component,
        struct sof_compr_stream *sstream = cstream->runtime->private_data;
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        struct sof_ipc_stream stream;
-       struct sof_ipc_reply reply;
        struct snd_sof_pcm *spcm;
        int ret = 0;
 
@@ -148,8 +147,7 @@ static int sof_compr_free(struct snd_soc_component *component,
        stream.comp_id = spcm->stream[cstream->direction].comp_id;
 
        if (spcm->prepared[cstream->direction]) {
-               ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
-                                        &reply, sizeof(reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
                if (!ret)
                        spcm->prepared[cstream->direction] = false;
        }
@@ -273,7 +271,6 @@ static int sof_compr_trigger(struct snd_soc_component *component,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        struct sof_ipc_stream stream;
-       struct sof_ipc_reply reply;
        struct snd_sof_pcm *spcm;
 
        spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -302,8 +299,7 @@ static int sof_compr_trigger(struct snd_soc_component *component,
                break;
        }
 
-       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
-                                 &reply, sizeof(reply));
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
 }
 
 static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,
index 7de8673a01ce10ea5aa1bf93ed4b28573f0685c5..9a9d82220fd0d7a65ea09aa5628f8b5cb5ca0d70 100644 (file)
@@ -208,6 +208,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
        /* set up platform component driver */
        snd_sof_new_platform_drv(sdev);
 
+       if (sdev->dspless_mode_selected) {
+               sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+               goto skip_dsp_init;
+       }
+
        /* register any debug/trace capabilities */
        ret = snd_sof_dbg_init(sdev);
        if (ret < 0) {
@@ -266,6 +271,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
                dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
        }
 
+skip_dsp_init:
        /* hereafter all FW boot flows are for PM reasons */
        sdev->first_boot = false;
 
@@ -365,6 +371,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
        if (sof_core_debug)
                dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
 
+       if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) {
+               if (plat_data->desc->dspless_mode_supported) {
+                       dev_info(dev, "Switching to DSPless mode\n");
+                       sdev->dspless_mode_selected = true;
+               } else {
+                       dev_info(dev, "DSPless mode is not supported by the platform\n");
+               }
+       }
+
        /* check IPC support */
        if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
                dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
@@ -378,12 +393,18 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
                return ret;
 
        /* check all mandatory ops */
-       if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
-           !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
-           !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
-           !sof_ops(sdev)->ipc_msg_data) {
+       if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
+               sof_ops_free(sdev);
+               dev_err(dev, "missing mandatory ops\n");
+               return -EINVAL;
+       }
+
+       if (!sdev->dspless_mode_selected &&
+           (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
+            !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
+            !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
                sof_ops_free(sdev);
-               dev_err(dev, "error: missing mandatory ops\n");
+               dev_err(dev, "missing mandatory DSP ops\n");
                return -EINVAL;
        }
 
index ade0507328af4bf9c7d6be10cbdd433fc13ac091..b42b5982cbbca6cabf6c27384745d131689d158a 100644 (file)
@@ -370,6 +370,7 @@ static const struct soc_fw_state_info {
        const char *name;
 } fw_state_dbg[] = {
        {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
+       {SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"},
        {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"},
        {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"},
        {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"},
index 715ba8a7f2f8da494cb9c6f2cc27f2297e68996c..f4eeacf1f281f581f6a352cbdd57e507f53f995a 100644 (file)
@@ -269,6 +269,13 @@ config SND_SOC_SOF_HDA_COMMON
        select SND_INTEL_DSP_CONFIG
        select SND_SOC_SOF_HDA_LINK_BASELINE
        select SND_SOC_SOF_HDA_PROBES
+       select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
+       help
+         This option is not user-selectable but automagically handled by
+         'select' statements at a higher level.
+
+config SND_SOC_SOF_HDA_MLINK
+       tristate
        help
          This option is not user-selectable but automagically handled by
          'select' statements at a higher level.
index 8201cbdd654c264c8318642c21c1fd82096cdf2e..fdb463c12e91daedbc9e47a8d8286b275d86db50 100644 (file)
@@ -5,10 +5,12 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
 
 snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
                                 hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
-                                hda-dai.o hda-bus.o hda-mlink.o \
+                                hda-dai.o hda-dai-ops.o hda-bus.o \
                                 skl.o hda-loader-skl.o \
                                 apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o
 
+snd-sof-intel-hda-mlink-objs := hda-mlink.o
+
 snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
 
 snd-sof-intel-hda-objs := hda-codec.o
@@ -19,6 +21,7 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o
 obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o
 obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o
 obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
+obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o
 obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
 
 snd-sof-pci-intel-tng-objs := pci-tng.o
index 0e7a7e4ad976bf86b56d32866ef3a163be9c655a..e1f25a8f0c3262bae0f266b38f0f20fa15925c0c 100644 (file)
@@ -48,6 +48,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_apl_ops.ipc_dump    = hda_ipc_dump;
+
+               sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
        }
 
        if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -73,6 +75,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_apl_ops.ipc_dump    = hda_ipc4_dump;
+
+               sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
        }
 
        /* set DAI driver ops */
index a08a77fa946b6c7ca61a90fa883274c68d66bdce..a95222e53ecf30a3a344a7a76771835d0b42485e 100644 (file)
@@ -395,6 +395,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_cnl_ops.ipc_dump    = cnl_ipc_dump;
+
+               sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
        }
 
        if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -420,6 +422,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_cnl_ops.ipc_dump    = cnl_ipc4_dump;
+
+               sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
        }
 
        /* set DAI driver ops */
index 397303b3ac9d5cfacb7719f3ff0e74cf9724ed9d..8e1cd0babd32c50e36741dc0e20556e6c0bdee3e 100644 (file)
@@ -89,7 +89,6 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
        .runtime_resume         = hda_dsp_runtime_resume,
        .runtime_idle           = hda_dsp_runtime_idle,
        .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
-       .set_power_state        = hda_dsp_set_power_state,
 
        /* ALSA HW info flags */
        .hw_info =      SNDRV_PCM_INFO_MMAP |
index f3bdeba2841221f8b205109db46d53ab04a112f1..84bf01bd360a3499c0b2012b3ab625569f3a0c30 100644 (file)
@@ -19,6 +19,7 @@
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_register.h>
 #include <sound/hda_component.h>
+#include <sound/hda-mlink.h>
 #include "../ops.h"
 #include "hda.h"
 
@@ -158,16 +159,18 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
  */
 int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
 {
+       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        u32 val;
 
        /* enable/disable audio dsp clock gating */
        val = enable ? PCI_CGCTL_ADSPDCGE : 0;
        snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val);
 
-       /* enable/disable DMI Link L1 support */
+       /* disable the DMI link when requested. But enable only if it wasn't disabled previously */
        val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0;
-       snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
-                               HDA_VS_INTEL_EM2_L1SEN, val);
+       if (!enable || !hda->l1_disabled)
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+                                       HDA_VS_INTEL_EM2_L1SEN, val);
 
        /* enable/disable audio dsp power gating */
        val = enable ? 0 : PCI_PGCTL_ADSPPGD;
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
new file mode 100644 (file)
index 0000000..4b39cec
--- /dev/null
@@ -0,0 +1,390 @@
+// 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) 2022 Intel Corporation. All rights reserved.
+
+#include <sound/pcm_params.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/sof/ipc4/header.h>
+#include <uapi/sound/sof/header.h>
+#include "../ipc4-priv.h"
+#include "../ipc4-topology.h"
+#include "../sof-priv.h"
+#include "../sof-audio.h"
+#include "hda.h"
+
+/* These ops are only applicable for the HDA DAI's in their current form */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+/*
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
+ */
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+                         int dir, int stream_tag)
+{
+       struct snd_pcm_substream *fe_substream;
+       struct hdac_stream *fe_hstream;
+       struct snd_soc_dpcm *dpcm;
+
+       for_each_dpcm_fe(rtd, dir, dpcm) {
+               fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+               fe_hstream = fe_substream->runtime->private_data;
+               if (fe_hstream->stream_tag == stream_tag)
+                       return true;
+       }
+
+       return false;
+}
+
+static struct hdac_ext_stream *
+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct sof_intel_hda_stream *hda_stream;
+       const struct sof_intel_dsp_desc *chip;
+       struct snd_sof_dev *sdev;
+       struct hdac_ext_stream *res = NULL;
+       struct hdac_stream *hstream = NULL;
+
+       int stream_dir = substream->stream;
+
+       if (!bus->ppcap) {
+               dev_err(bus->dev, "stream type not supported\n");
+               return NULL;
+       }
+
+       spin_lock_irq(&bus->reg_lock);
+       list_for_each_entry(hstream, &bus->stream_list, list) {
+               struct hdac_ext_stream *hext_stream =
+                       stream_to_hdac_ext_stream(hstream);
+               if (hstream->direction != substream->stream)
+                       continue;
+
+               hda_stream = hstream_to_sof_hda_stream(hext_stream);
+               sdev = hda_stream->sdev;
+               chip = get_chip_info(sdev->pdata);
+
+               /* check if link is available */
+               if (!hext_stream->link_locked) {
+                       /*
+                        * choose the first available link for platforms that do not have the
+                        * PROCEN_FMT_QUIRK set.
+                        */
+                       if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
+                               res = hext_stream;
+                               break;
+                       }
+
+                       if (hstream->opened) {
+                               /*
+                                * check if the stream tag matches the stream
+                                * tag of one of the connected FEs
+                                */
+                               if (hda_check_fes(rtd, stream_dir,
+                                                 hstream->stream_tag)) {
+                                       res = hext_stream;
+                                       break;
+                               }
+                       } else {
+                               res = hext_stream;
+
+                               /*
+                                * This must be a hostless stream.
+                                * So reserve the host DMA channel.
+                                */
+                               hda_stream->host_reserved = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (res) {
+               /* Make sure that host and link DMA is decoupled. */
+               snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
+               res->link_locked = 1;
+               res->link_substream = substream;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+
+       return res;
+}
+
+static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
+                                                  struct snd_soc_dai *cpu_dai,
+                                                  struct snd_pcm_substream *substream)
+{
+       return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
+static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
+                                                     struct snd_soc_dai *cpu_dai,
+                                                     struct snd_pcm_substream *substream)
+{
+       struct hdac_ext_stream *hext_stream;
+
+       hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
+       if (!hext_stream)
+               return NULL;
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+
+       return hext_stream;
+}
+
+static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                                   struct snd_pcm_substream *substream)
+{
+       struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+       snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+}
+
+static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+                                 unsigned int format_val)
+{
+       snd_hdac_ext_stream_setup(hext_stream, format_val);
+}
+
+static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+{
+       snd_hdac_ext_stream_reset(hext_stream);
+}
+
+static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                               struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_sof_widget *pipe_widget;
+       struct sof_ipc4_pipeline *pipeline;
+       struct snd_sof_widget *swidget;
+       struct snd_soc_dapm_widget *w;
+       int ret;
+
+       w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+       swidget = w->dobj.private;
+       pipe_widget = swidget->spipe->pipe_widget;
+       pipeline = pipe_widget->private;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+                                                 SOF_IPC4_PIPE_PAUSED);
+               if (ret < 0)
+                       return ret;
+
+               pipeline->state = SOF_IPC4_PIPE_PAUSED;
+               break;
+       default:
+               dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                      struct snd_pcm_substream *substream, int cmd)
+{
+       struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               snd_hdac_ext_stream_start(hext_stream);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               snd_hdac_ext_stream_clear(hext_stream);
+               break;
+       default:
+               dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                                struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_sof_widget *pipe_widget;
+       struct sof_ipc4_pipeline *pipeline;
+       struct snd_sof_widget *swidget;
+       struct snd_soc_dapm_widget *w;
+       int ret;
+
+       w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+       swidget = w->dobj.private;
+       pipe_widget = swidget->spipe->pipe_widget;
+       pipeline = pipe_widget->private;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+                       ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+                                                         SOF_IPC4_PIPE_PAUSED);
+                       if (ret < 0)
+                               return ret;
+                       pipeline->state = SOF_IPC4_PIPE_PAUSED;
+               }
+
+               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+                                                 SOF_IPC4_PIPE_RUNNING);
+               if (ret < 0)
+                       return ret;
+               pipeline->state = SOF_IPC4_PIPE_RUNNING;
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       {
+               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+                                                 SOF_IPC4_PIPE_RESET);
+               if (ret < 0)
+                       return ret;
+
+               pipeline->state = SOF_IPC4_PIPE_RESET;
+               break;
+       }
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               break;
+       default:
+               dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_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,
+       .pre_trigger = hda_ipc4_pre_trigger,
+       .trigger = hda_trigger,
+       .post_trigger = hda_ipc4_post_trigger
+};
+
+static const struct hda_dai_widget_dma_ops hda_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,
+};
+
+static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                                struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       {
+               struct snd_sof_dai_config_data data = { 0 };
+
+               data.dai_data = DMA_CHAN_INVALID;
+               return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
+       }
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc3_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,
+       .post_trigger = hda_ipc3_post_trigger,
+};
+
+static struct hdac_ext_stream *
+hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                           struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *hstream = substream->runtime->private_data;
+
+       return stream_to_hdac_ext_stream(hstream);
+}
+
+static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
+                                         struct hdac_ext_stream *hext_stream,
+                                         unsigned int format_val)
+{
+       /*
+        * Save the format_val which was adjusted by the maxbps of the codec.
+        * This information is not available on the FE side since there we are
+        * using dummy_codec.
+        */
+       hext_stream->hstream.format_val = format_val;
+}
+
+static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
+       .get_hext_stream = hda_dspless_get_hext_stream,
+       .setup_hext_stream = hda_dspless_setup_hext_stream,
+};
+
+#endif
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+       struct snd_sof_dai *sdai;
+
+       if (sdev->dspless_mode_selected)
+               return &hda_dspless_dma_ops;
+
+       sdai = swidget->private;
+
+       switch (sdev->pdata->ipc_type) {
+       case SOF_IPC:
+       {
+               struct sof_dai_private_data *private = sdai->private;
+
+               if (private->dai_config->type == SOF_DAI_INTEL_HDA)
+                       return &hda_ipc3_dma_ops;
+               break;
+       }
+       case SOF_INTEL_IPC4:
+       {
+               struct sof_ipc4_copier *ipc4_copier = sdai->private;
+
+               if (ipc4_copier->dai_type == 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;
+               }
+               break;
+       }
+       default:
+               break;
+       }
+#endif
+       return NULL;
+}
index 8d9c38d562d3dcaf75760a6a870229ca575eb805..44a5d94c5050fc21f39093470344fabfc823662a 100644 (file)
@@ -27,119 +27,77 @@ static bool hda_use_tplg_nhlt;
 module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
 MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
 
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+                  struct snd_sof_dai_config_data *data)
+{
+       struct snd_sof_widget *swidget = w->dobj.private;
+       const struct sof_ipc_tplg_ops *tplg_ops;
+       struct snd_soc_component *component;
+       struct snd_sof_dev *sdev;
+       int ret;
 
-struct hda_pipe_params {
-       u32 ch;
-       u32 s_freq;
-       snd_pcm_format_t format;
-       int link_index;
-       unsigned int link_bps;
-};
+       if (!swidget)
+               return 0;
 
-/*
- * This function checks if the host dma channel corresponding
- * to the link DMA stream_tag argument is assigned to one
- * of the FEs connected to the BE DAI.
- */
-static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
-                         int dir, int stream_tag)
-{
-       struct snd_pcm_substream *fe_substream;
-       struct hdac_stream *fe_hstream;
-       struct snd_soc_dpcm *dpcm;
-
-       for_each_dpcm_fe(rtd, dir, dpcm) {
-               fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
-               fe_hstream = fe_substream->runtime->private_data;
-               if (fe_hstream->stream_tag == stream_tag)
-                       return true;
+       component = swidget->scomp;
+       sdev = snd_soc_component_get_drvdata(component);
+       tplg_ops = sof_ipc_get_ops(sdev, tplg);
+
+       if (tplg_ops && tplg_ops->dai_config) {
+               ret = tplg_ops->dai_config(sdev, swidget, flags, data);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
+                               flags, w->name);
+                       return ret;
+               }
        }
 
-       return false;
+       return 0;
 }
 
-static struct hdac_ext_stream *
-hda_link_stream_assign(struct hdac_bus *bus,
-                      struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct sof_intel_hda_stream *hda_stream;
-       const struct sof_intel_dsp_desc *chip;
-       struct snd_sof_dev *sdev;
-       struct hdac_ext_stream *res = NULL;
-       struct hdac_stream *hstream = NULL;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
 
-       int stream_dir = substream->stream;
+static const struct hda_dai_widget_dma_ops *
+hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+       struct snd_sof_widget *swidget = w->dobj.private;
+       struct snd_sof_dai *sdai;
 
-       if (!bus->ppcap) {
-               dev_err(bus->dev, "stream type not supported\n");
-               return NULL;
-       }
+       /*
+        * The swidget parameter of hda_select_dai_widget_ops() is ignored in
+        * case of DSPless mode
+        */
+       if (sdev->dspless_mode_selected)
+               return hda_select_dai_widget_ops(sdev, NULL);
 
-       spin_lock_irq(&bus->reg_lock);
-       list_for_each_entry(hstream, &bus->stream_list, list) {
-               struct hdac_ext_stream *hext_stream =
-                       stream_to_hdac_ext_stream(hstream);
-               if (hstream->direction != substream->stream)
-                       continue;
-
-               hda_stream = hstream_to_sof_hda_stream(hext_stream);
-               sdev = hda_stream->sdev;
-               chip = get_chip_info(sdev->pdata);
-
-               /* check if link is available */
-               if (!hext_stream->link_locked) {
-                       /*
-                        * choose the first available link for platforms that do not have the
-                        * PROCEN_FMT_QUIRK set.
-                        */
-                       if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
-                               res = hext_stream;
-                               break;
-                       }
+       sdai = swidget->private;
 
-                       if (hstream->opened) {
-                               /*
-                                * check if the stream tag matches the stream
-                                * tag of one of the connected FEs
-                                */
-                               if (hda_check_fes(rtd, stream_dir,
-                                                 hstream->stream_tag)) {
-                                       res = hext_stream;
-                                       break;
-                               }
-                       } else {
-                               res = hext_stream;
-
-                               /*
-                                * This must be a hostless stream.
-                                * So reserve the host DMA channel.
-                                */
-                               hda_stream->host_reserved = 1;
-                               break;
-                       }
-               }
-       }
+       /* select and set the DAI widget ops if not set already */
+       if (!sdai->platform_private) {
+               const struct hda_dai_widget_dma_ops *ops =
+                       hda_select_dai_widget_ops(sdev, swidget);
+               if (!ops)
+                       return NULL;
 
-       if (res) {
-               /* Make sure that host and link DMA is decoupled. */
-               snd_hdac_ext_stream_decouple_locked(bus, res, true);
+               /* check if mandatory ops are set */
+               if (!ops || !ops->get_hext_stream)
+                       return NULL;
 
-               res->link_locked = 1;
-               res->link_substream = substream;
+               sdai->platform_private = ops;
        }
-       spin_unlock_irq(&bus->reg_lock);
 
-       return res;
+       return sdai->platform_private;
 }
 
 static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
                                struct hdac_ext_stream *hext_stream,
                                struct snd_soc_dai *cpu_dai,
-                               struct snd_soc_dai *codec_dai,
-                               bool trigger_suspend_stop)
+                               struct snd_soc_dai *codec_dai)
 {
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
        struct hdac_stream *hstream = &hext_stream->hstream;
        struct hdac_bus *bus = hstream->bus;
        struct sof_intel_hda_stream *hda_stream;
@@ -150,15 +108,14 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
        if (!hlink)
                return -EINVAL;
 
-       if (trigger_suspend_stop)
-               snd_hdac_ext_stream_clear(hext_stream);
-
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                stream_tag = hdac_stream(hext_stream)->stream_tag;
                snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
        }
-       snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
-       snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+
+       if (ops->release_hext_stream)
+               ops->release_hext_stream(sdev, cpu_dai, substream);
+
        hext_stream->link_prepared = 0;
 
        /* free the host DMA channel reserved by hostless streams */
@@ -168,50 +125,20 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
-                              struct hda_pipe_params *params)
-{
-       struct hdac_stream *hstream = &hext_stream->hstream;
-       unsigned char stream_tag = hstream->stream_tag;
-       struct hdac_bus *bus = hstream->bus;
-       struct hdac_ext_link *hlink;
-       unsigned int format_val;
-
-       snd_hdac_ext_stream_reset(hext_stream);
-
-       format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
-                                                params->format,
-                                                params->link_bps, 0);
-
-       dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
-               format_val, params->s_freq, params->ch, params->format);
-
-       snd_hdac_ext_stream_setup(hext_stream, format_val);
-
-       if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               list_for_each_entry(hlink, &bus->hlink_list, list) {
-                       if (hlink->index == params->link_index)
-                               snd_hdac_ext_bus_link_set_stream_id(hlink,
-                                                                   stream_tag);
-               }
-       }
-
-       hext_stream->link_prepared = 1;
-
-       return 0;
-}
-
 static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *params)
+                                 struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
 {
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       struct hda_pipe_params p_params = {0};
        struct hdac_ext_stream *hext_stream;
+       struct hdac_stream *hstream;
        struct hdac_ext_link *hlink;
        struct snd_sof_dev *sdev;
        struct hdac_bus *bus;
+       unsigned int format_val;
+       unsigned int link_bps;
+       int stream_tag;
 
        sdev = snd_soc_component_get_drvdata(cpu_dai->component);
        bus = sof_to_bus(sdev);
@@ -220,337 +147,196 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
        if (!hlink)
                return -EINVAL;
 
-       hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
-       if (!hext_stream) {
-               hext_stream = hda_link_stream_assign(bus, substream);
-               if (!hext_stream)
-                       return -EBUSY;
+       hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
 
-               snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+       if (!hext_stream) {
+               if (ops->assign_hext_stream)
+                       hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
        }
 
+       if (!hext_stream)
+               return -EBUSY;
+
+       hstream = &hext_stream->hstream;
+       stream_tag = hstream->stream_tag;
+
+       if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
+
        /* set the hdac_stream in the codec dai */
        snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
 
-       p_params.ch = params_channels(params);
-       p_params.s_freq = params_rate(params);
-       p_params.link_index = hlink->index;
-       p_params.format = params_format(params);
-
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               p_params.link_bps = codec_dai->driver->playback.sig_bits;
+               link_bps = codec_dai->driver->playback.sig_bits;
        else
-               p_params.link_bps = codec_dai->driver->capture.sig_bits;
+               link_bps = codec_dai->driver->capture.sig_bits;
 
-       return hda_link_dma_params(hext_stream, &p_params);
-}
+       if (ops->reset_hext_stream)
+               ops->reset_hext_stream(sdev, hext_stream);
 
-static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       int stream = substream->stream;
+       format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
+                                                params_format(params), link_bps, 0);
 
-       return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
-}
+       dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+               params_rate(params), params_channels(params), params_format(params));
 
-static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
-       int ret;
+       if (ops->setup_hext_stream)
+               ops->setup_hext_stream(sdev, hext_stream, format_val);
 
-       if (!hext_stream)
-               return 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               snd_hdac_ext_stream_start(hext_stream);
-               break;
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-               ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, true);
-               if (ret < 0)
-                       return ret;
-
-               break;
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               snd_hdac_ext_stream_clear(hext_stream);
+       hext_stream->link_prepared = 1;
 
-               break;
-       default:
-               return -EINVAL;
-       }
        return 0;
 }
 
-static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
+static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       struct hdac_ext_stream *hext_stream;
-
-       hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
-       if (!hext_stream)
-               return 0;
-
-       return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
-}
-
-static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
-                                int channel, bool widget_setup)
-{
-       struct snd_sof_dai_config_data data;
-
-       data.dai_data = channel;
-
-       /* set up/free DAI widget and send DAI_CONFIG IPC */
-       if (widget_setup)
-               return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
+       int stream = substream->stream;
 
-       return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+       return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
 }
 
-static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
-                                   struct snd_pcm_hw_params *params,
-                                   struct snd_soc_dai *dai)
+static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
 {
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
        struct hdac_ext_stream *hext_stream;
-       struct snd_soc_dapm_widget *w;
-       int stream_tag;
 
-       hext_stream = snd_soc_dai_get_dma_data(dai, substream);
-       if (!hext_stream)
+       if (!ops) {
+               dev_err(sdev->dev, "DAI widget ops not set\n");
                return -EINVAL;
+       }
 
-       stream_tag = hdac_stream(hext_stream)->stream_tag;
-
-       w = snd_soc_dai_get_widget(dai, substream->stream);
+       hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+       if (!hext_stream)
+               return 0;
 
-       /* set up the DAI widget and send the DAI_CONFIG with the new tag */
-       return hda_dai_widget_update(w, stream_tag - 1, true);
+       return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
 }
 
 static int hda_dai_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params,
                             struct snd_soc_dai *dai)
 {
-       struct hdac_ext_stream *hext_stream =
-                               snd_soc_dai_get_dma_data(dai, substream);
+       struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+       struct hdac_ext_stream *hext_stream;
+       struct snd_sof_dai_config_data data = { 0 };
+       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
        int ret;
 
+       if (!ops) {
+               dev_err(sdev->dev, "DAI widget ops not set\n");
+               return -EINVAL;
+       }
+
+       hext_stream = ops->get_hext_stream(sdev, dai, substream);
        if (hext_stream && hext_stream->link_prepared)
                return 0;
 
-       ret = hda_link_dma_hw_params(substream, params);
+       ret = hda_link_dma_hw_params(substream, params, dai);
        if (ret < 0)
                return ret;
 
-       return hda_dai_hw_params_update(substream, params, dai);
-}
-
-
-static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
-{
-       struct snd_sof_widget *swidget = w->dobj.private;
-       struct snd_soc_component *component = swidget->scomp;
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
-       int ret = 0;
+       hext_stream = ops->get_hext_stream(sdev, dai, substream);
 
-       if (tplg_ops->dai_config) {
-               ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
-               if (ret < 0)
-                       dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
-                               w->name);
-       }
+       flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+       data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
 
-       return ret;
+       return hda_dai_config(w, flags, &data);
 }
 
 static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
 {
-       struct hdac_ext_stream *hext_stream =
-                               snd_soc_dai_get_dma_data(dai, substream);
+       struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       int stream = substream->stream;
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+       struct hdac_ext_stream *hext_stream;
+       struct snd_sof_dai_config_data data = { 0 };
+       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
        int ret;
 
+       hext_stream = ops->get_hext_stream(sdev, dai, substream);
        if (hext_stream && hext_stream->link_prepared)
                return 0;
 
        dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
 
-       ret = hda_link_dma_prepare(substream);
-       if (ret < 0)
-               return ret;
-
-       return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
-}
-
-static int hda_dai_hw_free_ipc(int stream, /* direction */
-                              struct snd_soc_dai *dai)
-{
-       struct snd_soc_dapm_widget *w;
-
-       w = snd_soc_dai_get_widget(dai, stream);
-
-       /* free the link DMA channel in the FW and the DAI widget */
-       return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
-}
-
-static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
-                               int cmd, struct snd_soc_dai *dai)
-{
-       struct snd_soc_dapm_widget *w;
-       int ret;
-
-       dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
-               dai->name, substream->stream);
-
-       ret = hda_link_dma_trigger(substream, cmd);
+       ret = hda_link_dma_prepare(substream, dai);
        if (ret < 0)
                return ret;
 
-       w = snd_soc_dai_get_widget(dai, substream->stream);
+       hext_stream = ops->get_hext_stream(sdev, dai, substream);
 
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-               /*
-                * free DAI widget during stop/suspend to keep widget use_count's balanced.
-                */
-               ret = hda_dai_hw_free_ipc(substream->stream, dai);
-               if (ret < 0)
-                       return ret;
-
-               break;
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               ret = hda_dai_config_pause_push_ipc(w);
-               if (ret < 0)
-                       return ret;
-               break;
+       flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+       data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
 
-       default:
-               break;
-       }
-       return 0;
+       return hda_dai_config(w, flags, &data);
 }
 
 /*
  * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
  * (over IPC channel) and DMA state change (direct host register changes).
  */
-static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
-                               int cmd, struct snd_soc_dai *dai)
+static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
 {
-       struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
-       struct snd_sof_widget *pipe_widget;
-       struct sof_ipc4_pipeline *pipeline;
+       const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+       struct hdac_ext_stream *hext_stream;
        struct snd_soc_pcm_runtime *rtd;
-       struct snd_sof_widget *swidget;
-       struct snd_soc_dapm_widget *w;
        struct snd_soc_dai *codec_dai;
-       struct snd_soc_dai *cpu_dai;
        int ret;
 
        dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
                dai->name, substream->stream);
 
+       hext_stream = ops->get_hext_stream(sdev, dai, substream);
+       if (!hext_stream)
+               return -EINVAL;
+
        rtd = asoc_substream_to_rtd(substream);
-       cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        codec_dai = asoc_rtd_to_codec(rtd, 0);
 
-       w = snd_soc_dai_get_widget(dai, substream->stream);
-       swidget = w->dobj.private;
-       pipe_widget = swidget->spipe->pipe_widget;
-       pipeline = pipe_widget->private;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               snd_hdac_ext_stream_start(hext_stream);
-               if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
-                       ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-                                                         SOF_IPC4_PIPE_PAUSED);
-                       if (ret < 0)
-                               return ret;
-                       pipeline->state = SOF_IPC4_PIPE_PAUSED;
-               }
-
-               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-                                                 SOF_IPC4_PIPE_RUNNING);
+       if (ops->pre_trigger) {
+               ret = ops->pre_trigger(sdev, dai, substream, cmd);
                if (ret < 0)
                        return ret;
-               pipeline->state = SOF_IPC4_PIPE_RUNNING;
-               break;
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-       {
-               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-                                                 SOF_IPC4_PIPE_PAUSED);
+       }
+
+       if (ops->trigger) {
+               ret = ops->trigger(sdev, dai, substream, cmd);
                if (ret < 0)
                        return ret;
+       }
 
-               pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
-               snd_hdac_ext_stream_clear(hext_stream);
-
-               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-                                                 SOF_IPC4_PIPE_RESET);
+       if (ops->post_trigger) {
+               ret = ops->post_trigger(sdev, dai, substream, cmd);
                if (ret < 0)
                        return ret;
+       }
 
-               pipeline->state = SOF_IPC4_PIPE_RESET;
-
-               ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
                if (ret < 0) {
                        dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
                        return ret;
                }
                break;
-       }
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-       {
-               ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-                                                 SOF_IPC4_PIPE_PAUSED);
-               if (ret < 0)
-                       return ret;
-
-               pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
-               snd_hdac_ext_stream_clear(hext_stream);
-               break;
-       }
        default:
-               dev_err(sdev->dev, "%s: unknown trigger command %d\n", __func__, cmd);
-               return -EINVAL;
+               break;
        }
 
        return 0;
 }
 
-static int hda_dai_hw_free(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       int ret;
-
-       ret = hda_link_dma_hw_free(substream);
-       if (ret < 0)
-               return ret;
-
-       return hda_dai_hw_free_ipc(substream->stream, dai);
-}
-
-static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
+static const struct snd_soc_dai_ops hda_dai_ops = {
        .hw_params = hda_dai_hw_params,
        .hw_free = hda_dai_hw_free,
-       .trigger = ipc3_hda_dai_trigger,
+       .trigger = hda_dai_trigger,
        .prepare = hda_dai_prepare,
 };
 
@@ -573,186 +359,62 @@ static int hda_dai_suspend(struct hdac_bus *bus)
                 * explicitly during suspend.
                 */
                if (hext_stream->link_substream) {
-                       struct snd_soc_dai *cpu_dai;
+                       const struct hda_dai_widget_dma_ops *ops;
+                       struct snd_sof_widget *swidget;
+                       struct snd_soc_dapm_widget *w;
                        struct snd_soc_dai *codec_dai;
+                       struct snd_soc_dai *cpu_dai;
+                       struct snd_sof_dev *sdev;
+                       struct snd_sof_dai *sdai;
 
                        rtd = asoc_substream_to_rtd(hext_stream->link_substream);
                        cpu_dai = asoc_rtd_to_cpu(rtd, 0);
                        codec_dai = asoc_rtd_to_codec(rtd, 0);
+                       w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
+                       swidget = w->dobj.private;
+                       sdev = snd_soc_component_get_drvdata(swidget->scomp);
+                       sdai = swidget->private;
+                       ops = sdai->platform_private;
 
                        ret = hda_link_dma_cleanup(hext_stream->link_substream,
                                                   hext_stream,
-                                                  cpu_dai, codec_dai, false);
+                                                  cpu_dai, codec_dai);
                        if (ret < 0)
                                return ret;
 
-                       /* for consistency with TRIGGER_SUSPEND we free DAI resources */
-                       ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
-                       if (ret < 0)
-                               return ret;
+                       /* for consistency with TRIGGER_SUSPEND  */
+                       if (ops->post_trigger) {
+                               ret = ops->post_trigger(sdev, cpu_dai,
+                                                       hext_stream->link_substream,
+                                                       SNDRV_PCM_TRIGGER_SUSPEND);
+                               if (ret < 0)
+                                       return ret;
+                       }
                }
        }
 
        return 0;
 }
 
-static const struct snd_soc_dai_ops ipc4_hda_dai_ops = {
-       .hw_params = hda_dai_hw_params,
-       .hw_free = hda_dai_hw_free,
-       .trigger = ipc4_hda_dai_trigger,
-       .prepare = hda_dai_prepare,
-};
-
 #endif
 
-/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
-struct ssp_dai_dma_data {
-       bool setup;
-};
-
-static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
-                                bool setup)
-{
-       struct snd_soc_dapm_widget *w;
-
-       w = snd_soc_dai_get_widget(dai, substream->stream);
-
-       if (setup)
-               return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-
-       return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-}
-
-static int ssp_dai_startup(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       struct ssp_dai_dma_data *dma_data;
-
-       dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
-       if (!dma_data)
-               return -ENOMEM;
-
-       snd_soc_dai_set_dma_data(dai, substream, dma_data);
-
-       return 0;
-}
-
-static int ssp_dai_setup(struct snd_pcm_substream *substream,
-                        struct snd_soc_dai *dai,
-                        bool setup)
-{
-       struct ssp_dai_dma_data *dma_data;
-       int ret = 0;
-
-       dma_data = snd_soc_dai_get_dma_data(dai, substream);
-       if (!dma_data) {
-               dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
-               return -EIO;
-       }
-
-       if (dma_data->setup != setup) {
-               ret = ssp_dai_setup_or_free(substream, dai, setup);
-               if (!ret)
-                       dma_data->setup = setup;
-       }
-       return ret;
-}
-
-static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *params,
-                            struct snd_soc_dai *dai)
-{
-       /* params are ignored for now */
-       return ssp_dai_setup(substream, dai, true);
-}
-
-static int ssp_dai_prepare(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       /*
-        * the SSP will only be reconfigured during resume operations and
-        * not in case of xruns
-        */
-       return ssp_dai_setup(substream, dai, true);
-}
-
-static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
-                               int cmd, struct snd_soc_dai *dai)
-{
-       if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
-               return 0;
-
-       return ssp_dai_setup(substream, dai, false);
-}
-
-static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       return ssp_dai_setup(substream, dai, false);
-}
-
-static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
-{
-       struct ssp_dai_dma_data *dma_data;
-
-       dma_data = snd_soc_dai_get_dma_data(dai, substream);
-       if (!dma_data) {
-               dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
-               return;
-       }
-       snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(dma_data);
-}
-
-static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
-       .startup = ssp_dai_startup,
-       .hw_params = ssp_dai_hw_params,
-       .prepare = ssp_dai_prepare,
-       .trigger = ipc3_ssp_dai_trigger,
-       .hw_free = ssp_dai_hw_free,
-       .shutdown = ssp_dai_shutdown,
-};
-
 void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
 {
        int i;
 
-       switch (sdev->pdata->ipc_type) {
-       case SOF_IPC:
-               for (i = 0; i < ops->num_drv; i++) {
-                       if (strstr(ops->drv[i].name, "SSP")) {
-                               ops->drv[i].ops = &ipc3_ssp_dai_ops;
-                               continue;
-                       }
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
-                       if (strstr(ops->drv[i].name, "iDisp") ||
-                           strstr(ops->drv[i].name, "Analog") ||
-                           strstr(ops->drv[i].name, "Digital"))
-                               ops->drv[i].ops = &ipc3_hda_dai_ops;
-#endif
-               }
-               break;
-       case SOF_INTEL_IPC4:
-       {
-               struct sof_ipc4_fw_data *ipc4_data = sdev->private;
-
-               for (i = 0; i < ops->num_drv; i++) {
+       for (i = 0; i < ops->num_drv; i++) {
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
-                       if (strstr(ops->drv[i].name, "iDisp") ||
-                           strstr(ops->drv[i].name, "Analog") ||
-                           strstr(ops->drv[i].name, "Digital"))
-                               ops->drv[i].ops = &ipc4_hda_dai_ops;
+               if (strstr(ops->drv[i].name, "iDisp") ||
+                   strstr(ops->drv[i].name, "Analog") ||
+                   strstr(ops->drv[i].name, "Digital"))
+                       ops->drv[i].ops = &hda_dai_ops;
 #endif
-               }
+       }
 
-               if (!hda_use_tplg_nhlt)
-                       ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
+       if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) {
+               struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 
-               break;
-       }
-       default:
-               break;
+               ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
        }
 }
 
index a6f2822401e03da1d3553f50fa4370955219f6e2..44f39a520bb39cc44e068231a817846c77b8b0a9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
 #include <trace/events/sof_intel.h>
 #include "../sof-audio.h"
 #include "../ops.h"
@@ -321,6 +322,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        const struct sof_intel_dsp_desc *chip = hda->desc;
 
+       if (sdev->dspless_mode_selected)
+               return;
+
        /* enable IPC DONE and BUSY interrupts */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
                        HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY,
@@ -336,6 +340,9 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        const struct sof_intel_dsp_desc *chip = hda->desc;
 
+       if (sdev->dspless_mode_selected)
+               return;
+
        /* disable IPC interrupt */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
                                HDA_DSP_ADSPIC_IPC, 0);
@@ -567,31 +574,11 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
  * is called again either because of a new IPC sent to the DSP or
  * during system suspend/resume.
  */
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
-                           const struct sof_dsp_power_state *target_state)
+static int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
+                                  const struct sof_dsp_power_state *target_state)
 {
        int ret = 0;
 
-       /*
-        * When the DSP is already in D0I3 and the target state is D0I3,
-        * it could be the case that the DSP is in D0I3 during S0
-        * and the system is suspending to S0Ix. Therefore,
-        * hda_dsp_set_D0_state() must be called to disable trace DMA
-        * by sending the PM_GATE IPC to the FW.
-        */
-       if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
-           sdev->system_suspend_target == SOF_SUSPEND_S0IX)
-               goto set_state;
-
-       /*
-        * For all other cases, return without doing anything if
-        * the DSP is already in the target state.
-        */
-       if (target_state->state == sdev->dsp_power_state.state &&
-           target_state->substate == sdev->dsp_power_state.substate)
-               return 0;
-
-set_state:
        switch (target_state->state) {
        case SOF_DSP_PM_D0:
                ret = hda_dsp_set_D0_state(sdev, target_state);
@@ -623,6 +610,42 @@ set_state:
        return ret;
 }
 
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+                                const struct sof_dsp_power_state *target_state)
+{
+       /*
+        * When the DSP is already in D0I3 and the target state is D0I3,
+        * it could be the case that the DSP is in D0I3 during S0
+        * and the system is suspending to S0Ix. Therefore,
+        * hda_dsp_set_D0_state() must be called to disable trace DMA
+        * by sending the PM_GATE IPC to the FW.
+        */
+       if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
+           sdev->system_suspend_target == SOF_SUSPEND_S0IX)
+               return hda_dsp_set_power_state(sdev, target_state);
+
+       /*
+        * For all other cases, return without doing anything if
+        * the DSP is already in the target state.
+        */
+       if (target_state->state == sdev->dsp_power_state.state &&
+           target_state->substate == sdev->dsp_power_state.substate)
+               return 0;
+
+       return hda_dsp_set_power_state(sdev, target_state);
+}
+
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+                                const struct sof_dsp_power_state *target_state)
+{
+       /* Return without doing anything if the DSP is already in the target state */
+       if (target_state->state == sdev->dsp_power_state.state &&
+           target_state->substate == sdev->dsp_power_state.substate)
+               return 0;
+
+       return hda_dsp_set_power_state(sdev, target_state);
+}
+
 /*
  * Audio DSP states may transform as below:-
  *
@@ -681,6 +704,9 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
        /* power down all hda links */
        hda_bus_ml_suspend(bus);
 
+       if (sdev->dspless_mode_selected)
+               goto skip_dsp;
+
        ret = chip->power_down_dsp(sdev);
        if (ret < 0) {
                dev_err(sdev->dev, "failed to power down DSP during suspend\n");
@@ -694,6 +720,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
        /* disable ppcap interrupt */
        hda_dsp_ctrl_ppcap_enable(sdev, false);
        hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+skip_dsp:
 
        /* disable hda bus irq and streams */
        hda_dsp_ctrl_stop_chip(sdev);
@@ -744,9 +771,11 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
                        hda_codec_jack_check(sdev);
        }
 
-       /* enable ppcap interrupt */
-       hda_dsp_ctrl_ppcap_enable(sdev, true);
-       hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+       if (!sdev->dspless_mode_selected) {
+               /* enable ppcap interrupt */
+               hda_dsp_ctrl_ppcap_enable(sdev, true);
+               hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+       }
 
 cleanup:
        /* display codec can powered off after controller init */
@@ -788,7 +817,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev)
                }
 
                /* restore L1SEN bit */
-               if (hda->l1_support_changed)
+               if (hda->l1_disabled)
                        snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
                                                HDA_VS_INTEL_EM2,
                                                HDA_VS_INTEL_EM2_L1SEN, 0);
@@ -843,8 +872,10 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
        };
        int ret;
 
-       /* cancel any attempt for DSP D0I3 */
-       cancel_delayed_work_sync(&hda->d0i3_work);
+       if (!sdev->dspless_mode_selected) {
+               /* cancel any attempt for DSP D0I3 */
+               cancel_delayed_work_sync(&hda->d0i3_work);
+       }
 
        /* stop hda controller and power dsp off */
        ret = hda_suspend(sdev, true);
@@ -866,8 +897,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
        };
        int ret;
 
-       /* cancel any attempt for DSP D0I3 */
-       cancel_delayed_work_sync(&hda->d0i3_work);
+       if (!sdev->dspless_mode_selected) {
+               /* cancel any attempt for DSP D0I3 */
+               cancel_delayed_work_sync(&hda->d0i3_work);
+       }
 
        if (target_state == SOF_DSP_PM_D0) {
                /* Set DSP power state */
@@ -880,11 +913,9 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
                }
 
                /* enable L1SEN to make sure the system can enter S0Ix */
-               hda->l1_support_changed =
-                       snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
-                                               HDA_VS_INTEL_EM2,
-                                               HDA_VS_INTEL_EM2_L1SEN,
-                                               HDA_VS_INTEL_EM2_L1SEN);
+               if (hda->l1_disabled)
+                       snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+                                               HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
 
                /* stop the CORB/RIRB DMA if it is On */
                hda_codec_suspend_cmd_io(sdev);
index df541b22b2d2b82e530b6f4e5b74f13ce725f005..a838dddb1d327f84cb81a6fe1ee8a457f6462219 100644 (file)
@@ -355,6 +355,9 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
        bool ret = false;
        u32 irq_status;
 
+       if (sdev->dspless_mode_selected)
+               return false;
+
        /* store status */
        irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
        trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);
index d680562edb3555aca8d92566b60ed0e34792c997..50ce6b190002e2d8589185065b8b90dac67be2e1 100644 (file)
@@ -321,13 +321,13 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
 int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
 {
        struct hdac_ext_stream *iccmax_stream;
-       struct hdac_bus *bus = sof_to_bus(sdev);
        struct snd_dma_buffer dmab_bdl;
        int ret, ret1;
        u8 original_gb;
 
        /* save the original LTRP guardband value */
-       original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK;
+       original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) &
+               HDA_VS_INTEL_LTRP_GB_MASK;
 
        /*
         * Prepare capture stream for ICCMAX. We do not need to store
@@ -356,7 +356,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
        }
 
        /* restore the original guardband value after FW boot */
-       snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
+       snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
+                           HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
 
        return ret;
 }
@@ -556,7 +557,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
                goto cleanup;
        }
 
-       ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
 
        ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
        if (ret1 < 0) {
index 76ab9a2e7bb34a66ad048aee1ae1023ea111136e..775582ab7494187cca6f2ce76e9dcb24f921707a 100644 (file)
 
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
 
-#include <linux/acpi.h>
+#include <linux/bitfield.h>
 #include <linux/module.h>
-#include <linux/soundwire/sdw.h>
-#include <linux/soundwire/sdw_intel.h>
-#include <sound/intel-dsp-config.h>
-#include <sound/intel-nhlt.h>
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-#include "../sof-audio.h"
-#include "../sof-pci-dev.h"
-#include "../ops.h"
-#include "hda.h"
 
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
 
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus)
+/**
+ * struct hdac_ext2_link - HDAudio extended+alternate link
+ *
+ * @hext_link:         hdac_ext_link
+ * @alt:               flag set for alternate extended links
+ * @intc:              boolean for interrupt capable
+ * @ofls:              boolean for offload support
+ * @lss:               boolean for link synchronization capabilities
+ * @slcount:           sublink count
+ * @elid:              extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
+ * @elver:             extended link version
+ * @leptr:             extended link pointer
+ * @eml_lock:          mutual exclusion to access shared registers e.g. CPA/SPA bits
+ * in LCTL register
+ * @base_ptr:          pointer to shim/ip/shim_vs space
+ * @instance_offset:   offset between each of @slcount instances managed by link
+ * @shim_offset:       offset to SHIM register base
+ * @ip_offset:         offset to IP register base
+ * @shim_vs_offset:    offset to vendor-specific (VS) SHIM base
+ */
+struct hdac_ext2_link {
+       struct hdac_ext_link hext_link;
+
+       /* read directly from LCAP register */
+       bool alt;
+       bool intc;
+       bool ofls;
+       bool lss;
+       int slcount;
+       int elid;
+       int elver;
+       u32 leptr;
+
+       struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
+
+       /* internal values computed from LCAP contents */
+       void __iomem *base_ptr;
+       u32 instance_offset;
+       u32 shim_offset;
+       u32 ip_offset;
+       u32 shim_vs_offset;
+};
+
+#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
+
+#define AZX_REG_SDW_INSTANCE_OFFSET                    0x8000
+#define AZX_REG_SDW_SHIM_OFFSET                                0x0
+#define AZX_REG_SDW_IP_OFFSET                          0x100
+#define AZX_REG_SDW_VS_SHIM_OFFSET                     0x6000
+
+/* only one instance supported */
+#define AZX_REG_INTEL_DMIC_SHIM_OFFSET                 0x0
+#define AZX_REG_INTEL_DMIC_IP_OFFSET                   0x100
+#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET              0x6000
+
+#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET              0x1000
+#define AZX_REG_INTEL_SSP_SHIM_OFFSET                  0x0
+#define AZX_REG_INTEL_SSP_IP_OFFSET                    0x100
+#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET               0xC00
+
+/* only one instance supported */
+#define AZX_REG_INTEL_UAOL_SHIM_OFFSET                 0x0
+#define AZX_REG_INTEL_UAOL_IP_OFFSET                   0x100
+#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET              0xC00
+
+/* HDAML section - this part follows sequences in the hardware specification,
+ * including naming conventions and the use of the hdaml_ prefix.
+ * The code is intentionally minimal with limited dependencies on frameworks or
+ * helpers. Locking and scanning lists is handled at a higher level
+ */
+
+static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
+                         void __iomem *ml_addr, int link_idx)
+{
+       struct hdac_ext_link *hlink = &h2link->hext_link;
+       u32 base_offset;
+
+       hlink->lcaps  = readl(ml_addr + AZX_REG_ML_LCAP);
+
+       h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
+
+       /* handle alternate extensions */
+       if (!h2link->alt) {
+               h2link->slcount = 1;
+
+               /*
+                * LSDIID is initialized by hardware for HDaudio link,
+                * it needs to be setup by software for alternate links
+                */
+               hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
+
+               dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
+                       link_idx, hlink->lsdiid);
+
+               return 0;
+       }
+
+       h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
+       h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
+       h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
+
+       /* read slcount (increment due to zero-based hardware representation */
+       h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+       dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
+               link_idx, h2link->slcount);
+
+       /* find IP ID and offsets */
+       h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+
+       h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
+
+       base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
+       h2link->base_ptr = hlink->ml_addr + base_offset;
+
+       switch (h2link->elid) {
+       case AZX_REG_ML_LEPTR_ID_SDW:
+               h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
+               h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
+               h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
+               dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
+                       link_idx, base_offset);
+               break;
+       case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
+               h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
+               h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
+               h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
+               dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
+                       link_idx, base_offset);
+               break;
+       case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
+               h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
+               h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
+               h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
+               dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
+                       link_idx, base_offset);
+               break;
+       case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
+               h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
+               h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
+               h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
+               dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
+                       link_idx, base_offset);
+               break;
+       default:
+               dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
+                       link_idx, h2link->elid);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * Hardware recommendations are to wait ~10us before checking any hardware transition
+ * reported by bits changing status.
+ * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
+ * The worst-case is about 1ms before reporting an issue
+ */
+#define HDAML_POLL_DELAY_MIN_US 10
+#define HDAML_POLL_DELAY_SLACK_US 5
+#define HDAML_POLL_DELAY_RETRY  100
+
+static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
 {
-       if (bus->mlcap)
-               snd_hdac_ext_bus_get_ml_capabilities(bus);
+       int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
+       int retry = HDAML_POLL_DELAY_RETRY;
+       u32 val;
+
+       usleep_range(HDAML_POLL_DELAY_MIN_US,
+                    HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+       do {
+               val = readl(lctl);
+               if (enabled) {
+                       if (val & mask)
+                               return 0;
+               } else {
+                       if (!(val & mask))
+                               return 0;
+               }
+               usleep_range(HDAML_POLL_DELAY_MIN_US,
+                            HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+
+       } while (--retry);
+
+       return -EIO;
 }
 
-void hda_bus_ml_free(struct hdac_bus *bus)
+static int hdaml_link_init(u32 __iomem *lctl, int sublink)
+{
+       u32 val;
+       u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+
+       val = readl(lctl);
+       val |= mask;
+
+       writel(val, lctl);
+
+       return check_sublink_power(lctl, sublink, true);
+}
+
+static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
+{
+       u32 val;
+       u32 mask;
+
+       val = readl(lctl);
+       mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+       val &= ~mask;
+
+       writel(val, lctl);
+
+       return check_sublink_power(lctl, sublink, false);
+}
+
+static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
+{
+       u32 val;
+
+       val = readl(lctl);
+       if (enable)
+               val |= AZX_ML_LCTL_INTEN;
+       else
+               val &= ~AZX_ML_LCTL_INTEN;
+
+       writel(val, lctl);
+}
+
+static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
+{
+       u32 val;
+
+       val = readl(lctl);
+
+       return val & AZX_ML_LCTL_INTSTS;
+}
+
+static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
+{
+       int timeout = HDAML_POLL_DELAY_RETRY;
+       u32 reg_read;
+
+       do {
+               reg_read = readl(base + offset);
+               if ((reg_read & mask) == target)
+                       return 0;
+
+               timeout--;
+               usleep_range(HDAML_POLL_DELAY_MIN_US,
+                            HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+       } while (timeout != 0);
+
+       return -EAGAIN;
+}
+
+static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
+{
+       u32 val;
+
+       val = readl(lsync);
+       val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
+       val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
+
+       /*
+        * set SYNCPU but do not wait. The bit is cleared by hardware when
+        * the link becomes active.
+        */
+       val |= AZX_REG_ML_LSYNC_SYNCPU;
+
+       writel(val, lsync);
+}
+
+static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
+{
+       return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
+}
+
+static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
+{
+       u32 val;
+
+       val = readl(lsync);
+       val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
+
+       writel(val, lsync);
+}
+
+static void hdaml_link_sync_go(u32 __iomem *lsync)
 {
+       u32 val;
+
+       val = readl(lsync);
+       val |= AZX_REG_ML_LSYNC_SYNCGO;
+
+       writel(val, lsync);
+}
+
+static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
+{
+       u32 val;
+
+       val = readl(lsync);
+
+       return !!(val & cmdsync_mask);
+}
+
+static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
+{
+       u32 val;
+
+       val = readl(lsdiid);
+       val |= BIT(dev_num);
+
+       writel(val, lsdiid);
+}
+
+static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
+{
+       u32 val = readl(lctl);
+
+       if (enable)
+               val |=  AZX_ML_LCTL_OFLEN;
+       else
+               val &=  ~AZX_ML_LCTL_OFLEN;
+
+       writel(val, lctl);
+}
+
+/* END HDAML section */
+
+static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
+{
+       struct hdac_ext2_link *h2link;
        struct hdac_ext_link *hlink;
+       int ret;
+
+       h2link  = kzalloc(sizeof(*h2link), GFP_KERNEL);
+       if (!h2link)
+               return -ENOMEM;
+
+       /* basic initialization */
+       hlink = &h2link->hext_link;
+
+       hlink->index = index;
+       hlink->bus = bus;
+       hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
+
+       ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
+       if (ret < 0) {
+               kfree(h2link);
+               return ret;
+       }
+
+       mutex_init(&h2link->eml_lock);
+
+       list_add_tail(&hlink->list, &bus->hlink_list);
+
+       /*
+        * HDaudio regular links are powered-on by default, the
+        * refcount needs to be initialized.
+        */
+       if (!h2link->alt)
+               hlink->ref_count = 1;
+
+       return 0;
+}
+
+int hda_bus_ml_init(struct hdac_bus *bus)
+{
+       u32 link_count;
+       int ret;
+       int i;
+
+       if (!bus->mlcap)
+               return 0;
+
+       link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+       dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
+
+       for (i = 0; i < link_count; i++) {
+               ret = hda_ml_alloc_h2link(bus, i);
+               if (ret < 0) {
+                       hda_bus_ml_free(bus);
+                       return ret;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
+
+void hda_bus_ml_free(struct hdac_bus *bus)
+{
+       struct hdac_ext_link *hlink, *_h;
+       struct hdac_ext2_link *h2link;
 
        if (!bus->mlcap)
                return;
 
-       while (!list_empty(&bus->hlink_list)) {
-               hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+       list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
                list_del(&hlink->list);
-               kfree(hlink);
+               h2link = hdac_ext_link_to_ext2(hlink);
+
+               mutex_destroy(&h2link->eml_lock);
+               kfree(h2link);
+       }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
+
+static struct hdac_ext2_link *
+find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext_link *hlink;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+               if (h2link->alt == alt && h2link->elid == elid)
+                       return h2link;
        }
+
+       return NULL;
+}
+
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return 0;
+
+       return h2link->slcount;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return;
+
+       if (!h2link->intc)
+               return;
+
+       hlink = &h2link->hext_link;
+
+       mutex_lock(&h2link->eml_lock);
+
+       hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+       mutex_unlock(&h2link->eml_lock);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return false;
+
+       if (!h2link->intc)
+               return false;
+
+       hlink = &h2link->hext_link;
+
+       return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return 0;
+
+       if (!h2link->lss)
+               return 0;
+
+       hlink = &h2link->hext_link;
+
+       hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
+{
+       return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return 0;
+
+       if (!h2link->lss)
+               return 0;
+
+       hlink = &h2link->hext_link;
+
+       return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
+{
+       return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return;
+
+       if (!h2link->lss)
+               return;
+
+       hlink = &h2link->hext_link;
+
+       hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
+{
+       hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return 0;
+
+       if (!h2link->lss)
+               return 0;
+
+       hlink = &h2link->hext_link;
+
+       hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
+{
+       return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+       u32 cmdsync_mask;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return 0;
+
+       if (!h2link->lss)
+               return 0;
+
+       hlink = &h2link->hext_link;
+
+       cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
+                              AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
+
+       return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
+                                       cmdsync_mask);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
+{
+       return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+                                     bool eml_lock)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+       int ret = 0;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return -ENODEV;
+
+       if (sublink >= h2link->slcount)
+               return -EINVAL;
+
+       hlink = &h2link->hext_link;
+
+       if (eml_lock)
+               mutex_lock(&h2link->eml_lock);
+
+       if (++hlink->ref_count > 1)
+               goto skip_init;
+
+       ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_init:
+       if (eml_lock)
+               mutex_unlock(&h2link->eml_lock);
+
+       return ret;
+}
+
+int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
 }
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+                                       bool eml_lock)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+       int ret = 0;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return -ENODEV;
+
+       if (sublink >= h2link->slcount)
+               return -EINVAL;
+
+       hlink = &h2link->hext_link;
+
+       if (eml_lock)
+               mutex_lock(&h2link->eml_lock);
+
+       if (--hlink->ref_count > 0)
+               goto skip_shutdown;
+
+       ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_shutdown:
+       if (eml_lock)
+               mutex_unlock(&h2link->eml_lock);
+
+       return ret;
+}
+
+int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+       return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
+{
+       return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
+{
+       return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+       if (!h2link)
+               return -ENODEV;
+
+       hlink = &h2link->hext_link;
+
+       mutex_lock(&h2link->eml_lock);
+
+       hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
+
+       mutex_unlock(&h2link->eml_lock);
+
+       return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
 
 void hda_bus_ml_put_all(struct hdac_bus *bus)
 {
        struct hdac_ext_link *hlink;
 
-       list_for_each_entry(hlink, &bus->hlink_list, list)
-               snd_hdac_ext_bus_link_put(bus, hlink);
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+               if (!h2link->alt)
+                       snd_hdac_ext_bus_link_put(bus, hlink);
+       }
 }
+EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
 
 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
 {
@@ -64,6 +761,7 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
        list_for_each_entry(hlink, &bus->hlink_list, list)
                writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
 }
+EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
 
 int hda_bus_ml_resume(struct hdac_bus *bus)
 {
@@ -72,7 +770,9 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
 
        /* power up links that were active before suspend */
        list_for_each_entry(hlink, &bus->hlink_list, list) {
-               if (hlink->ref_count) {
+               struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+               if (!h2link->alt && hlink->ref_count) {
                        ret = snd_hdac_ext_bus_link_power_up(hlink);
                        if (ret < 0)
                                return ret;
@@ -80,10 +780,86 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
        }
        return 0;
 }
+EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
 
 int hda_bus_ml_suspend(struct hdac_bus *bus)
 {
-       return snd_hdac_ext_bus_link_power_down_all(bus);
+       struct hdac_ext_link *hlink;
+       int ret;
+
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+               if (!h2link->alt) {
+                       ret = snd_hdac_ext_bus_link_power_down(hlink);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
+
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
+{
+       struct hdac_ext2_link *h2link;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return NULL;
+
+       return &h2link->eml_lock;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
+{
+       struct hdac_ext2_link *h2link;
+
+       h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+       if (!h2link)
+               return NULL;
+
+       return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
+{
+       struct hdac_ext2_link *h2link;
+
+       h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+       if (!h2link)
+               return NULL;
+
+       return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+       struct hdac_ext2_link *h2link;
+       struct hdac_ext_link *hlink;
+
+       h2link = find_ext2_link(bus, alt, elid);
+       if (!h2link)
+               return -ENODEV;
+
+       if (!h2link->ofls)
+               return 0;
+
+       hlink = &h2link->hext_link;
+
+       mutex_lock(&h2link->eml_lock);
+
+       hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+       mutex_unlock(&h2link->eml_lock);
+
+       return 0;
 }
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
 
 #endif
+
+MODULE_LICENSE("Dual BSD/GPL");
index dc0b359ed9b6cc386f1bd452b60d4242cff02fc8..981e7b699bdbe5b797b85ce63fbc9430f3863d76 100644 (file)
@@ -101,18 +101,23 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        struct snd_dma_buffer *dmab;
        int ret;
-       u32 size, rate, bits;
-
-       size = params_buffer_bytes(params);
-       rate = hda_dsp_get_mult_div(sdev, params_rate(params));
-       bits = hda_dsp_get_bits(sdev, params_width(params));
 
        hstream->substream = substream;
 
        dmab = substream->runtime->dma_buffer_p;
 
-       hstream->format_val = rate | bits | (params_channels(params) - 1);
-       hstream->bufsize = size;
+       /*
+        * Use the codec required format val (which is link_bps adjusted) when
+        * the DSP is not in use
+        */
+       if (!sdev->dspless_mode_selected) {
+               u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params));
+               u32 bits = hda_dsp_get_bits(sdev, params_width(params));
+
+               hstream->format_val = rate | bits | (params_channels(params) - 1);
+       }
+
+       hstream->bufsize = params_buffer_bytes(params);
        hstream->period_bytes = params_period_bytes(params);
        hstream->no_period_wakeup  =
                        (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
@@ -249,6 +254,11 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
        snd_pcm_hw_constraint_integer(substream->runtime,
                                      SNDRV_PCM_HW_PARAM_PERIODS);
 
+       /* Only S16 and S32 supported by HDA hardware when used without DSP */
+       if (sdev->dspless_mode_selected)
+               snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+                                            SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);
+
        /* binding pcm substream to hda stream */
        substream->runtime->private_data = &dsp_stream->hstream;
        return 0;
index 7f0fd05a96e6b165d23bd47d226b936574ae952a..8de422604ad5ea95253ecaeeba914365d92d7c10 100644 (file)
@@ -182,6 +182,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
 struct hdac_ext_stream *
 hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
 {
+       const struct sof_intel_dsp_desc *chip_info =  get_chip_info(sdev->pdata);
+       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        struct hdac_bus *bus = sof_to_bus(sdev);
        struct sof_intel_hda_stream *hda_stream;
        struct hdac_ext_stream *hext_stream = NULL;
@@ -220,12 +222,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
        /*
         * Prevent DMI Link L1 entry for streams that don't support it.
         * Workaround to address a known issue with host DMA that results
-        * in xruns during pause/release in capture scenarios.
+        * in xruns during pause/release in capture scenarios. This is not needed for the ACE IP.
         */
-       if (!(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE))
+       if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 &&
+           !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
                snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
                                        HDA_VS_INTEL_EM2,
                                        HDA_VS_INTEL_EM2_L1SEN, 0);
+               hda->l1_disabled = true;
+       }
 
        return hext_stream;
 }
@@ -233,6 +238,8 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
 /* free a stream */
 int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
 {
+       const struct sof_intel_dsp_desc *chip_info =  get_chip_info(sdev->pdata);
+       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        struct hdac_bus *bus = sof_to_bus(sdev);
        struct sof_intel_hda_stream *hda_stream;
        struct hdac_ext_stream *hext_stream;
@@ -264,9 +271,11 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
        spin_unlock_irq(&bus->reg_lock);
 
        /* Enable DMI L1 if permitted */
-       if (dmi_l1_enable)
+       if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && dmi_l1_enable) {
                snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
                                        HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
+               hda->l1_disabled = false;
+       }
 
        if (!found) {
                dev_err(sdev->dev, "%s: stream_tag %d not opened!\n",
@@ -328,7 +337,13 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
        /* cmd must be for audio stream */
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (!sdev->dspless_mode_selected)
+                       break;
+               fallthrough;
        case SNDRV_PCM_TRIGGER_START:
+               if (hstream->running)
+                       break;
+
                snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
                                        1 << hstream->index,
                                        1 << hstream->index);
@@ -351,8 +366,11 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
                        hstream->running = true;
 
                break;
-       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (!sdev->dspless_mode_selected)
+                       break;
+               fallthrough;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
                snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
                                        sd_offset,
@@ -476,9 +494,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
 {
        const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
        struct hdac_bus *bus = sof_to_bus(sdev);
-       struct hdac_stream *hstream = &hext_stream->hstream;
-       int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
-       int ret;
+       struct hdac_stream *hstream;
+       int sd_offset, ret;
        u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
        u32 mask;
        u32 run;
@@ -493,10 +510,14 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
                return -ENODEV;
        }
 
-       /* decouple host and link DMA */
-       mask = 0x1 << hstream->index;
-       snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
-                               mask, mask);
+       hstream = &hext_stream->hstream;
+       sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+       mask = BIT(hstream->index);
+
+       /* decouple host and link DMA if the DSP is used */
+       if (!sdev->dspless_mode_selected)
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+                                       mask, mask);
 
        /* clear stream status */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
@@ -597,11 +618,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
         *    enable decoupled mode
         */
 
-       if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+       if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
                /* couple host and link DMA, disable DSP features */
                snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
                                        mask, 0);
-       }
 
        /* program stream format */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -609,11 +629,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
                                SOF_HDA_ADSP_REG_SD_FORMAT,
                                0xffff, hstream->format_val);
 
-       if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+       if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
                /* decouple host and link DMA, enable DSP features */
                snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
                                        mask, mask);
-       }
 
        /* program last valid index */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -666,20 +685,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
        struct hdac_ext_stream *hext_stream = container_of(hstream,
                                                         struct hdac_ext_stream,
                                                         hstream);
-       struct hdac_bus *bus = sof_to_bus(sdev);
-       u32 mask = 0x1 << hstream->index;
        int ret;
 
        ret = hda_dsp_stream_reset(sdev, hstream);
        if (ret < 0)
                return ret;
 
-       spin_lock_irq(&bus->reg_lock);
-       /* couple host and link DMA if link DMA channel is idle */
-       if (!hext_stream->link_locked)
-               snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
-                                       SOF_HDA_REG_PP_PPCTL, mask, 0);
-       spin_unlock_irq(&bus->reg_lock);
+       if (!sdev->dspless_mode_selected) {
+               struct hdac_bus *bus = sof_to_bus(sdev);
+               u32 mask = BIT(hstream->index);
+
+               spin_lock_irq(&bus->reg_lock);
+               /* couple host and link DMA if link DMA channel is idle */
+               if (!hext_stream->link_locked)
+                       snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
+                                               SOF_HDA_REG_PP_PPCTL, mask, 0);
+               spin_unlock_irq(&bus->reg_lock);
+       }
 
        hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
 
@@ -861,12 +883,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
 
                hext_stream = &hda_stream->hext_stream;
 
-               hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
-                       SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+               if (sdev->bar[HDA_DSP_PP_BAR]) {
+                       hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+                               SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
 
-               hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
-                       SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
-                       SOF_HDA_PPLC_INTERVAL * i;
+                       hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+                               SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+                               SOF_HDA_PPLC_INTERVAL * i;
+               }
 
                hstream = &hext_stream->hstream;
 
@@ -917,13 +941,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
 
                hext_stream = &hda_stream->hext_stream;
 
-               /* we always have DSP support */
-               hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
-                       SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+               if (sdev->bar[HDA_DSP_PP_BAR]) {
+                       hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+                               SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
 
-               hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
-                       SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
-                       SOF_HDA_PPLC_INTERVAL * i;
+                       hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+                               SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+                               SOF_HDA_PPLC_INTERVAL * i;
+               }
 
                hstream = &hext_stream->hstream;
 
index 81c697e201085975c6ad4b3600c3c064607a75bd..3153e21f100ab59687d775759342ca2ac1b842eb 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/intel-nhlt.h>
 #include <sound/sof.h>
 #include <sound/sof/xtensa.h>
+#include <sound/hda-mlink.h>
 #include "../sof-audio.h"
 #include "../sof-pci-dev.h"
 #include "../ops.h"
 #define EXCEPT_MAX_HDR_SIZE    0x400
 #define HDA_EXT_ROM_STATUS_SIZE 8
 
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
-                             struct snd_sof_dai_config_data *data)
+static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
 {
-       struct snd_sof_widget *swidget = w->dobj.private;
-       struct snd_soc_component *component = swidget->scomp;
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
-       struct snd_sof_dai *sof_dai = swidget->private;
-       int ret;
-
-       if (!sof_dai) {
-               dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name);
-               return -EINVAL;
-       }
-
-       if (tplg_ops->dai_config) {
-               unsigned int flags;
-
-               /* set HW_PARAMS flag along with quirks */
-               flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
-                       quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
-
-               ret = tplg_ops->dai_config(sdev, swidget, flags, data);
-               if (ret < 0) {
-                       dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
-                               w->name);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
-                            struct snd_sof_dai_config_data *data)
-{
-       struct snd_sof_widget *swidget = w->dobj.private;
-       struct snd_soc_component *component = swidget->scomp;
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
-       struct snd_sof_dai *sof_dai = swidget->private;
-
-       if (!sof_dai) {
-               dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name);
-               return -EINVAL;
-       }
-
-       if (tplg_ops->dai_config) {
-               unsigned int flags;
-               int ret;
-
-               /* set HW_FREE flag along with any quirks */
-               flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
-                       quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+       const struct sof_intel_dsp_desc *chip;
+       u32 interface_mask[2] = { 0 };
 
-               ret = tplg_ops->dai_config(sdev, swidget, flags, data);
-               if (ret < 0)
-                       dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__,
-                               w->name);
+       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);
+               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);
+               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);
+               break;
+       default:
+               break;
        }
 
-       return 0;
+       return interface_mask[sdev->dspless_mode_selected];
 }
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
@@ -124,35 +94,17 @@ static int sdw_params_stream(struct device *dev,
                             struct sdw_intel_stream_params_data *params_data)
 {
        struct snd_soc_dai *d = params_data->dai;
-       struct snd_sof_dai_config_data data;
-       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->stream);
+       struct snd_sof_dai_config_data data = { 0 };
 
-       w = snd_soc_dai_get_widget(d, params_data->stream);
        data.dai_index = (params_data->link_id << 8) | d->id;
        data.dai_data = params_data->alh_stream_id;
 
-       return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
-}
-
-static int sdw_free_stream(struct device *dev,
-                          struct sdw_intel_stream_free_data *free_data)
-{
-       struct snd_soc_dai *d = free_data->dai;
-       struct snd_sof_dai_config_data data;
-       struct snd_soc_dapm_widget *w;
-
-       w = snd_soc_dai_get_widget(d, free_data->stream);
-       data.dai_index = (free_data->link_id << 8) | d->id;
-
-       /* send invalid stream_id */
-       data.dai_data = 0xFFFF;
-
-       return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+       return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
 }
 
 struct sdw_intel_ops sdw_callback = {
        .params_stream = sdw_params_stream,
-       .free_stream = sdw_free_stream,
 };
 
 void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
@@ -171,8 +123,12 @@ void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
 
 void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        const struct sof_intel_dsp_desc *chip;
 
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               return;
+
        chip = get_chip_info(sdev->pdata);
        if (chip && chip->enable_sdw_irq)
                chip->enable_sdw_irq(sdev, enable);
@@ -180,10 +136,14 @@ void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
 
 static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        struct sof_intel_hda_dev *hdev;
        acpi_handle handle;
        int ret;
 
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               return -EINVAL;
+
        handle = ACPI_HANDLE(sdev->dev);
 
        /* save ACPI info for the probe step */
@@ -254,8 +214,8 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
        /* Check HW supported vs property value */
        if (caps < ctx->count) {
                dev_err(sdev->dev,
-                       "BIOS master count %d is larger than hardware capabilities %d\n",
-                       ctx->count, caps);
+                       "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+                       __func__, ctx->count, caps);
                return -EINVAL;
        }
 
@@ -337,8 +297,12 @@ out:
 
 static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        const struct sof_intel_dsp_desc *chip;
 
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               return false;
+
        chip = get_chip_info(sdev->pdata);
        if (chip && chip->check_sdw_irq)
                return chip->check_sdw_irq(sdev);
@@ -353,8 +317,12 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
 
 static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        struct sof_intel_hda_dev *hdev;
 
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               return false;
+
        hdev = sdev->pdata->hw_pdata;
        if (hdev->sdw &&
            snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@@ -366,8 +334,12 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
 
 void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        struct sof_intel_hda_dev *hdev;
 
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               return;
+
        hdev = sdev->pdata->hw_pdata;
        if (!hdev->sdw)
                return;
@@ -927,6 +899,7 @@ static int dmic_detect_topology_fixup(struct snd_sof_dev *sdev,
 
 static int hda_init_caps(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(sdev);
        struct hdac_bus *bus = sof_to_bus(sdev);
        struct snd_sof_pdata *pdata = sdev->pdata;
        struct sof_intel_hda_dev *hdev = pdata->hw_pdata;
@@ -945,7 +918,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
                return ret;
        }
 
-       hda_bus_ml_get_capabilities(bus);
+       hda_bus_ml_init(bus);
+
+       /* Skip SoundWire if it is not supported */
+       if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+               goto skip_soundwire;
 
        /* scan SoundWire capabilities exposed by DSDT */
        ret = hda_sdw_acpi_scan(sdev);
@@ -1054,21 +1031,25 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
        const struct sof_intel_dsp_desc *chip;
        int ret = 0;
 
-       /*
-        * detect DSP by checking class/subclass/prog-id information
-        * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
-        * class=04 subclass 01 prog-if 00: DSP is present
-        *   (and may be required e.g. for DMIC or SSP support)
-        * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
-        */
-       if (pci->class == 0x040300) {
-               dev_err(sdev->dev, "error: the DSP is not enabled on this platform, aborting probe\n");
-               return -ENODEV;
-       } else if (pci->class != 0x040100 && pci->class != 0x040380) {
-               dev_err(sdev->dev, "error: unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n", pci->class);
-               return -ENODEV;
+       if (!sdev->dspless_mode_selected) {
+               /*
+                * detect DSP by checking class/subclass/prog-id information
+                * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
+                * class=04 subclass 01 prog-if 00: DSP is present
+                *   (and may be required e.g. for DMIC or SSP support)
+                * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
+                */
+               if (pci->class == 0x040300) {
+                       dev_err(sdev->dev, "the DSP is not enabled on this platform, aborting probe\n");
+                       return -ENODEV;
+               } else if (pci->class != 0x040100 && pci->class != 0x040380) {
+                       dev_err(sdev->dev, "unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n",
+                               pci->class);
+                       return -ENODEV;
+               }
+               dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n",
+                        pci->class);
        }
-       dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", pci->class);
 
        chip = get_chip_info(sdev->pdata);
        if (!chip) {
@@ -1104,12 +1085,18 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
        hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0;
 #endif
 
+       if (sdev->dspless_mode_selected)
+               hdev->no_ipc_position = 1;
+
        /* set up HDA base */
        bus = sof_to_bus(sdev);
        ret = hda_init(sdev);
        if (ret < 0)
                goto hdac_bus_unmap;
 
+       if (sdev->dspless_mode_selected)
+               goto skip_dsp_setup;
+
        /* DSP base */
        sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
        if (!sdev->bar[HDA_DSP_BAR]) {
@@ -1120,6 +1107,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
 
        sdev->mmio_bar = HDA_DSP_BAR;
        sdev->mailbox_bar = HDA_DSP_BAR;
+skip_dsp_setup:
 
        /* allow 64bit DMA address if supported by H/W */
        if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(64))) {
@@ -1185,14 +1173,16 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
        if (ret < 0)
                goto free_ipc_irq;
 
-       /* enable ppcap interrupt */
-       hda_dsp_ctrl_ppcap_enable(sdev, true);
-       hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+       if (!sdev->dspless_mode_selected) {
+               /* enable ppcap interrupt */
+               hda_dsp_ctrl_ppcap_enable(sdev, true);
+               hda_dsp_ctrl_ppcap_int_enable(sdev, true);
 
-       /* set default mailbox offset for FW ready message */
-       sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
+               /* set default mailbox offset for FW ready message */
+               sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
 
-       INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+               INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+       }
 
        init_waitqueue_head(&hdev->waitq);
 
@@ -1208,7 +1198,8 @@ free_irq_vector:
 free_streams:
        hda_dsp_stream_free(sdev);
 /* dsp_unmap: not currently used */
-       iounmap(sdev->bar[HDA_DSP_BAR]);
+       if (!sdev->dspless_mode_selected)
+               iounmap(sdev->bar[HDA_DSP_BAR]);
 hdac_bus_unmap:
        platform_device_unregister(hdev->dmic_dev);
        iounmap(bus->remap_addr);
@@ -1228,8 +1219,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
        if (nhlt)
                intel_nhlt_free(nhlt);
 
-       /* cancel any attempt for DSP D0I3 */
-       cancel_delayed_work_sync(&hda->d0i3_work);
+       if (!sdev->dspless_mode_selected)
+               /* cancel any attempt for DSP D0I3 */
+               cancel_delayed_work_sync(&hda->d0i3_work);
 
        hda_codec_device_remove(sdev);
 
@@ -1238,14 +1230,19 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
        if (!IS_ERR_OR_NULL(hda->dmic_dev))
                platform_device_unregister(hda->dmic_dev);
 
-       /* disable DSP IRQ */
-       snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
-                               SOF_HDA_PPCTL_PIE, 0);
+       if (!sdev->dspless_mode_selected) {
+               /* disable DSP IRQ */
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+                                       SOF_HDA_PPCTL_PIE, 0);
+       }
 
        /* disable CIE and GIE interrupts */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
                                SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0);
 
+       if (sdev->dspless_mode_selected)
+               goto skip_disable_dsp;
+
        /* no need to check for error as the DSP will be disabled anyway */
        if (chip && chip->power_down_dsp)
                chip->power_down_dsp(sdev);
@@ -1254,6 +1251,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
        snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
                                SOF_HDA_PPCTL_GPROCEN, 0);
 
+skip_disable_dsp:
        free_irq(sdev->ipc_irq, sdev);
        if (sdev->msi_enabled)
                pci_free_irq_vectors(pci);
@@ -1262,7 +1260,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
 
        hda_bus_ml_free(sof_to_bus(sdev));
 
-       iounmap(sdev->bar[HDA_DSP_BAR]);
+       if (!sdev->dspless_mode_selected)
+               iounmap(sdev->bar[HDA_DSP_BAR]);
+
        iounmap(bus->remap_addr);
 
        sof_hda_bus_exit(sdev);
@@ -1568,12 +1568,16 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
 
 struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
 {
+       u32 interface_mask = hda_get_interface_mask(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;
        const char *tplg_filename;
 
-       mach = snd_soc_acpi_find_machine(desc->machines);
+       /* Try I2S or DMIC if it is supported */
+       if (interface_mask & (BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC)))
+               mach = snd_soc_acpi_find_machine(desc->machines);
+
        if (mach) {
                bool add_extension = false;
                bool tplg_fixup = false;
@@ -1680,10 +1684,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
                }
        }
 
-       /*
-        * If I2S fails, try SoundWire
-        */
-       if (!mach)
+       /* If I2S fails, try SoundWire if it is supported */
+       if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
                mach = hda_sdw_machine_select(sdev);
 
        /*
@@ -1729,3 +1731,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
 MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
 MODULE_IMPORT_NS(SOUNDWIRE_INTEL);
+MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK);
index 45f9d4248f14e9ba47d4f122e2ff658766de19f3..c4befacde23e435fa054b73e46b184a64e1776eb 100644 (file)
@@ -502,7 +502,7 @@ struct sof_intel_hda_dev {
        u32 stream_max;
 
        /* PM related */
-       bool l1_support_changed;/* during suspend, is L1SEN changed or not */
+       bool l1_disabled;/* is DMI link L1 disabled? */
 
        /* DMIC device */
        struct platform_device *dmic_dev;
@@ -584,8 +584,10 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
 void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
 bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask);
 
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
-                           const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+                                const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+                                const struct sof_dsp_power_state *target_state);
 
 int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state);
 int hda_dsp_resume(struct snd_sof_dev *sdev);
@@ -763,26 +765,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
 
 #endif
 
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus);
-void hda_bus_ml_free(struct hdac_bus *bus);
-void hda_bus_ml_put_all(struct hdac_bus *bus);
-void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
-int hda_bus_ml_resume(struct hdac_bus *bus);
-int hda_bus_ml_suspend(struct hdac_bus *bus);
-
-#else
-
-static inline void hda_bus_ml_get_capabilities(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
-static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
-static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
-
-#endif /* CONFIG_SND_SOC_SOF_HDA */
-
 /*
  * Trace Control.
  */
@@ -896,10 +878,6 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 
 struct snd_sof_dai;
 struct sof_ipc_dai_config;
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
-                             struct snd_sof_dai_config_data *data);
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
-                            struct snd_sof_dai_config_data *data);
 
 #define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY      (0) /* previous implementation */
 #define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS      (1) /* recommended if VC0 only */
@@ -928,4 +906,41 @@ extern struct sdw_intel_ops sdw_callback;
 struct sof_ipc4_fw_library;
 int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
                              struct sof_ipc4_fw_library *fw_lib, bool reload);
+
+/**
+ * struct hda_dai_widget_dma_ops - DAI DMA ops optional by default unless specified otherwise
+ * @get_hext_stream: Mandatory function pointer to get the saved pointer to struct hdac_ext_stream
+ * @assign_hext_stream: Function pointer to assign a hdac_ext_stream
+ * @release_hext_stream: Function pointer to release the hdac_ext_stream
+ * @setup_hext_stream: Function pointer for hdac_ext_stream setup
+ * @reset_hext_stream: Function pointer for hdac_ext_stream reset
+ * @pre_trigger: Function pointer for DAI DMA pre-trigger actions
+ * @trigger: Function pointer for DAI DMA trigger actions
+ * @post_trigger: Function pointer for DAI DMA post-trigger actions
+ */
+struct hda_dai_widget_dma_ops {
+       struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev,
+                                                  struct snd_soc_dai *cpu_dai,
+                                                  struct snd_pcm_substream *substream);
+       struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev,
+                                                     struct snd_soc_dai *cpu_dai,
+                                                     struct snd_pcm_substream *substream);
+       void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                                   struct snd_pcm_substream *substream);
+       void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+                                 unsigned int format_val);
+       void (*reset_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_sream);
+       int (*pre_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                          struct snd_pcm_substream *substream, int cmd);
+       int (*trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                      struct snd_pcm_substream *substream, int cmd);
+       int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+                           struct snd_pcm_substream *substream, int cmd);
+};
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+                  struct snd_sof_dai_config_data *data);
+
 #endif
index 435941a1692fba5957d05d0c96f9fd0dddfb26ba..0f249efc6a5a28de4d2ed2dcce980ed907becab3 100644 (file)
@@ -116,6 +116,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_icl_ops.ipc_dump    = cnl_ipc_dump;
+
+               sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
        }
 
        if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -141,6 +143,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_icl_ops.ipc_dump    = cnl_ipc4_dump;
+
+               sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
        }
 
        /* debug */
index 307faad2ecf4b994c6cef1fb4cf59bdbf00678d3..46caf3ccde66e13d4130a85110168d2087e79935 100644 (file)
@@ -60,6 +60,9 @@ static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
        u32 irq_status;
        u32 hfintipptr;
 
+       if (sdev->dspless_mode_selected)
+               return false;
+
        /* read Interrupt IP Pointer */
        hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
        irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, hfintipptr + MTL_DSP_IRQSTS);
@@ -120,6 +123,9 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        const struct sof_intel_dsp_desc *chip = hda->desc;
 
+       if (sdev->dspless_mode_selected)
+               return;
+
        /* enable IPC DONE and BUSY interrupts */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
                                MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE,
@@ -131,6 +137,9 @@ static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev)
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        const struct sof_intel_dsp_desc *chip = hda->desc;
 
+       if (sdev->dspless_mode_selected)
+               return;
+
        /* disable IPC DONE and BUSY interrupts */
        snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
                                MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE, 0);
@@ -143,6 +152,9 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
        u32 val;
        int ret;
 
+       if (sdev->dspless_mode_selected)
+               return;
+
        /* Enable/Disable SoundWire interrupt */
        mask = MTL_DSP_REG_HfSNDWIE_IE_MASK;
        if (enable)
@@ -170,6 +182,9 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
        u32 val;
        int ret;
 
+       if (sdev->dspless_mode_selected)
+               return 0;
+
        /* read Interrupt IP Pointer */
        hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
 
@@ -217,6 +232,7 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
 /* pre fw run operations */
 static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
 {
+       struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
        u32 dsphfpwrsts;
        u32 dsphfdsscs;
        u32 cpa;
@@ -255,9 +271,11 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
        if (ret < 0)
                dev_err(sdev->dev, "failed to power up gated DSP domain\n");
 
-       /* make sure SoundWire is not power-gated */
-       snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, MTL_HFPWRCTL,
-                               MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+       /* if SoundWire is used, make sure it is not power-gated */
+       if (hdev->info.handle && hdev->info.link_mask > 0)
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFPWRCTL,
+                                       MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+
        return ret;
 }
 
@@ -650,6 +668,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
        /* set DAI ops */
        hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
 
+       sof_mtl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+
        return 0;
 };
 EXPORT_SYMBOL_NS(sof_mtl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
index aff6cb573c270f9a4cf387c400a6c85c94757031..69cad5a6bc724e9ae16859b53a3837424136d89a 100644 (file)
@@ -29,6 +29,7 @@ static const struct sof_dev_desc bxt_desc = {
        .chip_info = &apl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/apl",
@@ -60,6 +61,7 @@ static const struct sof_dev_desc glk_desc = {
        .chip_info = &apl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/glk",
index 4c0c1c369dcd8a8ffb1fec3d9de2360730e9f0f2..8895508a0be6d7dbc6285b85a5f78972c58e0565 100644 (file)
@@ -30,6 +30,7 @@ static const struct sof_dev_desc cnl_desc = {
        .chip_info = &cnl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc cfl_desc = {
        .chip_info = &cnl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -94,6 +96,7 @@ static const struct sof_dev_desc cml_desc = {
        .chip_info = &cnl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/cnl",
index 6785669113b3c2ee74f4d0315b4f3053a61ad6e8..5fb5a820693e4ada842993f7bfb3a6df4e54c75a 100644 (file)
@@ -30,6 +30,7 @@ static const struct sof_dev_desc icl_desc = {
        .chip_info = &icl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/icl",
@@ -61,6 +62,7 @@ static const struct sof_dev_desc jsl_desc = {
        .chip_info = &jsl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/jsl",
index b183dc0014b4b5a102b57c63e0a787bc76b55d01..e276e1e37fed8ee616fe400fee5ea3fb1ab0bb36 100644 (file)
@@ -31,6 +31,7 @@ static const struct sof_dev_desc mtl_desc = {
        .chip_info = &mtl_chip_info,
        .ipc_supported_mask     = BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_INTEL_IPC4,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl",
        },
index 5b4bccf819658eeb356f2f58425c6ebb894f8436..5e69af6eed344b9ed84f7aceec086d08edc70433 100644 (file)
@@ -26,6 +26,7 @@ static struct sof_dev_desc skl_desc = {
        .irqindex_host_ipc      = -1,
        .ipc_supported_mask     = BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_INTEL_IPC4,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_INTEL_IPC4] = "intel/avs/skl",
        },
@@ -50,6 +51,7 @@ static struct sof_dev_desc kbl_desc = {
        .irqindex_host_ipc      = -1,
        .ipc_supported_mask     = BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_INTEL_IPC4,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_INTEL_IPC4] = "intel/avs/kbl",
        },
index 22e769e0831d9349331e646ed5fbbfab1debf68a..ca37ff1bbd2ae56713199d1bba221b8a1e6e82d1 100644 (file)
@@ -30,6 +30,7 @@ static const struct sof_dev_desc tgl_desc = {
        .chip_info = &tgl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/tgl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc tglh_desc = {
        .chip_info = &tglh_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/tgl-h",
@@ -93,6 +95,7 @@ static const struct sof_dev_desc ehl_desc = {
        .chip_info = &ehl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/ehl",
@@ -125,6 +128,7 @@ static const struct sof_dev_desc adls_desc = {
        .chip_info = &adls_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/adl-s",
@@ -157,6 +161,7 @@ static const struct sof_dev_desc adl_desc = {
        .chip_info = &tgl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/adl",
@@ -189,6 +194,7 @@ static const struct sof_dev_desc adl_n_desc = {
        .chip_info = &tgl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/adl-n",
@@ -221,6 +227,7 @@ static const struct sof_dev_desc rpls_desc = {
        .chip_info = &adls_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/rpl-s",
@@ -253,6 +260,7 @@ static const struct sof_dev_desc rpl_desc = {
        .chip_info = &tgl_chip_info,
        .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
        .ipc_default            = SOF_IPC,
+       .dspless_mode_supported = true,         /* Only supported for HDaudio */
        .default_fw_path = {
                [SOF_IPC] = "intel/sof",
                [SOF_INTEL_IPC4] = "intel/avs/rpl",
index 58ac3a46e6a7514c6144918ae587262fd7eb7a37..2713b7dc793124ccae5c95d8bb9540ae9b901804 100644 (file)
@@ -71,6 +71,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_tgl_ops.ipc_dump    = cnl_ipc_dump;
+
+               sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
        }
 
        if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -96,6 +98,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
 
                /* debug */
                sof_tgl_ops.ipc_dump    = cnl_ipc4_dump;
+
+               sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
        }
 
        /* set DAI driver ops */
index 217ac5501a9885b29bd369d965f63e527dfa4e75..ad040e7bb850ded6642320c19e047839a8cc7c21 100644 (file)
@@ -104,7 +104,7 @@ unlock:
        return ret;
 }
 
-static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
+static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol)
 {
        struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
        struct snd_soc_component *scomp = scontrol->scomp;
@@ -138,7 +138,7 @@ static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,
        unsigned int channels = scontrol->num_channels;
        unsigned int i;
 
-       snd_sof_refresh_control(scontrol);
+       sof_ipc3_refresh_control(scontrol);
 
        /* read back each channel */
        for (i = 0; i < channels; i++)
@@ -189,7 +189,7 @@ static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,
        unsigned int channels = scontrol->num_channels;
        unsigned int i;
 
-       snd_sof_refresh_control(scontrol);
+       sof_ipc3_refresh_control(scontrol);
 
        /* read back each channel */
        for (i = 0; i < channels; i++)
@@ -237,7 +237,7 @@ static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,
        unsigned int channels = scontrol->num_channels;
        unsigned int i;
 
-       snd_sof_refresh_control(scontrol);
+       sof_ipc3_refresh_control(scontrol);
 
        /* read back each channel */
        for (i = 0; i < channels; i++)
@@ -286,7 +286,7 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
        struct sof_abi_hdr *data = cdata->data;
        size_t size;
 
-       snd_sof_refresh_control(scontrol);
+       sof_ipc3_refresh_control(scontrol);
 
        if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
                dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
@@ -343,55 +343,6 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
        return 0;
 }
 
-static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
-                                 const unsigned int __user *binary_data, unsigned int size)
-{
-       struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
-       struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
-       struct snd_soc_component *scomp = scontrol->scomp;
-       struct snd_ctl_tlv header;
-       size_t data_size;
-
-       snd_sof_refresh_control(scontrol);
-
-       /*
-        * Decrement the limit by ext bytes header size to
-        * ensure the user space buffer is not exceeded.
-        */
-       if (size < sizeof(struct snd_ctl_tlv))
-               return -ENOSPC;
-
-       size -= sizeof(struct snd_ctl_tlv);
-
-       /* set the ABI header values */
-       cdata->data->magic = SOF_ABI_MAGIC;
-       cdata->data->abi = SOF_ABI_VERSION;
-
-       /* check data size doesn't exceed max coming from topology */
-       if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
-               dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
-                                   cdata->data->size,
-                                   scontrol->max_size - sizeof(struct sof_abi_hdr));
-               return -EINVAL;
-       }
-
-       data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
-
-       /* make sure we don't exceed size provided by user space for data */
-       if (data_size > size)
-               return -ENOSPC;
-
-       header.numid = cdata->cmd;
-       header.length = data_size;
-       if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
-               return -EFAULT;
-
-       if (copy_to_user(tlvd->tlv, cdata->data, data_size))
-               return -EFAULT;
-
-       return 0;
-}
-
 static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
                                  const unsigned int __user *binary_data,
                                  unsigned int size)
@@ -457,16 +408,15 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
        return 0;
 }
 
-static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
-                                          const unsigned int __user *binary_data,
-                                          unsigned int size)
+static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
+                                  const unsigned int __user *binary_data,
+                                  unsigned int size, bool from_dsp)
 {
        struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
        struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
        struct snd_soc_component *scomp = scontrol->scomp;
        struct snd_ctl_tlv header;
        size_t data_size;
-       int ret;
 
        /*
         * Decrement the limit by ext bytes header size to
@@ -482,9 +432,12 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
        cdata->data->abi = SOF_ABI_VERSION;
 
        /* get all the component data from DSP */
-       ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
-       if (ret < 0)
-               return ret;
+       if (from_dsp) {
+               int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
+
+               if (ret < 0)
+                       return ret;
+       }
 
        /* check data size doesn't exceed max coming from topology */
        if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
@@ -508,7 +461,20 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
        if (copy_to_user(tlvd->tlv, cdata->data, data_size))
                return -EFAULT;
 
-       return ret;
+       return 0;
+}
+
+static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
+                                 const unsigned int __user *binary_data, unsigned int size)
+{
+       return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, false);
+}
+
+static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
+                                          const unsigned int __user *binary_data,
+                                          unsigned int size)
+{
+       return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, true);
 }
 
 static void snd_sof_update_control(struct snd_sof_control *scontrol,
index b815b0244d9e43b82d32cf238a0e194c557c447d..1d3bca2d28dd61d1b2261da5c7ac460e9f9caa1e 100644 (file)
@@ -150,7 +150,6 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
                                    struct sof_ipc_trace_filter_elem *elems)
 {
        struct sof_ipc_trace_filter *msg;
-       struct sof_ipc_reply reply;
        size_t size;
        int ret;
 
@@ -172,13 +171,13 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
                dev_err(sdev->dev, "enabling device failed: %d\n", ret);
                goto error;
        }
-       ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
        pm_runtime_mark_last_busy(sdev->dev);
        pm_runtime_put_autosuspend(sdev->dev);
 
 error:
        kfree(msg);
-       return ret ? ret : reply.error;
+       return ret;
 }
 
 static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
@@ -434,7 +433,6 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
        struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
        struct sof_ipc_fw_version *v = &ready->version;
        struct sof_ipc_dma_trace_params_ext params;
-       struct sof_ipc_reply ipc_reply;
        int ret;
 
        if (!sdev->fw_trace_is_supported)
@@ -474,7 +472,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
 
        /* send IPC to the DSP */
        priv->dtrace_state = SOF_DTRACE_INITIALIZING;
-       ret = sof_ipc_tx_message(sdev->ipc, &params, sizeof(params), &ipc_reply, sizeof(ipc_reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &params, sizeof(params));
        if (ret < 0) {
                dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
                goto trace_release;
@@ -604,7 +602,6 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
        struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
        struct sof_ipc_fw_version *v = &ready->version;
        struct sof_ipc_cmd_hdr hdr;
-       struct sof_ipc_reply ipc_reply;
        int ret;
 
        if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
@@ -623,8 +620,7 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
                hdr.size = sizeof(hdr);
                hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
 
-               ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
-                                        &ipc_reply, sizeof(ipc_reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, &hdr, hdr.size);
                if (ret < 0)
                        dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
        }
index b29d93e0d2164bd0501272917f8df960390e885d..304faf6425abc16da9c7c7d28479a69d21113d60 100644 (file)
@@ -19,7 +19,6 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct sof_ipc_stream stream;
-       struct sof_ipc_reply reply;
        struct snd_sof_pcm *spcm;
 
        spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -34,7 +33,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
        stream.comp_id = spcm->stream[substream->stream].comp_id;
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
 }
 
 static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
@@ -146,7 +145,6 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
        struct sof_ipc_stream stream;
-       struct sof_ipc_reply reply;
        struct snd_sof_pcm *spcm;
 
        spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -178,7 +176,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
        }
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
 }
 
 static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
@@ -382,4 +380,5 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
        .hw_free = sof_ipc3_pcm_hw_free,
        .trigger = sof_ipc3_pcm_trigger,
        .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
+       .reset_hw_params_during_stop = true,
 };
index b1f425b39db94fd54b95bd6e9f3a6bc25538cf58..fc1eb8e2de2c1d9e19f3a86522e2af3f3c162a8d 100644 (file)
@@ -1627,7 +1627,6 @@ static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget)
 static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
 {
        struct sof_ipc_pipe_comp_connect connect;
-       struct sof_ipc_reply reply;
        int ret;
 
        connect.hdr.size = sizeof(connect);
@@ -1640,7 +1639,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
                sroute->sink_widget->widget->name);
 
        /* send ipc */
-       ret = sof_ipc_tx_message(sdev->ipc, &connect, sizeof(connect), &reply, sizeof(reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect));
        if (ret < 0)
                dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,
                        sroute->src_widget->widget->name, sroute->sink_widget->widget->name);
@@ -1789,7 +1788,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro
        fcomp.id = scontrol->comp_id;
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, &fcomp, sizeof(fcomp), NULL, 0);
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &fcomp, sizeof(fcomp));
 }
 
 /* send pcm params ipc */
@@ -1797,7 +1796,6 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
 {
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-       struct sof_ipc_pcm_params_reply ipc_params_reply;
        struct snd_pcm_hw_params *params;
        struct sof_ipc_pcm_params pcm;
        struct snd_sof_pcm *spcm;
@@ -1841,8 +1839,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
        }
 
        /* send IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
-                                &ipc_params_reply, sizeof(ipc_params_reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm));
        if (ret < 0)
                dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
                        swidget->widget->name);
@@ -1856,7 +1853,6 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc_stream stream;
-       struct sof_ipc_reply reply;
        int ret;
 
        /* set IPC stream params */
@@ -1865,7 +1861,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
        stream.comp_id = swidget->comp_id;
 
        /* send IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
        if (ret < 0)
                dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
 
@@ -1982,7 +1978,6 @@ static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp,
 static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
        struct sof_ipc_pipe_ready ready;
-       struct sof_ipc_reply reply;
        int ret;
 
        dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
@@ -1993,7 +1988,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w
        ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
        ready.comp_id = swidget->comp_id;
 
-       ret = sof_ipc_tx_message(sdev->ipc, &ready, sizeof(ready), &reply, sizeof(reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ready, sizeof(ready));
        if (ret < 0)
                return ret;
 
@@ -2009,7 +2004,6 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
                },
                .id = swidget->comp_id,
        };
-       struct sof_ipc_reply reply;
        int ret;
 
        if (!swidget->private)
@@ -2029,8 +2023,7 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
                break;
        }
 
-       ret = sof_ipc_tx_message(sdev->ipc, &ipc_free, sizeof(ipc_free),
-                                &reply, sizeof(reply));
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free));
        if (ret < 0)
                dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);
 
@@ -2044,7 +2037,6 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
        struct snd_sof_dai *dai = swidget->private;
        struct sof_dai_private_data *private;
        struct sof_ipc_dai_config *config;
-       struct sof_ipc_reply reply;
        int ret = 0;
 
        if (!dai || !dai->private) {
@@ -2118,8 +2110,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
 
        /* only send the IPC if the widget is set up in the DSP */
        if (swidget->use_count > 0) {
-               ret = sof_ipc_tx_message(sdev->ipc, config, config->hdr.size,
-                                        &reply, sizeof(reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size);
                if (ret < 0)
                        dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);
 
@@ -2132,7 +2123,6 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
 
 static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
-       struct sof_ipc_comp_reply reply;
        int ret;
 
        if (!swidget->private)
@@ -2146,8 +2136,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_dai_private_data *dai_data = dai->private;
                struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;
 
-               ret = sof_ipc_tx_message(sdev->ipc, dai_data->comp_dai,
-                                        comp->hdr.size, &reply, sizeof(reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, dai_data->comp_dai, comp->hdr.size);
                break;
        }
        case snd_soc_dapm_scheduler:
@@ -2155,8 +2144,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_ipc_pipe_new *pipeline;
 
                pipeline = swidget->private;
-               ret = sof_ipc_tx_message(sdev->ipc, pipeline, sizeof(*pipeline),
-                                        &reply, sizeof(reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, pipeline, sizeof(*pipeline));
                break;
        }
        default:
@@ -2164,8 +2152,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_ipc_cmd_hdr *hdr;
 
                hdr = swidget->private;
-               ret = sof_ipc_tx_message(sdev->ipc, swidget->private, hdr->size,
-                                        &reply, sizeof(reply));
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, swidget->private, hdr->size);
                break;
        }
        }
index 4493bbd7faf12bbda0a2e9ed8d4f6c869f83b0c8..c677677420939984b7b03e9f4ec7b1516fa1b75c 100644 (file)
@@ -1044,15 +1044,13 @@ static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool
                .hdr.size = sizeof(core_cfg),
                .hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
        };
-       struct sof_ipc_reply reply;
 
        if (on)
                core_cfg.enable_mask = sdev->enabled_cores_mask | BIT(core_idx);
        else
                core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx);
 
-       return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg),
-                              &reply, sizeof(reply), false);
+       return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg), NULL, 0, false);
 }
 
 static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
@@ -1061,11 +1059,9 @@ static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
                .hdr.size = sizeof(pm_ctx),
                .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd,
        };
-       struct sof_ipc_reply reply;
 
        /* send ctx save ipc to dsp */
-       return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx),
-                              &reply, sizeof(reply), false);
+       return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx), NULL, 0, false);
 }
 
 static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
@@ -1081,7 +1077,6 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
 static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
 {
        struct sof_ipc_pm_gate pm_gate;
-       struct sof_ipc_reply reply;
 
        memset(&pm_gate, 0, sizeof(pm_gate));
 
@@ -1091,8 +1086,7 @@ static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
        pm_gate.flags = flags;
 
        /* send pm_gate ipc to dsp */
-       return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
-                                       &reply, sizeof(reply));
+       return sof_ipc_tx_message_no_pm_no_reply(sdev->ipc, &pm_gate, sizeof(pm_gate));
 }
 
 static const struct sof_ipc_pm_ops ipc3_pm_ops = {
index 9a71af1a613ac3800c9a5016de2a638cfac5d2d5..6f0698be9451f426f7c5faa134326e1c15f6ca98 100644 (file)
@@ -181,21 +181,263 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
        return 0;
 }
 
+static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
+                                      struct snd_sof_control *scontrol,
+                                      bool set, bool lock)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct sof_abi_hdr *data = cdata->data;
+       struct sof_ipc4_msg *msg = &cdata->msg;
+       int ret = 0;
+
+       /* Send the new data to the firmware only if it is powered up */
+       if (set && !pm_runtime_active(sdev->dev))
+               return 0;
+
+       msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+       msg->data_ptr = data->data;
+       msg->data_size = data->size;
+
+       ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
+       if (ret < 0)
+               dev_err(sdev->dev, "Failed to %s for %s\n",
+                       set ? "set bytes update" : "get bytes",
+                       scontrol->name);
+
+       msg->data_ptr = NULL;
+       msg->data_size = 0;
+
+       return ret;
+}
+
+static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct sof_abi_hdr *data = cdata->data;
+       size_t size;
+
+       if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+               dev_err_ratelimited(scomp->dev,
+                                   "data max %zu exceeds ucontrol data array size\n",
+                                   scontrol->max_size);
+               return -EINVAL;
+       }
+
+       /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
+       if (data->size > scontrol->max_size - sizeof(*data)) {
+               dev_err_ratelimited(scomp->dev,
+                                   "data size too big %u bytes max is %zu\n",
+                                   data->size, scontrol->max_size - sizeof(*data));
+               return -EINVAL;
+       }
+
+       size = data->size + sizeof(*data);
+
+       /* copy from kcontrol */
+       memcpy(data, ucontrol->value.bytes.data, size);
+
+       sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+
+       return 0;
+}
+
+static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct sof_abi_hdr *data = cdata->data;
+       size_t size;
+
+       if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+               dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
+                                   scontrol->max_size);
+               return -EINVAL;
+       }
+
+       if (data->size > scontrol->max_size - sizeof(*data)) {
+               dev_err_ratelimited(scomp->dev,
+                                   "%u bytes of control data is invalid, max is %zu\n",
+                                   data->size, scontrol->max_size - sizeof(*data));
+               return -EINVAL;
+       }
+
+       size = data->size + sizeof(*data);
+
+       /* copy back to kcontrol */
+       memcpy(ucontrol->value.bytes.data, data, size);
+
+       return 0;
+}
+
+static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
+                                 const unsigned int __user *binary_data,
+                                 unsigned int size)
+{
+       struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct sof_abi_hdr *data = cdata->data;
+       struct sof_abi_hdr abi_hdr;
+       struct snd_ctl_tlv header;
+
+       /*
+        * The beginning of bytes data contains a header from where
+        * the length (as bytes) is needed to know the correct copy
+        * length of data from tlvd->tlv.
+        */
+       if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))
+               return -EFAULT;
+
+       /* make sure TLV info is consistent */
+       if (header.length + sizeof(struct snd_ctl_tlv) > size) {
+               dev_err_ratelimited(scomp->dev,
+                                   "Inconsistent TLV, data %d + header %zu > %d\n",
+                                   header.length, sizeof(struct snd_ctl_tlv), size);
+               return -EINVAL;
+       }
+
+       /* be->max is coming from topology */
+       if (header.length > scontrol->max_size) {
+               dev_err_ratelimited(scomp->dev,
+                                   "Bytes data size %d exceeds max %zu\n",
+                                   header.length, scontrol->max_size);
+               return -EINVAL;
+       }
+
+       /* Verify the ABI header first */
+       if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr)))
+               return -EFAULT;
+
+       if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) {
+               dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n",
+                                   abi_hdr.magic);
+               return -EINVAL;
+       }
+
+       if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) {
+               dev_err_ratelimited(scomp->dev,
+                                   "%u bytes of control data is invalid, max is %zu\n",
+                                   abi_hdr.size, scontrol->max_size - sizeof(abi_hdr));
+               return -EINVAL;
+       }
+
+       /* Copy the whole binary data which includes the ABI header and the payload */
+       if (copy_from_user(data, tlvd->tlv, header.length))
+               return -EFAULT;
+
+       sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+
+       return 0;
+}
+
+static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
+                                  const unsigned int __user *binary_data,
+                                  unsigned int size, bool from_dsp)
+{
+       struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct sof_abi_hdr *data = cdata->data;
+       struct snd_ctl_tlv header;
+       size_t data_size;
+
+       /*
+        * Decrement the limit by ext bytes header size to ensure the user space
+        * buffer is not exceeded.
+        */
+       if (size < sizeof(struct snd_ctl_tlv))
+               return -ENOSPC;
+
+       size -= sizeof(struct snd_ctl_tlv);
+
+       /* get all the component data from DSP */
+       if (from_dsp) {
+               struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+               int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true);
+
+               if (ret < 0)
+                       return ret;
+
+               /* Set the ABI magic (if the control is not initialized) */
+               data->magic = SOF_IPC4_ABI_MAGIC;
+       }
+
+       if (data->size > scontrol->max_size - sizeof(*data)) {
+               dev_err_ratelimited(scomp->dev,
+                                   "%u bytes of control data is invalid, max is %zu\n",
+                                   data->size, scontrol->max_size - sizeof(*data));
+               return -EINVAL;
+       }
+
+       data_size = data->size + sizeof(struct sof_abi_hdr);
+
+       /* make sure we don't exceed size provided by user space for data */
+       if (data_size > size)
+               return -ENOSPC;
+
+       header.numid = scontrol->comp_id;
+       header.length = data_size;
+
+       if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
+               return -EFAULT;
+
+       if (copy_to_user(tlvd->tlv, data, data_size))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
+                                 const unsigned int __user *binary_data,
+                                 unsigned int size)
+{
+       return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
+}
+
+static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
+                                          const unsigned int __user *binary_data,
+                                          unsigned int size)
+{
+       return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true);
+}
+
 /* set up all controls for the widget */
 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
        struct snd_sof_control *scontrol;
-       int ret;
+       int ret = 0;
 
-       list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+       list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
                if (scontrol->comp_id == swidget->comp_id) {
-                       ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
+                       switch (scontrol->info_type) {
+                       case SND_SOC_TPLG_CTL_VOLSW:
+                       case SND_SOC_TPLG_CTL_VOLSW_SX:
+                       case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+                               ret = sof_ipc4_set_volume_data(sdev, swidget,
+                                                              scontrol, false);
+                               break;
+                       case SND_SOC_TPLG_CTL_BYTES:
+                               ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
+                                                                 true, false);
+                               break;
+                       default:
+                               break;
+                       }
+
                        if (ret < 0) {
-                               dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
-                                       __func__, scontrol->comp_id, swidget->widget->name);
+                               dev_err(sdev->dev,
+                                       "kcontrol %d set up failed for widget %s\n",
+                                       scontrol->comp_id, swidget->widget->name);
                                return ret;
                        }
                }
+       }
 
        return 0;
 }
@@ -225,6 +467,11 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I
 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
        .volume_put = sof_ipc4_volume_put,
        .volume_get = sof_ipc4_volume_get,
+       .bytes_put = sof_ipc4_bytes_put,
+       .bytes_get = sof_ipc4_bytes_get,
+       .bytes_ext_put = sof_ipc4_bytes_ext_put,
+       .bytes_ext_get = sof_ipc4_bytes_ext_get,
+       .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get,
        .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
        .set_up_volume_table = sof_ipc4_set_up_volume_table,
 };
index 0ec6ef681012d6ecd654d7cda8a4afa4ca80efc3..2b4659a1768e96154cbd730d0a066d50c776489e 100644 (file)
@@ -609,6 +609,16 @@ static void ipc4_mtrace_free(struct snd_sof_dev *sdev)
        ipc4_mtrace_disable(sdev);
 }
 
+static int sof_ipc4_mtrace_update_pos_all_cores(struct snd_sof_dev *sdev)
+{
+       int i;
+
+       for (i = 0; i < sdev->num_cores; i++)
+               sof_ipc4_mtrace_update_pos(sdev, i);
+
+       return 0;
+}
+
 int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core)
 {
        struct sof_mtrace_priv *priv = sdev->fw_trace_data;
@@ -642,6 +652,16 @@ int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core)
        return 0;
 }
 
+static void ipc4_mtrace_fw_crashed(struct snd_sof_dev *sdev)
+{
+       /*
+        * The DSP might not be able to send SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS
+        * messages anymore, so check the log buffer status on all
+        * cores and process any pending messages.
+        */
+       sof_ipc4_mtrace_update_pos_all_cores(sdev);
+}
+
 static int ipc4_mtrace_resume(struct snd_sof_dev *sdev)
 {
        return ipc4_mtrace_enable(sdev);
@@ -655,6 +675,7 @@ static void ipc4_mtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
 const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops = {
        .init = ipc4_mtrace_init,
        .free = ipc4_mtrace_free,
+       .fw_crashed = ipc4_mtrace_fw_crashed,
        .suspend = ipc4_mtrace_suspend,
        .resume = ipc4_mtrace_resume,
 };
index 68258767aace91feca9eb5caa724d3d5a163de3e..9e2b6c45080dd803eb87966e1efec413e0ca2334 100644 (file)
@@ -39,7 +39,7 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state
        msg.data_size = ipc_size;
        msg.data_ptr = trigger_list;
 
-       return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size);
 }
 
 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
@@ -57,7 +57,7 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
 
        msg.primary = primary;
 
-       return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
 }
 EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
 
@@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
  * prepare ioctl before the START trigger.
  */
 
+/*
+ * Chained DMA is a special case where there is no processing on
+ * DSP. The samples are just moved over by host side DMA to a single
+ * buffer on DSP and directly from there to link DMA. However, the
+ * model on SOF driver has two notional pipelines, one at host DAI,
+ * and another at link DAI. They both shall have the use_chain_dma
+ * attribute.
+ */
+
+static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
+                                     struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
+                                     int state, int cmd)
+{
+       bool allocate, enable, set_fifo_size;
+       struct sof_ipc4_msg msg = {{ 0 }};
+       int i;
+
+       switch (state) {
+       case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
+               allocate = true;
+               enable = true;
+               /*
+                * SOF assumes creation of a new stream from the presence of fifo_size
+                * in the message, so we must leave it out in pause release case.
+                */
+               if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+                       set_fifo_size = false;
+               else
+                       set_fifo_size = true;
+               break;
+       case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */
+               allocate = true;
+               enable = false;
+               set_fifo_size = false;
+               break;
+       case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
+               allocate = false;
+               enable = false;
+               set_fifo_size = false;
+               break;
+       default:
+               dev_err(sdev->dev, "Unexpected state %d", state);
+               return -EINVAL;
+       }
+
+       msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
+       msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+       msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+
+       /*
+        * To set-up the DMA chain, the host DMA ID and SCS setting
+        * are retrieved from the host pipeline configuration. Likewise
+        * the link DMA ID and fifo_size are retrieved from the link
+        * pipeline configuration.
+        */
+       for (i = 0; i < pipeline_list->count; i++) {
+               struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i];
+               struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+               struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+               if (!pipeline->use_chain_dma) {
+                       dev_err(sdev->dev,
+                               "All pipelines in chained DMA stream should have use_chain_dma attribute set.");
+                       return -EINVAL;
+               }
+
+               msg.primary |= pipeline->msg.primary;
+
+               /* Add fifo_size (actually DMA buffer size) field to the message */
+               if (set_fifo_size)
+                       msg.extension |= pipeline->msg.extension;
+       }
+
+       if (allocate)
+               msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
+
+       if (enable)
+               msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
+
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+}
+
 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
                                      struct snd_pcm_substream *substream, int state, int cmd)
 {
@@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
        struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
        struct sof_ipc4_fw_data *ipc4_data = sdev->private;
        struct ipc4_pipeline_set_state_data *trigger_list;
+       struct snd_sof_widget *pipe_widget;
+       struct sof_ipc4_pipeline *pipeline;
        struct snd_sof_pipeline *spipe;
        struct snd_sof_pcm *spcm;
        int ret;
@@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
        if (!pipeline_list->pipelines || !pipeline_list->count)
                return 0;
 
+       spipe = pipeline_list->pipelines[0];
+       pipe_widget = spipe->pipe_widget;
+       pipeline = pipe_widget->private;
+
+       /*
+        * If use_chain_dma attribute is set we proceed to chained DMA
+        * 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);
+
        /* allocate memory for the pipeline data */
        trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
                               GFP_KERNEL);
@@ -362,15 +457,70 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const
        }
 }
 
+/*
+ * Fixup DAI link parameters for sampling rate based on
+ * DAI copier configuration.
+ */
+static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev,
+                                           struct snd_pcm_hw_params *params,
+                                           struct sof_ipc4_copier *ipc4_copier)
+{
+       struct sof_ipc4_pin_format *pin_fmts = ipc4_copier->available_fmt.input_pin_fmts;
+       struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       int num_input_formats = ipc4_copier->available_fmt.num_input_formats;
+       unsigned int fe_rate = params_rate(params);
+       bool fe_be_rate_match = false;
+       bool single_be_rate = true;
+       unsigned int be_rate;
+       int i;
+
+       /*
+        * Copier does not change sampling rate, so we
+        * need to only consider the input pin information.
+        */
+       for (i = 0; i < num_input_formats; i++) {
+               unsigned int val = pin_fmts[i].audio_fmt.sampling_frequency;
+
+               if (i == 0)
+                       be_rate = val;
+               else if (val != be_rate)
+                       single_be_rate = false;
+
+               if (val == fe_rate) {
+                       fe_be_rate_match = true;
+                       break;
+               }
+       }
+
+       /*
+        * If rate is different than FE rate, topology must
+        * contain an SRC. But we do require topology to
+        * define a single rate in the DAI copier config in
+        * this case (FE rate may be variable).
+        */
+       if (!fe_be_rate_match) {
+               if (!single_be_rate) {
+                       dev_err(sdev->dev, "Unable to select sampling rate for DAI link\n");
+                       return -EINVAL;
+               }
+
+               rate->min = be_rate;
+               rate->max = rate->min;
+       }
+
+       return 0;
+}
+
 static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
                                       struct snd_pcm_hw_params *params)
 {
        struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
        struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name);
-       struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        struct sof_ipc4_copier *ipc4_copier;
+       bool use_chain_dma = false;
+       int dir;
 
        if (!dai) {
                dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -385,12 +535,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
                return -EINVAL;
        }
 
-       /* always set BE format to 32-bits for both playback and capture */
-       snd_mask_none(fmt);
-       snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+       for_each_pcm_streams(dir) {
+               struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir);
+
+               if (w) {
+                       struct snd_sof_widget *swidget = w->dobj.private;
+                       struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+                       struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+                       if (pipeline->use_chain_dma)
+                               use_chain_dma = true;
+               }
+       }
+
+       /* Chain DMA does not use copiers, so no fixup needed */
+       if (!use_chain_dma) {
+               int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier);
 
-       rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
-       rate->max = rate->min;
+               if (ret)
+                       return ret;
+       }
 
        switch (ipc4_copier->dai_type) {
        case SOF_DAI_INTEL_SSP:
@@ -671,5 +835,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
        .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
        .pcm_setup = sof_ipc4_pcm_setup,
        .pcm_free = sof_ipc4_pcm_free,
-       .delay = sof_ipc4_pcm_delay
+       .delay = sof_ipc4_pcm_delay,
+       .ipc_first_on_start = true,
+       .platform_stop_during_hw_free = true,
 };
index 3a5394c3dd83bf35053bd5e398de7f345bd1c7ba..059eebf0a68756dc6c99882e7dcd6f0a118daa69 100644 (file)
@@ -6,6 +6,7 @@
 // Copyright(c) 2022 Intel Corporation. All rights reserved.
 //
 //
+#include <linux/bitfield.h>
 #include <uapi/sound/sof/tokens.h>
 #include <sound/pcm_params.h>
 #include <sound/sof/ext_manifest4.h>
 
 #define SOF_IPC4_GAIN_PARAM_ID  0
 #define SOF_IPC4_TPLG_ABI_SIZE 6
+#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
 
 static DEFINE_IDA(alh_group_ida);
 static DEFINE_IDA(pipeline_ida);
 
 static const struct sof_topology_token ipc4_sched_tokens[] = {
        {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_pipeline, lp_mode)}
+               offsetof(struct sof_ipc4_pipeline, lp_mode)},
+       {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+               offsetof(struct sof_ipc4_pipeline, use_chain_dma)},
+       {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_pipeline, core_id)},
 };
 
 static const struct sof_topology_token pipeline_tokens[] = {
@@ -39,45 +45,48 @@ static const struct sof_topology_token ipc4_comp_tokens[] = {
                offsetof(struct sof_ipc4_base_module_cfg, is_pages)},
 };
 
-static const struct sof_topology_token ipc4_audio_format_buffer_size_tokens[] = {
-       {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_base_module_cfg, ibs)},
-       {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_base_module_cfg, obs)},
-};
-
 static const struct sof_topology_token ipc4_in_audio_format_tokens[] = {
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, bit_depth)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, ch_map)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, ch_cfg)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
-               get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
+               get_token_u32, offsetof(struct sof_ipc4_pin_format,
+               audio_fmt.interleaving_style)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
+       {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_pin_format, pin_index)},
+       {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_pin_format, buffer_size)},
 };
 
 static const struct sof_topology_token ipc4_out_audio_format_tokens[] = {
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, bit_depth)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, ch_map)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, ch_cfg)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
-               get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
+               get_token_u32, offsetof(struct sof_ipc4_pin_format,
+               audio_fmt.interleaving_style)},
        {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
+               offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
+       {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_pin_format, pin_index)},
+       {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_pin_format, buffer_size)},
 };
 
-static const struct sof_topology_token ipc4_copier_gateway_cfg_tokens[] = {
-       {SOF_TKN_CAVS_AUDIO_FORMAT_DMA_BUFFER_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
+static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = {
+       {SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
 };
 
 static const struct sof_topology_token ipc4_copier_tokens[] = {
@@ -85,8 +94,10 @@ static const struct sof_topology_token ipc4_copier_tokens[] = {
 };
 
 static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = {
-       {SOF_TKN_COMP_NUM_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               0},
+       {SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_available_audio_format, num_input_formats)},
+       {SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct sof_ipc4_available_audio_format, num_output_formats)},
 };
 
 static const struct sof_topology_token dai_tokens[] = {
@@ -100,6 +111,8 @@ static const struct sof_topology_token dai_tokens[] = {
 static const struct sof_topology_token comp_ext_tokens[] = {
        {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
                offsetof(struct snd_sof_widget, uuid)},
+       {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct snd_sof_widget, core)},
 };
 
 static const struct sof_topology_token gain_tokens[] = {
@@ -131,11 +144,8 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
                ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)},
        [SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens",
                ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)},
-       [SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS] = {"IPC4 Audio format buffer size tokens",
-               ipc4_audio_format_buffer_size_tokens,
-               ARRAY_SIZE(ipc4_audio_format_buffer_size_tokens)},
-       [SOF_COPIER_GATEWAY_CFG_TOKENS] = {"IPC4 Copier gateway config tokens",
-               ipc4_copier_gateway_cfg_tokens, ARRAY_SIZE(ipc4_copier_gateway_cfg_tokens)},
+       [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens",
+               ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)},
        [SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens,
                ARRAY_SIZE(ipc4_copier_tokens)},
        [SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens",
@@ -144,130 +154,150 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
        [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
 };
 
-static void sof_ipc4_dbg_audio_format(struct device *dev,
-                                     struct sof_ipc4_audio_format *format,
-                                     size_t object_size, int num_format)
+static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,
+                                     int num_formats)
 {
-       struct sof_ipc4_audio_format *fmt;
-       void *ptr = format;
        int i;
 
-       for (i = 0; i < num_format; i++, ptr = (u8 *)ptr + object_size) {
-               fmt = ptr;
+       for (i = 0; i < num_formats; i++) {
+               struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;
                dev_dbg(dev,
-                       " #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n",
-                       i, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
-                       fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg);
+                       "Pin index #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
+                       pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
+                       fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
+                       pin_fmt[i].buffer_size);
        }
 }
 
+static const struct sof_ipc4_audio_format *
+sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index)
+{
+       struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
+       struct sof_ipc4_process *process;
+       int i;
+
+       if (swidget->id != snd_soc_dapm_effect) {
+               struct sof_ipc4_base_module_cfg *base = swidget->private;
+
+               /* For non-process modules, base module config format is used for all input pins */
+               return &base->audio_fmt;
+       }
+
+       process = swidget->private;
+       base_cfg_ext = process->base_config_ext;
+
+       /*
+        * If there are multiple input formats available for a pin, the first available format
+        * is chosen.
+        */
+       for (i = 0; i < base_cfg_ext->num_input_pin_fmts; i++) {
+               struct sof_ipc4_pin_format *pin_format = &base_cfg_ext->pin_formats[i];
+
+               if (pin_format->pin_index == pin_index)
+                       return &pin_format->audio_fmt;
+       }
+
+       return NULL;
+}
+
 /**
  * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples
  * @scomp: pointer to pointer to SOC component
  * @swidget: pointer to struct snd_sof_widget containing tuples
  * @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in
- * @has_out_format: true if available_fmt contains output format
+ * @module_base_cfg: Pointer to the base_config in the module init IPC payload
  *
  * Return: 0 if successful
  */
 static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
                                  struct snd_sof_widget *swidget,
                                  struct sof_ipc4_available_audio_format *available_fmt,
-                                 bool has_out_format)
+                                 struct sof_ipc4_base_module_cfg *module_base_cfg)
 {
-       struct sof_ipc4_base_module_cfg *base_config;
-       struct sof_ipc4_audio_format *out_format;
-       int audio_fmt_num = 0;
-       int ret, i;
+       struct sof_ipc4_pin_format *in_format = NULL;
+       struct sof_ipc4_pin_format *out_format;
+       int ret;
 
-       ret = sof_update_ipc_object(scomp, &audio_fmt_num,
+       ret = sof_update_ipc_object(scomp, available_fmt,
                                    SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(audio_fmt_num), 1);
-       if (ret || audio_fmt_num <= 0) {
-               dev_err(scomp->dev, "Invalid number of audio formats: %d\n", audio_fmt_num);
-               return -EINVAL;
-       }
-       available_fmt->audio_fmt_num = audio_fmt_num;
-
-       dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num);
-
-       base_config = kcalloc(available_fmt->audio_fmt_num, sizeof(*base_config), GFP_KERNEL);
-       if (!base_config)
-               return -ENOMEM;
-
-       /* set cpc and is_pages for all base_cfg */
-       for (i = 0; i < available_fmt->audio_fmt_num; i++) {
-               ret = sof_update_ipc_object(scomp, &base_config[i],
-                                           SOF_COMP_TOKENS, swidget->tuples,
-                                           swidget->num_tuples, sizeof(*base_config), 1);
-               if (ret) {
-                       dev_err(scomp->dev, "parse comp tokens failed %d\n", ret);
-                       goto err_in;
-               }
+                                   swidget->num_tuples, sizeof(available_fmt), 1);
+       if (ret) {
+               dev_err(scomp->dev, "Failed to parse audio format token count\n");
+               return ret;
        }
 
-       /* copy the ibs/obs for each base_cfg */
-       ret = sof_update_ipc_object(scomp, base_config,
-                                   SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(*base_config),
-                                   available_fmt->audio_fmt_num);
-       if (ret) {
-               dev_err(scomp->dev, "parse buffer size tokens failed %d\n", ret);
-               goto err_in;
+       if (!available_fmt->num_input_formats && !available_fmt->num_output_formats) {
+               dev_err(scomp->dev, "No input/output pin formats set in topology\n");
+               return -EINVAL;
        }
 
-       for (i = 0; i < available_fmt->audio_fmt_num; i++)
-               dev_dbg(scomp->dev, "%d: ibs: %d obs: %d cpc: %d is_pages: %d\n", i,
-                       base_config[i].ibs, base_config[i].obs,
-                       base_config[i].cpc, base_config[i].is_pages);
+       dev_dbg(scomp->dev,
+               "Number of input audio formats: %d. Number of output audio formats: %d\n",
+               available_fmt->num_input_formats, available_fmt->num_output_formats);
 
-       ret = sof_update_ipc_object(scomp, &base_config->audio_fmt,
-                                   SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(*base_config),
-                                   available_fmt->audio_fmt_num);
+       /* set cpc and is_pages in the module's base_config */
+       ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples,
+                                   swidget->num_tuples, sizeof(*module_base_cfg), 1);
        if (ret) {
-               dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret);
-               goto err_in;
+               dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n",
+                       swidget->widget->name, ret);
+               return ret;
        }
 
-       dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name);
-       sof_ipc4_dbg_audio_format(scomp->dev, &base_config->audio_fmt,
-                                 sizeof(*base_config),
-                                 available_fmt->audio_fmt_num);
+       dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n",
+               swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages);
 
-       available_fmt->base_config = base_config;
+       if (available_fmt->num_input_formats) {
+               in_format = kcalloc(available_fmt->num_input_formats,
+                                   sizeof(*in_format), GFP_KERNEL);
+               if (!in_format)
+                       return -ENOMEM;
+               available_fmt->input_pin_fmts = in_format;
 
-       if (!has_out_format)
-               return 0;
+               ret = sof_update_ipc_object(scomp, in_format,
+                                           SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
+                                           swidget->num_tuples, sizeof(*in_format),
+                                           available_fmt->num_input_formats);
+               if (ret) {
+                       dev_err(scomp->dev, "parse input audio fmt tokens failed %d\n", ret);
+                       goto err_in;
+               }
 
-       out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL);
-       if (!out_format) {
-               ret = -ENOMEM;
-               goto err_in;
+               dev_dbg(scomp->dev, "Input audio formats for %s\n", swidget->widget->name);
+               sof_ipc4_dbg_audio_format(scomp->dev, in_format,
+                                         available_fmt->num_input_formats);
        }
 
-       ret = sof_update_ipc_object(scomp, out_format,
-                                   SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(*out_format),
-                                   available_fmt->audio_fmt_num);
+       if (available_fmt->num_output_formats) {
+               out_format = kcalloc(available_fmt->num_output_formats, sizeof(*out_format),
+                                    GFP_KERNEL);
+               if (!out_format) {
+                       ret = -ENOMEM;
+                       goto err_in;
+               }
 
-       if (ret) {
-               dev_err(scomp->dev, "parse output audio_fmt tokens failed\n");
-               goto err_out;
-       }
+               ret = sof_update_ipc_object(scomp, out_format,
+                                           SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
+                                           swidget->num_tuples, sizeof(*out_format),
+                                           available_fmt->num_output_formats);
+               if (ret) {
+                       dev_err(scomp->dev, "parse output audio fmt tokens failed\n");
+                       goto err_out;
+               }
 
-       available_fmt->out_audio_fmt = out_format;
-       dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name);
-       sof_ipc4_dbg_audio_format(scomp->dev, out_format, sizeof(*out_format),
-                                 available_fmt->audio_fmt_num);
+               available_fmt->output_pin_fmts = out_format;
+               dev_dbg(scomp->dev, "Output audio formats for %s\n", swidget->widget->name);
+               sof_ipc4_dbg_audio_format(scomp->dev, out_format,
+                                         available_fmt->num_output_formats);
+       }
 
        return 0;
 
 err_out:
        kfree(out_format);
 err_in:
-       kfree(base_config);
-
+       kfree(in_format);
+       available_fmt->input_pin_fmts = NULL;
        return ret;
 }
 
@@ -275,10 +305,10 @@ err_in:
 static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)
 
 {
-       kfree(available_fmt->base_config);
-       available_fmt->base_config = NULL;
-       kfree(available_fmt->out_audio_fmt);
-       available_fmt->out_audio_fmt = NULL;
+       kfree(available_fmt->output_pin_fmts);
+       available_fmt->output_pin_fmts = NULL;
+       kfree(available_fmt->input_pin_fmts);
+       available_fmt->input_pin_fmts = NULL;
 }
 
 static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget)
@@ -326,13 +356,31 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_
        return 0;
 }
 
+static void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swidget)
+{
+       struct snd_soc_component *scomp = swidget->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct sof_ipc4_fw_module *fw_module = swidget->module_info;
+       struct snd_sof_control *scontrol;
+
+       /* update module ID for all kcontrols for this widget */
+       list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+               if (scontrol->comp_id == swidget->comp_id) {
+                       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+                       struct sof_ipc4_msg *msg = &cdata->msg;
+
+                       msg->primary |= fw_module->man4_module_entry.id;
+               }
+       }
+}
+
 static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
 {
        struct sof_ipc4_available_audio_format *available_fmt;
        struct snd_soc_component *scomp = swidget->scomp;
        struct sof_ipc4_copier *ipc4_copier;
        int node_type = 0;
-       int ret, i;
+       int ret;
 
        ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
        if (!ipc4_copier)
@@ -343,17 +391,11 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
 
        dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
 
-       ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
+                                    &ipc4_copier->data.base_config);
        if (ret)
                goto free_copier;
 
-       available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
-                                                GFP_KERNEL);
-       if (!available_fmt->dma_buffer_size) {
-               ret = -ENOMEM;
-               goto free_available_fmt;
-       }
-
        /*
         * This callback is used by host copier and module-to-module copier,
         * and only host copier needs to set gtw_cfg.
@@ -361,21 +403,6 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
        if (!WIDGET_IS_AIF(swidget->id))
                goto skip_gtw_cfg;
 
-       ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
-                                   SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(u32),
-                                   available_fmt->audio_fmt_num);
-       if (ret) {
-               dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
-                       swidget->widget->name);
-               goto err;
-       }
-
-       dev_dbg(scomp->dev, "dma buffer size:\n");
-       for (i = 0; i < available_fmt->audio_fmt_num; i++)
-               dev_dbg(scomp->dev, "%d: %u\n", i,
-                       available_fmt->dma_buffer_size[i]);
-
        ret = sof_update_ipc_object(scomp, &node_type,
                                    SOF_COPIER_TOKENS, swidget->tuples,
                                    swidget->num_tuples, sizeof(node_type), 1);
@@ -383,7 +410,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
        if (ret) {
                dev_err(scomp->dev, "parse host copier node type token failed %d\n",
                        ret);
-               goto err;
+               goto free_available_fmt;
        }
        dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
 
@@ -391,7 +418,7 @@ skip_gtw_cfg:
        ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
        if (!ipc4_copier->gtw_attr) {
                ret = -ENOMEM;
-               goto err;
+               goto free_available_fmt;
        }
 
        ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
@@ -422,8 +449,6 @@ skip_gtw_cfg:
 
 free_gtw_attr:
        kfree(ipc4_copier->gtw_attr);
-err:
-       kfree(available_fmt->dma_buffer_size);
 free_available_fmt:
        sof_ipc4_free_audio_fmt(available_fmt);
 free_copier:
@@ -441,9 +466,7 @@ static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget)
                return;
 
        available_fmt = &ipc4_copier->available_fmt;
-       kfree(available_fmt->dma_buffer_size);
-       kfree(available_fmt->base_config);
-       kfree(available_fmt->out_audio_fmt);
+       kfree(available_fmt->output_pin_fmts);
        kfree(ipc4_copier->gtw_attr);
        kfree(ipc4_copier);
        swidget->private = NULL;
@@ -455,8 +478,10 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dai *dai = swidget->private;
        struct sof_ipc4_copier *ipc4_copier;
+       struct snd_sof_widget *pipe_widget;
+       struct sof_ipc4_pipeline *pipeline;
        int node_type = 0;
-       int ret, i;
+       int ret;
 
        ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
        if (!ipc4_copier)
@@ -466,37 +491,17 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
 
        dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
 
-       ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
+                                    &ipc4_copier->data.base_config);
        if (ret)
                goto free_copier;
 
-       available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
-                                                GFP_KERNEL);
-       if (!available_fmt->dma_buffer_size) {
-               ret = -ENOMEM;
-               goto free_available_fmt;
-       }
-
-       ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
-                                   SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
-                                   swidget->num_tuples, sizeof(u32),
-                                   available_fmt->audio_fmt_num);
-       if (ret) {
-               dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
-                       swidget->widget->name);
-               goto err;
-       }
-
-       for (i = 0; i < available_fmt->audio_fmt_num; i++)
-               dev_dbg(scomp->dev, "%d: dma buffer size: %u\n", i,
-                       available_fmt->dma_buffer_size[i]);
-
        ret = sof_update_ipc_object(scomp, &node_type,
                                    SOF_COPIER_TOKENS, swidget->tuples,
                                    swidget->num_tuples, sizeof(node_type), 1);
        if (ret) {
                dev_err(scomp->dev, "parse dai node type failed %d\n", ret);
-               goto err;
+               goto free_available_fmt;
        }
 
        ret = sof_update_ipc_object(scomp, ipc4_copier,
@@ -504,7 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
                                    swidget->num_tuples, sizeof(u32), 1);
        if (ret) {
                dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret);
-               goto err;
+               goto free_available_fmt;
        }
 
        dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
@@ -512,17 +517,43 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
 
        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);
+               ret = -ENODEV;
+               goto free_available_fmt;
+       }
+
        switch (ipc4_copier->dai_type) {
        case SOF_DAI_INTEL_ALH:
        {
                struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
                struct sof_ipc4_alh_configuration_blob *blob;
+               struct snd_soc_dapm_path *p;
                struct snd_sof_widget *w;
+               int src_num = 0;
+
+               snd_soc_dapm_widget_for_each_source_path(swidget->widget, p)
+                       src_num++;
+
+               if (swidget->id == snd_soc_dapm_dai_in && src_num == 0) {
+                       /*
+                        * The blob will not be used if the ALH copier is playback direction
+                        * and doesn't connect to any source.
+                        * It is fine to call kfree(ipc4_copier->copier_config) since
+                        * ipc4_copier->copier_config is null.
+                        */
+                       ret = 0;
+                       break;
+               }
 
                blob = kzalloc(sizeof(*blob), GFP_KERNEL);
                if (!blob) {
                        ret = -ENOMEM;
-                       goto err;
+                       goto free_available_fmt;
                }
 
                list_for_each_entry(w, &sdev->widget_list, list) {
@@ -551,7 +582,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
                ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
                if (!ipc4_copier->gtw_attr) {
                        ret = -ENOMEM;
-                       goto err;
+                       goto free_available_fmt;
                }
 
                ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
@@ -572,8 +603,6 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
 
 free_copier_config:
        kfree(ipc4_copier->copier_config);
-err:
-       kfree(available_fmt->dma_buffer_size);
 free_available_fmt:
        sof_ipc4_free_audio_fmt(available_fmt);
 free_copier:
@@ -601,9 +630,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
        ipc4_copier = dai->private;
        available_fmt = &ipc4_copier->available_fmt;
 
-       kfree(available_fmt->dma_buffer_size);
-       kfree(available_fmt->base_config);
-       kfree(available_fmt->out_audio_fmt);
+       kfree(available_fmt->output_pin_fmts);
        if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP &&
            ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC)
                kfree(ipc4_copier->copier_config);
@@ -629,6 +656,14 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
                goto err;
        }
 
+       swidget->core = pipeline->core_id;
+
+       if (pipeline->use_chain_dma) {
+               dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
+               swidget->private = pipeline;
+               return 0;
+       }
+
        /* parse one set of pipeline tokens */
        ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
                                    swidget->num_tuples, sizeof(*swidget), 1);
@@ -640,9 +675,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
        /* TODO: Get priority from topology */
        pipeline->priority = 0;
 
-       dev_dbg(scomp->dev, "pipeline '%s': id %d pri %d lp mode %d\n",
+       dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
                swidget->widget->name, swidget->pipeline_id,
-               pipeline->priority, pipeline->lp_mode);
+               pipeline->priority, pipeline->core_id, pipeline->lp_mode);
 
        swidget->private = pipeline;
 
@@ -652,6 +687,7 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
        pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
 
        pipeline->msg.extension = pipeline->lp_mode;
+       pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_CORE_ID(pipeline->core_id);
        pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
 
        return 0;
@@ -663,9 +699,6 @@ err:
 static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
 {
        struct snd_soc_component *scomp = swidget->scomp;
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-       struct sof_ipc4_fw_module *fw_module;
-       struct snd_sof_control *scontrol;
        struct sof_ipc4_gain *gain;
        int ret;
 
@@ -678,8 +711,7 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
        gain->data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
        gain->data.init_val = SOF_IPC4_VOL_ZERO_DB;
 
-       /* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
-       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, false);
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, &gain->base_config);
        if (ret)
                goto err;
 
@@ -699,16 +731,7 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
        if (ret)
                goto err;
 
-       fw_module = swidget->module_info;
-
-       /* update module ID for all kcontrols for this widget */
-       list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
-               if (scontrol->comp_id == swidget->comp_id) {
-                       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
-                       struct sof_ipc4_msg *msg = &cdata->msg;
-
-                       msg->primary |= fw_module->man4_module_entry.id;
-               }
+       sof_ipc4_widget_update_kcontrol_module_id(swidget);
 
        return 0;
 err:
@@ -744,8 +767,8 @@ static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
 
        swidget->private = mixer;
 
-       /* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
-       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt, false);
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt,
+                                    &mixer->base_config);
        if (ret)
                goto err;
 
@@ -775,8 +798,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)
 
        swidget->private = src;
 
-       /* The out_audio_fmt in topology is ignored as it is not required by SRC */
-       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, false);
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, &src->base_config);
        if (ret)
                goto err;
 
@@ -825,6 +847,94 @@ static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
        swidget->private = NULL;
 }
 
+/*
+ * Add the process modules support. The process modules are defined as snd_soc_dapm_effect modules.
+ */
+static int sof_ipc4_widget_setup_comp_process(struct snd_sof_widget *swidget)
+{
+       struct snd_soc_component *scomp = swidget->scomp;
+       struct sof_ipc4_fw_module *fw_module;
+       struct sof_ipc4_process *process;
+       void *cfg;
+       int ret;
+
+       process = kzalloc(sizeof(*process), GFP_KERNEL);
+       if (!process)
+               return -ENOMEM;
+
+       swidget->private = process;
+
+       ret = sof_ipc4_get_audio_fmt(scomp, swidget, &process->available_fmt,
+                                    &process->base_config);
+       if (ret)
+               goto err;
+
+       ret = sof_ipc4_widget_setup_msg(swidget, &process->msg);
+       if (ret)
+               goto err;
+
+       /* parse process init module payload config type from module info */
+       fw_module = swidget->module_info;
+       process->init_config = FIELD_GET(SOF_IPC4_MODULE_INIT_CONFIG_MASK,
+                                        fw_module->man4_module_entry.type);
+
+       process->ipc_config_size = sizeof(struct sof_ipc4_base_module_cfg);
+
+       /* allocate memory for base config extension if needed */
+       if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
+               struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
+               u32 ext_size = struct_size(base_cfg_ext, pin_formats,
+                                               swidget->num_input_pins + swidget->num_output_pins);
+
+               base_cfg_ext = kzalloc(ext_size, GFP_KERNEL);
+               if (!base_cfg_ext) {
+                       ret = -ENOMEM;
+                       goto free_available_fmt;
+               }
+
+               base_cfg_ext->num_input_pin_fmts = swidget->num_input_pins;
+               base_cfg_ext->num_output_pin_fmts = swidget->num_output_pins;
+               process->base_config_ext = base_cfg_ext;
+               process->base_config_ext_size = ext_size;
+               process->ipc_config_size += ext_size;
+       }
+
+       cfg = kzalloc(process->ipc_config_size, GFP_KERNEL);
+       if (!cfg) {
+               ret = -ENOMEM;
+               goto free_base_cfg_ext;
+       }
+
+       process->ipc_config_data = cfg;
+
+       sof_ipc4_widget_update_kcontrol_module_id(swidget);
+
+       return 0;
+free_base_cfg_ext:
+       kfree(process->base_config_ext);
+       process->base_config_ext = NULL;
+free_available_fmt:
+       sof_ipc4_free_audio_fmt(&process->available_fmt);
+err:
+       kfree(process);
+       swidget->private = NULL;
+       return ret;
+}
+
+static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget)
+{
+       struct sof_ipc4_process *process = swidget->private;
+
+       if (!process)
+               return;
+
+       kfree(process->ipc_config_data);
+       kfree(process->base_config_ext);
+       sof_ipc4_free_audio_fmt(&process->available_fmt);
+       kfree(swidget->private);
+       swidget->private = NULL;
+}
+
 static void
 sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
                                   struct sof_ipc4_base_module_cfg *base_config)
@@ -876,22 +986,62 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
        return 0;
 }
 
+/* update hw_params based on the audio stream format */
+static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
+                                    struct sof_ipc4_audio_format *fmt)
+{
+       snd_pcm_format_t snd_fmt;
+       struct snd_interval *i;
+       struct snd_mask *m;
+       int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+       unsigned int channels, rate;
+
+       switch (valid_bits) {
+       case 16:
+               snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
+               break;
+       case 24:
+               snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
+               break;
+       case 32:
+               snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+               break;
+       default:
+               dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
+               return -EINVAL;
+       }
+
+       m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       snd_mask_none(m);
+       snd_mask_set_format(m, snd_fmt);
+
+       rate = fmt->sampling_frequency;
+       i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       i->min = rate;
+       i->max = rate;
+
+       channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+       i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       i->min = channels;
+       i->max = channels;
+
+       return 0;
+}
+
 static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
                                   struct snd_sof_widget *swidget,
                                   struct sof_ipc4_base_module_cfg *base_config,
-                                  struct sof_ipc4_audio_format *out_format,
                                   struct snd_pcm_hw_params *params,
                                   struct sof_ipc4_available_audio_format *available_fmt,
-                                  size_t object_offset)
+                                  struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
 {
-       void *ptr = available_fmt->ref_audio_fmt;
        u32 valid_bits;
        u32 channels;
        u32 rate;
        int sample_valid_bits;
        int i;
 
-       if (!ptr) {
+       if (!pin_fmts) {
                dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name);
                return -EINVAL;
        }
@@ -911,53 +1061,53 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
                return -EINVAL;
        }
 
-       if (!available_fmt->audio_fmt_num) {
+       if (!pin_fmts_size) {
                dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name);
                return -EINVAL;
        }
 
        /*
-        * Search supported audio formats to match rate, channels ,and
+        * Search supported audio formats with pin index 0 to match rate, channels ,and
         * sample_valid_bytes from runtime params
         */
-       for (i = 0; i < available_fmt->audio_fmt_num; i++, ptr = (u8 *)ptr + object_offset) {
-               struct sof_ipc4_audio_format *fmt = ptr;
+       for (i = 0; i < pin_fmts_size; i++) {
+               struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
+
+               if (pin_fmts[i].pin_index)
+                       continue;
 
                rate = fmt->sampling_frequency;
                channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
                valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
                if (params_rate(params) == rate && params_channels(params) == channels &&
                    sample_valid_bits == valid_bits) {
-                       dev_dbg(sdev->dev, "matching audio format index for %uHz, %ubit, %u channels: %d\n",
+                       dev_dbg(sdev->dev, "matched audio format index for %uHz, %ubit, %u channels: %d\n",
                                rate, valid_bits, channels, i);
-
-                       /* copy ibs/obs and input format */
-                       memcpy(base_config, &available_fmt->base_config[i],
-                              sizeof(struct sof_ipc4_base_module_cfg));
-
-                       /* copy output format */
-                       if (out_format)
-                               memcpy(out_format, &available_fmt->out_audio_fmt[i],
-                                      sizeof(struct sof_ipc4_audio_format));
                        break;
                }
        }
 
-       if (i == available_fmt->audio_fmt_num) {
+       if (i == pin_fmts_size) {
                dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
                        __func__, params_rate(params), sample_valid_bits, params_channels(params));
                return -EINVAL;
        }
 
-       dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
-       sof_ipc4_dbg_audio_format(sdev->dev, &base_config->audio_fmt,
-                                 sizeof(*base_config), 1);
-       if (out_format) {
-               dev_dbg(sdev->dev, "Init output audio formats for %s\n", swidget->widget->name);
-               sof_ipc4_dbg_audio_format(sdev->dev, out_format,
-                                         sizeof(*out_format), 1);
+       /* copy input format */
+       if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {
+               memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,
+                      sizeof(struct sof_ipc4_audio_format));
+
+               /* set base_cfg ibs/obs */
+               base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size;
+
+               dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
+               sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);
        }
 
+       if (available_fmt->num_output_formats && i < available_fmt->num_output_formats)
+               base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
+
        /* Return the index of the matched format */
        return i;
 }
@@ -974,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
        pipeline->mem_usage = 0;
 
        if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
+               if (pipeline->use_chain_dma) {
+                       pipeline->msg.primary = 0;
+                       pipeline->msg.extension = 0;
+               }
                ipc4_copier = swidget->private;
        } else if (WIDGET_IS_DAI(swidget->id)) {
                struct snd_sof_dai *dai = swidget->private;
 
                ipc4_copier = dai->private;
+
+               if (pipeline->use_chain_dma) {
+                       pipeline->msg.primary = 0;
+                       pipeline->msg.extension = 0;
+               }
+
                if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
                        struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
                        struct sof_ipc4_alh_configuration_blob *blob;
@@ -1109,6 +1269,69 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
 }
 #endif
 
+static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth)
+{
+       switch (bit_depth) {
+       case 16:
+               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+               break;
+       case 24:
+               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+               break;
+       case 32:
+               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev,
+                                      struct snd_pcm_hw_params *pipeline_params,
+                                      struct snd_pcm_hw_params *fe_params,
+                                      struct sof_ipc4_available_audio_format *available_fmt)
+{
+       struct sof_ipc4_audio_format *audio_fmt;
+       unsigned int sample_valid_bits;
+       bool multiple_formats = false;
+       bool fe_format_match = false;
+       struct snd_mask *fmt;
+       int i;
+
+       for (i = 0; i < available_fmt->num_output_formats; i++) {
+               unsigned int val;
+
+               audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
+               val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg);
+
+               if (i == 0)
+                       sample_valid_bits = val;
+               else if (sample_valid_bits != val)
+                       multiple_formats = true;
+
+               if (snd_pcm_format_width(params_format(fe_params)) == val)
+                       fe_format_match = true;
+       }
+
+       fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
+       snd_mask_none(fmt);
+
+       if (multiple_formats) {
+               if (fe_format_match) {
+                       /* multiple formats defined and one matches FE */
+                       snd_mask_set_format(fmt, params_format(fe_params));
+                       return 0;
+               }
+
+               dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n");
+               return -EINVAL;
+       }
+
+       return ipc4_set_fmt_mask(fmt, sample_valid_bits);
+}
+
 static int
 sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
                               struct snd_pcm_hw_params *fe_params,
@@ -1118,17 +1341,19 @@ sof_ipc4_prepare_copier_module(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 sof_ipc4_pin_format *format_list_to_search;
        struct sof_ipc4_copier_data *copier_data;
        struct snd_pcm_hw_params *ref_params;
        struct sof_ipc4_copier *ipc4_copier;
        struct snd_sof_dai *dai;
        struct snd_mask *fmt;
        int out_sample_valid_bits;
-       size_t ref_audio_fmt_size;
        void **ipc_config_data;
        int *ipc_config_size;
        u32 **data;
        int ipc_size, ret;
+       u32 deep_buffer_dma_ms = 0;
+       u32 format_list_count;
 
        dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
 
@@ -1140,25 +1365,66 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
                struct snd_sof_widget *pipe_widget;
                struct sof_ipc4_pipeline *pipeline;
 
-               pipe_widget = swidget->spipe->pipe_widget;
-               pipeline = pipe_widget->private;
+               /* parse the deep buffer dma size */
+               ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
+                                           SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,
+                                           swidget->num_tuples, sizeof(u32), 1);
+               if (ret) {
+                       dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n",
+                               swidget->widget->name);
+                       return ret;
+               }
+
                ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
                gtw_attr = ipc4_copier->gtw_attr;
                copier_data = &ipc4_copier->data;
                available_fmt = &ipc4_copier->available_fmt;
 
+               pipe_widget = swidget->spipe->pipe_widget;
+               pipeline = pipe_widget->private;
+
+               if (pipeline->use_chain_dma) {
+                       u32 host_dma_id;
+                       u32 fifo_size;
+
+                       host_dma_id = platform_params->stream_tag - 1;
+                       pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
+
+                       /* Set SCS bit for S16_LE format only */
+                       if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
+                               pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
+
+                       /*
+                        * Despite its name the bitfield 'fifo_size' is used to define DMA buffer
+                        * size. The expression calculates 2ms buffer size.
+                        */
+                       fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS *
+                                                 params_rate(fe_params) *
+                                                 params_channels(fe_params) *
+                                                 params_physical_width(fe_params)), 8000);
+                       pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);
+
+                       /*
+                        * Chain DMA does not support stream timestamping, set node_id to invalid
+                        * to skip the code in sof_ipc4_get_stream_start_offset().
+                        */
+                       copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
+
+                       return 0;
+               }
+
                /*
-                * base_config->audio_fmt and out_audio_fmt represent the input and output audio
-                * formats. Use the input format as the reference to match pcm params for playback
-                * and the output format as reference for capture.
+                * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
+                * for capture.
                 */
                if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
-                       available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
-                       ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+                       format_list_to_search = available_fmt->input_pin_fmts;
+                       format_list_count = available_fmt->num_input_formats;
                } else {
-                       available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
-                       ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
+                       format_list_to_search = available_fmt->output_pin_fmts;
+                       format_list_count = available_fmt->num_output_formats;
                }
+
                copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
                copier_data->gtw_cfg.node_id |=
                        SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);
@@ -1171,25 +1437,28 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
        case snd_soc_dapm_dai_in:
        case snd_soc_dapm_dai_out:
        {
+               struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+               struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+               if (pipeline->use_chain_dma)
+                       return 0;
+
                dai = swidget->private;
 
                ipc4_copier = (struct sof_ipc4_copier *)dai->private;
                copier_data = &ipc4_copier->data;
                available_fmt = &ipc4_copier->available_fmt;
                if (dir == SNDRV_PCM_STREAM_CAPTURE) {
-                       available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
-                       ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
+                       format_list_to_search = available_fmt->output_pin_fmts;
+                       format_list_count = available_fmt->num_output_formats;
 
-                       /*
-                        * modify the input params for the dai copier as it only supports
-                        * 32-bit always
-                        */
-                       fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
-                       snd_mask_none(fmt);
-                       snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+                       ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params,
+                                                         available_fmt);
+                       if (ret < 0)
+                               return ret;
                } else {
-                       available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
-                       ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+                       format_list_to_search = available_fmt->input_pin_fmts;
+                       format_list_count = available_fmt->num_input_formats;
                }
 
                ref_params = pipeline_params;
@@ -1209,12 +1478,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
                copier_data = &ipc4_copier->data;
                available_fmt = &ipc4_copier->available_fmt;
 
-               /*
-                * base_config->audio_fmt represent the input audio formats. Use
-                * the input format as the reference to match pcm params
-                */
-               available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
-               ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+               /* Use the input formats to match pcm params */
+               format_list_to_search = available_fmt->input_pin_fmts;
+               format_list_count = available_fmt->num_input_formats;
                ref_params = pipeline_params;
 
                break;
@@ -1226,12 +1492,23 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
        }
 
        /* set input and output audio formats */
-       ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config,
-                                     &copier_data->out_format, ref_params,
-                                     available_fmt, ref_audio_fmt_size);
+       ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
+                                     available_fmt, format_list_to_search, format_list_count);
        if (ret < 0)
                return ret;
 
+       /*
+        * Set the output format. Current topology defines pin 0 input and output formats in pairs.
+        * This assumes that the pin 0 formats are defined before all other pins.
+        * So pick the output audio format with the same index as the chosen
+        * input format. This logic will need to be updated when the format definitions
+        * in topology change.
+        */
+       memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+              sizeof(struct sof_ipc4_audio_format));
+       dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);
+       sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1);
+
        switch (swidget->id) {
        case snd_soc_dapm_dai_in:
        case snd_soc_dapm_dai_out:
@@ -1323,25 +1600,34 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
        out_sample_valid_bits =
                SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg);
        snd_mask_none(fmt);
-       switch (out_sample_valid_bits) {
-       case 16:
-               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+       ret = ipc4_set_fmt_mask(fmt, out_sample_valid_bits);
+       if (ret)
+               return ret;
+
+       /*
+        * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the
+        * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set
+        * in topology.
+        */
+       switch (swidget->id) {
+       case snd_soc_dapm_dai_in:
+               copier_data->gtw_cfg.dma_buffer_size =
+                       SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs;
                break;
-       case 24:
-               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+       case snd_soc_dapm_aif_in:
+                       copier_data->gtw_cfg.dma_buffer_size =
+                               max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
+                                       copier_data->base_config.ibs;
                break;
-       case 32:
-               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_aif_out:
+               copier_data->gtw_cfg.dma_buffer_size =
+                       SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs;
                break;
        default:
-               dev_err(sdev->dev, "invalid sample frame format %d\n",
-                       params_format(pipeline_params));
-               return -EINVAL;
+               break;
        }
 
-       /* set the gateway dma_buffer_size using the matched ID returned above */
-       copier_data->gtw_cfg.dma_buffer_size = available_fmt->dma_buffer_size[ret];
-
        data = &ipc4_copier->copier_config;
        ipc_config_size = &ipc4_copier->ipc_config_size;
        ipc_config_data = &ipc4_copier->ipc_config_data;
@@ -1377,14 +1663,13 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_gain *gain = swidget->private;
+       struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
        int ret;
 
-       gain->available_fmt.ref_audio_fmt = &gain->available_fmt.base_config->audio_fmt;
-
-       /* output format is not required to be sent to the FW for gain */
        ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
-                                     NULL, pipeline_params, &gain->available_fmt,
-                                     sizeof(gain->base_config));
+                                     pipeline_params, available_fmt,
+                                     available_fmt->input_pin_fmts,
+                                     available_fmt->num_input_formats);
        if (ret < 0)
                return ret;
 
@@ -1402,15 +1687,13 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_mixer *mixer = swidget->private;
+       struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
        int ret;
 
-       /* only 32bit is supported by mixer */
-       mixer->available_fmt.ref_audio_fmt = &mixer->available_fmt.base_config->audio_fmt;
-
-       /* output format is not required to be sent to the FW for mixer */
        ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
-                                     NULL, pipeline_params, &mixer->available_fmt,
-                                     sizeof(mixer->base_config));
+                                     pipeline_params, available_fmt,
+                                     available_fmt->input_pin_fmts,
+                                     available_fmt->num_input_formats);
        if (ret < 0)
                return ret;
 
@@ -1428,15 +1711,14 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
        struct snd_soc_component *scomp = swidget->scomp;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_src *src = swidget->private;
+       struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
        struct snd_interval *rate;
        int ret;
 
-       src->available_fmt.ref_audio_fmt = &src->available_fmt.base_config->audio_fmt;
-
-       /* output format is not required to be sent to the FW for SRC */
        ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
-                                     NULL, pipeline_params, &src->available_fmt,
-                                     sizeof(src->base_config));
+                                     pipeline_params, available_fmt,
+                                     available_fmt->input_pin_fmts,
+                                     available_fmt->num_input_formats);
        if (ret < 0)
                return ret;
 
@@ -1451,6 +1733,135 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
        return 0;
 }
 
+static int
+sof_ipc4_process_set_pin_formats(struct snd_sof_widget *swidget, int pin_type)
+{
+       struct sof_ipc4_process *process = swidget->private;
+       struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
+       struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+       struct sof_ipc4_pin_format *pin_format, *format_list_to_search;
+       struct snd_soc_component *scomp = swidget->scomp;
+       int num_pins, format_list_count;
+       int pin_format_offset = 0;
+       int i, j;
+
+       /* set number of pins, offset of pin format and format list to search based on pin type */
+       if (pin_type == SOF_PIN_TYPE_INPUT) {
+               num_pins = swidget->num_input_pins;
+               format_list_to_search = available_fmt->input_pin_fmts;
+               format_list_count = available_fmt->num_input_formats;
+       } else {
+               num_pins = swidget->num_output_pins;
+               pin_format_offset = swidget->num_input_pins;
+               format_list_to_search = available_fmt->output_pin_fmts;
+               format_list_count = available_fmt->num_output_formats;
+       }
+
+       for (i = pin_format_offset; i < num_pins + pin_format_offset; i++) {
+               pin_format = &base_cfg_ext->pin_formats[i];
+
+               /* Pin 0 audio formats are derived from the base config input/output format */
+               if (i == pin_format_offset) {
+                       if (pin_type == SOF_PIN_TYPE_INPUT) {
+                               pin_format->buffer_size = process->base_config.ibs;
+                               pin_format->audio_fmt = process->base_config.audio_fmt;
+                       } else {
+                               pin_format->buffer_size = process->base_config.obs;
+                               pin_format->audio_fmt = process->output_format;
+                       }
+                       continue;
+               }
+
+               /*
+                * For all other pins, find the pin formats from those set in topology. If there
+                * is more than one format specified for a pin, this will pick the first available
+                * one.
+                */
+               for (j = 0; j < format_list_count; j++) {
+                       struct sof_ipc4_pin_format *pin_format_item = &format_list_to_search[j];
+
+                       if (pin_format_item->pin_index == i - pin_format_offset) {
+                               *pin_format = *pin_format_item;
+                               break;
+                       }
+               }
+
+               if (j == format_list_count) {
+                       dev_err(scomp->dev, "%s pin %d format not found for %s\n",
+                               (pin_type == SOF_PIN_TYPE_INPUT) ? "input" : "output",
+                               i - pin_format_offset, swidget->widget->name);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int sof_ipc4_process_add_base_cfg_extn(struct snd_sof_widget *swidget)
+{
+       int ret, i;
+
+       /* copy input and output pin formats */
+       for (i = 0; i <= SOF_PIN_TYPE_OUTPUT; i++) {
+               ret = sof_ipc4_process_set_pin_formats(swidget, i);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
+                                          struct snd_pcm_hw_params *fe_params,
+                                          struct snd_sof_platform_stream_params *platform_params,
+                                          struct snd_pcm_hw_params *pipeline_params, int dir)
+{
+       struct snd_soc_component *scomp = swidget->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct sof_ipc4_process *process = swidget->private;
+       struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+       void *cfg = process->ipc_config_data;
+       int ret;
+
+       ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config,
+                                     pipeline_params, available_fmt,
+                                     available_fmt->input_pin_fmts,
+                                     available_fmt->num_input_formats);
+       if (ret < 0)
+               return ret;
+
+       /* copy Pin 0 output format */
+       if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats &&
+           !available_fmt->output_pin_fmts[ret].pin_index) {
+               memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+                      sizeof(struct sof_ipc4_audio_format));
+
+               /* modify the pipeline params with the pin 0 output format */
+               ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
+               if (ret)
+                       return ret;
+       }
+
+       /* update pipeline memory usage */
+       sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &process->base_config);
+
+       /* ipc_config_data is composed of the base_config followed by an optional extension */
+       memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg));
+       cfg += sizeof(struct sof_ipc4_base_module_cfg);
+
+       if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
+               struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
+
+               ret = sof_ipc4_process_add_base_cfg_extn(swidget);
+               if (ret < 0)
+                       return ret;
+
+               memcpy(cfg, base_cfg_ext, process->base_config_ext_size);
+       }
+
+       return 0;
+}
+
 static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
 {
        struct sof_ipc4_control_data *control_data;
@@ -1483,6 +1894,71 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof
        return 0;
 }
 
+static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+       struct sof_ipc4_control_data *control_data;
+       struct sof_ipc4_msg *msg;
+       int ret;
+
+       if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {
+               dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",
+                       scontrol->name, scontrol->max_size);
+               return -EINVAL;
+       }
+
+       if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {
+               dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",
+                       scontrol->name, scontrol->priv_size,
+                       scontrol->max_size - sizeof(*control_data));
+               return -EINVAL;
+       }
+
+       scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;
+
+       scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
+       if (!scontrol->ipc_control_data)
+               return -ENOMEM;
+
+       control_data = scontrol->ipc_control_data;
+       control_data->index = scontrol->index;
+       if (scontrol->priv_size > 0) {
+               memcpy(control_data->data, scontrol->priv, scontrol->priv_size);
+               kfree(scontrol->priv);
+               scontrol->priv = NULL;
+
+               if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) {
+                       dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n",
+                               control_data->data->magic, scontrol->name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               /* TODO: check the ABI version */
+
+               if (control_data->data->size + sizeof(struct sof_abi_hdr) !=
+                   scontrol->priv_size) {
+                       dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n",
+                               scontrol->name,
+                               control_data->data->size + sizeof(struct sof_abi_hdr),
+                               scontrol->priv_size);
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       msg = &control_data->msg;
+       msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+       msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+       msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+       return 0;
+
+err:
+       kfree(scontrol->ipc_control_data);
+       scontrol->ipc_control_data = NULL;
+       return ret;
+}
+
 static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
 {
        switch (scontrol->info_type) {
@@ -1490,6 +1966,8 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
        case SND_SOC_TPLG_CTL_VOLSW_SX:
        case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
                return sof_ipc4_control_load_volume(sdev, scontrol);
+       case SND_SOC_TPLG_CTL_BYTES:
+               return sof_ipc4_control_load_bytes(sdev, scontrol);
        default:
                break;
        }
@@ -1511,6 +1989,12 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
        case snd_soc_dapm_scheduler:
                pipeline = swidget->private;
 
+               if (pipeline->use_chain_dma) {
+                       dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
+                                swidget->widget->name);
+                       return 0;
+               }
+
                dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,
                        pipeline->mem_usage);
 
@@ -1533,6 +2017,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
        {
                struct sof_ipc4_copier *ipc4_copier = swidget->private;
 
+               pipeline = pipe_widget->private;
+               if (pipeline->use_chain_dma)
+                       return 0;
+
                ipc_size = ipc4_copier->ipc_config_size;
                ipc_data = ipc4_copier->ipc_config_data;
 
@@ -1545,6 +2033,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct snd_sof_dai *dai = swidget->private;
                struct sof_ipc4_copier *ipc4_copier = dai->private;
 
+               pipeline = pipe_widget->private;
+               if (pipeline->use_chain_dma)
+                       return 0;
+
                ipc_size = ipc4_copier->ipc_config_size;
                ipc_data = ipc4_copier->ipc_config_data;
 
@@ -1582,6 +2074,22 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                msg = &src->msg;
                break;
        }
+       case snd_soc_dapm_effect:
+       {
+               struct sof_ipc4_process *process = swidget->private;
+
+               if (!process->ipc_config_size) {
+                       dev_err(sdev->dev, "module %s has no config data!\n",
+                               swidget->widget->name);
+                       return -EINVAL;
+               }
+
+               ipc_size = process->ipc_config_size;
+               ipc_data = process->ipc_config_data;
+
+               msg = &process->msg;
+               break;
+       }
        default:
                dev_err(sdev->dev, "widget type %d not supported", swidget->id);
                return -EINVAL;
@@ -1610,7 +2118,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
        msg->data_size = ipc_size;
        msg->data_ptr = ipc_data;
 
-       ret = sof_ipc_tx_message(sdev->ipc, msg, ipc_size, NULL, 0);
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);
        if (ret < 0) {
                dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
 
@@ -1640,6 +2148,13 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_ipc4_msg msg = {{ 0 }};
                u32 header;
 
+               if (pipeline->use_chain_dma) {
+                       dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
+                                swidget->widget->name);
+                       mutex_unlock(&ipc4_data->pipeline_state_mutex);
+                       return 0;
+               }
+
                header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
                header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);
                header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
@@ -1647,7 +2162,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
 
                msg.primary = header;
 
-               ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
                if (ret < 0)
                        dev_err(sdev->dev, "failed to free pipeline widget %s\n",
                                swidget->widget->name);
@@ -1656,7 +2171,11 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
                pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
                ida_free(&pipeline_ida, swidget->instance_id);
        } else {
-               ida_free(&fw_module->m_ida, swidget->instance_id);
+               struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+               struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+               if (!pipeline->use_chain_dma)
+                       ida_free(&fw_module->m_ida, swidget->instance_id);
        }
 
        mutex_unlock(&ipc4_data->pipeline_state_mutex);
@@ -1675,17 +2194,17 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
        u32 num_pins;
        int i;
 
-       if (pin_type == SOF_PIN_TYPE_SOURCE) {
+       if (pin_type == SOF_PIN_TYPE_OUTPUT) {
                current_swidget = src_widget;
-               pin_binding = src_widget->src_pin_binding;
-               queue_ida = &src_widget->src_queue_ida;
-               num_pins = src_widget->num_source_pins;
+               pin_binding = src_widget->output_pin_binding;
+               queue_ida = &src_widget->output_queue_ida;
+               num_pins = src_widget->num_output_pins;
                buddy_name = sink_widget->widget->name;
        } else {
                current_swidget = sink_widget;
-               pin_binding = sink_widget->sink_pin_binding;
-               queue_ida = &sink_widget->sink_queue_ida;
-               num_pins = sink_widget->num_sink_pins;
+               pin_binding = sink_widget->input_pin_binding;
+               queue_ida = &sink_widget->input_queue_ida;
+               num_pins = sink_widget->num_input_pins;
                buddy_name = src_widget->widget->name;
        }
 
@@ -1693,12 +2212,12 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
 
        if (num_pins < 1) {
                dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",
-                       (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
+                       (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
                        num_pins, current_swidget->widget->name);
                return -EINVAL;
        }
 
-       /* If there is only one sink/source pin, queue id must be 0 */
+       /* If there is only one input/output pin, queue id must be 0 */
        if (num_pins == 1)
                return 0;
 
@@ -1713,7 +2232,7 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
                 * mixed use pin binding array and ida for queue ID allocation.
                 */
                dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",
-                       (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
+                       (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
                        current_swidget->widget->name);
                return -EINVAL;
        }
@@ -1729,14 +2248,14 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
        char **pin_binding;
        int num_pins;
 
-       if (pin_type == SOF_PIN_TYPE_SOURCE) {
-               pin_binding = swidget->src_pin_binding;
-               queue_ida = &swidget->src_queue_ida;
-               num_pins = swidget->num_source_pins;
+       if (pin_type == SOF_PIN_TYPE_OUTPUT) {
+               pin_binding = swidget->output_pin_binding;
+               queue_ida = &swidget->output_queue_ida;
+               num_pins = swidget->num_output_pins;
        } else {
-               pin_binding = swidget->sink_pin_binding;
-               queue_ida = &swidget->sink_queue_ida;
-               num_pins = swidget->num_sink_pins;
+               pin_binding = swidget->input_pin_binding;
+               queue_ida = &swidget->input_queue_ida;
+               num_pins = swidget->num_input_pins;
        }
 
        /* Nothing to free if queue ID is not allocated with ida. */
@@ -1751,9 +2270,9 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
                                           struct snd_sof_widget *sink_widget,
                                           int sink_id)
 {
-       struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
-       struct sof_ipc4_base_module_cfg *src_config;
        struct sof_ipc4_copier_config_set_sink_format format;
+       struct sof_ipc4_base_module_cfg *src_config;
+       const struct sof_ipc4_audio_format *pin_fmt;
        struct sof_ipc4_fw_module *fw_module;
        struct sof_ipc4_msg msg = {{ 0 }};
        u32 header, extension;
@@ -1773,7 +2292,16 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
 
        format.sink_id = sink_id;
        memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
-       memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
+
+       pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id);
+       if (!pin_fmt) {
+               dev_err(sdev->dev, "Unable to get pin %d format for %s",
+                       sink_id, sink_widget->widget->name);
+               return -EINVAL;
+       }
+
+       memcpy(&format.sink_fmt, pin_fmt, sizeof(format.sink_fmt));
+
        msg.data_size = sizeof(format);
        msg.data_ptr = &format;
 
@@ -1792,19 +2320,34 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
        msg.primary = header;
        msg.extension = extension;
 
-       return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
+       return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, msg.data_size);
 }
 
 static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
 {
        struct snd_sof_widget *src_widget = sroute->src_widget;
        struct snd_sof_widget *sink_widget = sroute->sink_widget;
+       struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
+       struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
        struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
        struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
+       struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
+       struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
        struct sof_ipc4_msg msg = {{ 0 }};
        u32 header, extension;
        int ret;
 
+       /* no route set up if chain DMA is used */
+       if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) {
+               if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) {
+                       dev_err(sdev->dev,
+                               "use_chain_dma must be set for both src %s and sink %s pipelines\n",
+                               src_widget->widget->name, sink_widget->widget->name);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
        if (!src_fw_module || !sink_fw_module) {
                dev_err(sdev->dev,
                        "cannot bind %s -> %s, no firmware module for: %s%s\n",
@@ -1816,7 +2359,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
        }
 
        sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
-                                                    SOF_PIN_TYPE_SOURCE);
+                                                    SOF_PIN_TYPE_OUTPUT);
        if (sroute->src_queue_id < 0) {
                dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
                        src_widget->widget->name);
@@ -1824,12 +2367,12 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
        }
 
        sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
-                                                    SOF_PIN_TYPE_SINK);
+                                                    SOF_PIN_TYPE_INPUT);
        if (sroute->dst_queue_id < 0) {
                dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
                        sink_widget->widget->name);
                sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
-                                     SOF_PIN_TYPE_SOURCE);
+                                     SOF_PIN_TYPE_OUTPUT);
                return sroute->dst_queue_id;
        }
 
@@ -1862,7 +2405,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
        msg.primary = header;
        msg.extension = extension;
 
-       ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
        if (ret < 0) {
                dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
                        src_widget->widget->name, sroute->src_queue_id,
@@ -1873,8 +2416,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
        return ret;
 
 out:
-       sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
-       sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
+       sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
+       sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
        return ret;
 }
 
@@ -1885,9 +2428,17 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
        struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
        struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
        struct sof_ipc4_msg msg = {{ 0 }};
+       struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
+       struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
+       struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
+       struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
        u32 header, extension;
        int ret = 0;
 
+       /* no route is set up if chain DMA is used */
+       if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma)
+               return 0;
+
        dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
                src_widget->widget->name, sroute->src_queue_id,
                sink_widget->widget->name, sroute->dst_queue_id);
@@ -1913,14 +2464,14 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
        msg.primary = header;
        msg.extension = extension;
 
-       ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+       ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
        if (ret < 0)
                dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
                        src_widget->widget->name, sroute->src_queue_id,
                        sink_widget->widget->name, sroute->dst_queue_id);
 out:
-       sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
-       sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
+       sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
+       sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
 
        return ret;
 }
@@ -1949,6 +2500,11 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
 
        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;
                pipeline->skip_during_fe_trigger = true;
@@ -2149,10 +2705,9 @@ static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link
 static enum sof_tokens common_copier_token_list[] = {
        SOF_COMP_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
        SOF_OUT_AUDIO_FORMAT_TOKENS,
-       SOF_COPIER_GATEWAY_CFG_TOKENS,
+       SOF_COPIER_DEEP_BUFFER_TOKENS,
        SOF_COPIER_TOKENS,
        SOF_COMP_EXT_TOKENS,
 };
@@ -2165,10 +2720,8 @@ static enum sof_tokens pipeline_token_list[] = {
 static enum sof_tokens dai_token_list[] = {
        SOF_COMP_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
        SOF_OUT_AUDIO_FORMAT_TOKENS,
-       SOF_COPIER_GATEWAY_CFG_TOKENS,
        SOF_COPIER_TOKENS,
        SOF_DAI_TOKENS,
        SOF_COMP_EXT_TOKENS,
@@ -2178,8 +2731,8 @@ static enum sof_tokens pga_token_list[] = {
        SOF_COMP_TOKENS,
        SOF_GAIN_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
+       SOF_OUT_AUDIO_FORMAT_TOKENS,
        SOF_COMP_EXT_TOKENS,
 };
 
@@ -2187,7 +2740,7 @@ static enum sof_tokens mixer_token_list[] = {
        SOF_COMP_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
+       SOF_OUT_AUDIO_FORMAT_TOKENS,
        SOF_COMP_EXT_TOKENS,
 };
 
@@ -2196,7 +2749,15 @@ static enum sof_tokens src_token_list[] = {
        SOF_SRC_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
+       SOF_OUT_AUDIO_FORMAT_TOKENS,
+       SOF_COMP_EXT_TOKENS,
+};
+
+static enum sof_tokens process_token_list[] = {
+       SOF_COMP_TOKENS,
+       SOF_AUDIO_FMT_NUM_TOKENS,
+       SOF_IN_AUDIO_FORMAT_TOKENS,
+       SOF_OUT_AUDIO_FORMAT_TOKENS,
        SOF_COMP_EXT_TOKENS,
 };
 
@@ -2237,6 +2798,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
                                src_token_list, ARRAY_SIZE(src_token_list),
                                NULL, sof_ipc4_prepare_src_module,
                                NULL},
+       [snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process,
+                               sof_ipc4_widget_free_comp_process,
+                               process_token_list, ARRAY_SIZE(process_token_list),
+                               NULL, sof_ipc4_prepare_process_module,
+                               NULL},
 };
 
 const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
index 123f1096f3261d46b0e1eda51376ce9b3c02e1d8..cf007282867b5e9e59a2899cb8bece4258d0b794 100644 (file)
 #define SOF_IPC4_MODULE_LL             BIT(5)
 #define SOF_IPC4_MODULE_DP             BIT(6)
 #define SOF_IPC4_MODULE_LIB_CODE               BIT(7)
+#define SOF_IPC4_MODULE_INIT_CONFIG_MASK       GENMASK(11, 8)
+
+#define SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG              0
+#define SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT     1
 
 #define SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE 12
 #define SOF_IPC4_PIPELINE_OBJECT_SIZE 448
@@ -55,6 +59,9 @@
 
 #define SOF_IPC4_INVALID_NODE_ID       0xffffffff
 
+/* FW requires minimum 2ms DMA buffer size */
+#define SOF_IPC4_MIN_DMA_BUFFER_SIZE   2
+
 /*
  * The base of multi-gateways. Multi-gateways addressing starts from
  * ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
@@ -117,7 +124,9 @@ struct sof_ipc4_copier_config_set_sink_format {
  * @priority: Priority of this pipeline
  * @lp_mode: Low power mode
  * @mem_usage: Memory usage
+ * @core_id: Target core for the pipeline
  * @state: Pipeline state
+ * @use_chain_dma: flag to indicate if the firmware shall use chained DMA
  * @msg: message structure for pipeline
  * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
  */
@@ -125,7 +134,9 @@ struct sof_ipc4_pipeline {
        uint32_t priority;
        uint32_t lp_mode;
        uint32_t mem_usage;
+       uint32_t core_id;
        int state;
+       bool use_chain_dma;
        struct sof_ipc4_msg msg;
        bool skip_during_fe_trigger;
 };
@@ -140,20 +151,33 @@ struct ipc4_pipeline_set_state_data {
        DECLARE_FLEX_ARRAY(u32, pipeline_ids);
 } __packed;
 
+/**
+ * struct sof_ipc4_pin_format - Module pin format
+ * @pin_index: pin index
+ * @buffer_size: buffer size in bytes
+ * @audio_fmt: audio format for the pin
+ *
+ * This structure can be used for both output or input pins and the pin_index is relative to the
+ * pin type i.e output/input pin
+ */
+struct sof_ipc4_pin_format {
+       u32 pin_index;
+       u32 buffer_size;
+       struct sof_ipc4_audio_format audio_fmt;
+};
+
 /**
  * struct sof_ipc4_available_audio_format - Available audio formats
- * @base_config: Available base config
- * @out_audio_fmt: Available output audio format
- * @ref_audio_fmt: Reference audio format to match runtime audio format
- * @dma_buffer_size: Available Gateway DMA buffer size (in bytes)
- * @audio_fmt_num: Number of available audio formats
+ * @output_pin_fmts: Available output pin formats
+ * @input_pin_fmts: Available input pin formats
+ * @num_input_formats: Number of input pin formats
+ * @num_output_formats: Number of output pin formats
  */
 struct sof_ipc4_available_audio_format {
-       struct sof_ipc4_base_module_cfg *base_config;
-       struct sof_ipc4_audio_format *out_audio_fmt;
-       struct sof_ipc4_audio_format *ref_audio_fmt;
-       u32 *dma_buffer_size;
-       int audio_fmt_num;
+       struct sof_ipc4_pin_format *output_pin_fmts;
+       struct sof_ipc4_pin_format *input_pin_fmts;
+       u32 num_input_formats;
+       u32 num_output_formats;
 };
 
 /**
@@ -266,8 +290,8 @@ struct sof_ipc4_control_data {
        int index;
 
        union {
-               struct sof_ipc4_ctrl_value_chan chanv[0];
-               struct sof_abi_hdr data[0];
+               DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+               DECLARE_FLEX_ARRAY(struct sof_abi_hdr, data);
        };
 };
 
@@ -329,4 +353,45 @@ struct sof_ipc4_src {
        struct sof_ipc4_msg msg;
 };
 
+/**
+ * struct sof_ipc4_base_module_cfg_ext - base module config extension containing the pin format
+ * information for the module. Both @num_input_pin_fmts and @num_output_pin_fmts cannot be 0 for a
+ * module.
+ * @num_input_pin_fmts: number of input pin formats in the @pin_formats array
+ * @num_output_pin_fmts: number of output pin formats in the @pin_formats array
+ * @reserved: reserved for future use
+ * @pin_formats: flexible array consisting of @num_input_pin_fmts input pin format items followed
+ *              by @num_output_pin_fmts output pin format items
+ */
+struct sof_ipc4_base_module_cfg_ext {
+       u16 num_input_pin_fmts;
+       u16 num_output_pin_fmts;
+       u8 reserved[12];
+       DECLARE_FLEX_ARRAY(struct sof_ipc4_pin_format, pin_formats);
+} __packed;
+
+/**
+ * struct sof_ipc4_process - process config data
+ * @base_config: IPC base config data
+ * @base_config_ext: Base config extension data for module init
+ * @output_format: Output audio format
+ * @available_fmt: Available audio format
+ * @ipc_config_data: Process module config data
+ * @ipc_config_size: Size of process module config data
+ * @msg: IPC4 message struct containing header and data info
+ * @base_config_ext_size: Size of the base config extension data in bytes
+ * @init_config: Module init config type (SOF_IPC4_MODULE_INIT_CONFIG_TYPE_*)
+ */
+struct sof_ipc4_process {
+       struct sof_ipc4_base_module_cfg base_config;
+       struct sof_ipc4_base_module_cfg_ext *base_config_ext;
+       struct sof_ipc4_audio_format output_format;
+       struct sof_ipc4_available_audio_format available_fmt;
+       void *ipc_config_data;
+       uint32_t ipc_config_size;
+       struct sof_ipc4_msg msg;
+       u32 base_config_ext_size;
+       u32 init_config;
+};
+
 #endif
index 81d202e5ce53de3eec2943ee7bd453452b6f27c6..2f8555f11c03646ed3202649defd72b57e90a245 100644 (file)
@@ -123,7 +123,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
                                               "fw_version", 0444);
                /* errors are only due to memory allocation, not debugfs */
                if (ret < 0) {
-                       dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
+                       dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
                        return ret;
                }
        }
@@ -131,7 +131,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
        /* perform pre fw run operations */
        ret = snd_sof_dsp_pre_fw_run(sdev);
        if (ret < 0) {
-               dev_err(sdev->dev, "error: failed pre fw run op\n");
+               dev_err(sdev->dev, "failed pre fw run op\n");
                return ret;
        }
 
index 445acb5c3a21bb3a744e73e66ab55cd546a0dc48..567db32173a88fc922cfc87b8f5640efa3219e89 100644 (file)
@@ -211,16 +211,22 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
        dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
                spcm->pcm.pcm_id, substream->stream);
 
-       /* free PCM in the DSP */
-       if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
-               ret = pcm_ops->hw_free(component, substream);
-               if (ret < 0)
-                       err = ret;
+       if (spcm->prepared[substream->stream]) {
+               /* stop DMA first if needed */
+               if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+                       snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
+
+               /* free PCM in the DSP */
+               if (pcm_ops && pcm_ops->hw_free) {
+                       ret = pcm_ops->hw_free(component, substream);
+                       if (ret < 0)
+                               err = ret;
+               }
 
                spcm->prepared[substream->stream] = false;
        }
 
-       /* stop DMA */
+       /* reset DMA */
        ret = snd_sof_pcm_platform_hw_free(sdev, substream);
        if (ret < 0) {
                dev_err(component->dev, "error: platform hw free failed\n");
@@ -301,6 +307,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
                ipc_first = true;
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (pcm_ops && pcm_ops->ipc_first_on_start)
+                       ipc_first = true;
                break;
        case SNDRV_PCM_TRIGGER_START:
                if (spcm->stream[substream->stream].suspend_ignored) {
@@ -312,6 +320,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
                        spcm->stream[substream->stream].suspend_ignored = false;
                        return 0;
                }
+
+               if (pcm_ops && pcm_ops->ipc_first_on_start)
+                       ipc_first = true;
                break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
                if (sdev->system_suspend_target == SOF_SUSPEND_S0IX &&
@@ -325,29 +336,45 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
                        spcm->stream[substream->stream].suspend_ignored = true;
                        return 0;
                }
+
+               /* On suspend the DMA must be stopped in DSPless mode */
+               if (sdev->dspless_mode_selected)
+                       reset_hw_params = true;
+
                fallthrough;
        case SNDRV_PCM_TRIGGER_STOP:
                ipc_first = true;
-               reset_hw_params = true;
+               if (pcm_ops && pcm_ops->reset_hw_params_during_stop)
+                       reset_hw_params = true;
                break;
        default:
                dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
                return -EINVAL;
        }
 
-       /*
-        * DMA and IPC sequence is different for start and stop. Need to send
-        * STOP IPC before stop DMA
-        */
        if (!ipc_first)
                snd_sof_pcm_platform_trigger(sdev, substream, cmd);
 
        if (pcm_ops && pcm_ops->trigger)
                ret = pcm_ops->trigger(component, substream, cmd);
 
-       /* need to STOP DMA even if trigger IPC failed */
-       if (ipc_first)
-               snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_START:
+               /* invoke platform trigger to start DMA only if pcm_ops is successful */
+               if (ipc_first && !ret)
+                       snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
+               if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free))
+                       snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+               break;
+       default:
+               break;
+       }
 
        /* free PCM if reset_hw_params is set and the STOP IPC is successful */
        if (!ret && reset_hw_params)
@@ -690,7 +717,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
 
        pd->pcm_construct = sof_pcm_new;
        pd->ignore_machine = drv_name;
-       pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
        pd->be_pcm_base = SOF_BE_PCM_BASE;
        pd->use_dai_pcm_id = true;
        pd->topology_name_prefix = "sof";
@@ -699,4 +725,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
        pd->module_get_upon_open = 1;
 
        pd->legacy_dai_naming = 1;
+
+       /*
+        * The fixup is only needed when the DSP is in use as with the DSPless
+        * mode we are directly using the audio interface
+        */
+       if (!sdev->dspless_mode_selected)
+               pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
 }
index 85412aeb1ca16b0ed66d4db24a5e449be4370e57..2fdbc53ca7150b3ad5f597d22705d83fc87ce7c3 100644 (file)
@@ -103,6 +103,11 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                return ret;
        }
 
+       if (sdev->dspless_mode_selected) {
+               sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+               return 0;
+       }
+
        /*
         * Nothing further to be done for platforms that support the low power
         * D0 substate. Resume trace and return when resuming from
index 6de388a8d0b8df64e6653acc2df7e09510d50326..1cbda595c5183448cb148d4f643ad8370c5deb98 100644 (file)
@@ -280,9 +280,11 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
 static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
                                          struct snd_soc_dapm_widget_list *list, int dir)
 {
+       const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
        struct snd_soc_dapm_widget *widget;
+       struct snd_sof_route *sroute;
        struct snd_soc_dapm_path *p;
-       int ret;
+       int ret = 0;
        int i;
 
        /*
@@ -325,6 +327,63 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
                }
        }
 
+       /*
+        * The above loop handles connections between widgets that belong to the DAPM widget list.
+        * This is not sufficient to handle loopback cases between pipelines configured with
+        * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
+        * protection module.
+        */
+       list_for_each_entry(sroute, &sdev->route_list, list) {
+               bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
+               struct snd_sof_widget *swidget;
+
+               if (sroute->setup)
+                       continue;
+
+               src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
+               sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
+
+               /*
+                * if both source and sink are in the DAPM list, the route must already have been
+                * set up above. And if neither are in the DAPM list, the route shouldn't be
+                * handled now.
+                */
+               if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
+                       continue;
+
+               /*
+                * At this point either the source widget or the sink widget is in the DAPM list
+                * with a route that might need to be set up. Check the use_count of the widget
+                * that is not in the DAPM list to confirm if it is in use currently before setting
+                * up the route.
+                */
+               if (src_widget_in_dapm_list)
+                       swidget = sroute->sink_widget;
+               else
+                       swidget = sroute->src_widget;
+
+               mutex_lock(&swidget->setup_mutex);
+               if (!swidget->use_count) {
+                       mutex_unlock(&swidget->setup_mutex);
+                       continue;
+               }
+
+               if (tplg_ops && tplg_ops->route_setup) {
+                       /*
+                        * this route will get freed when either the source widget or the sink
+                        * widget is freed during hw_free
+                        */
+                       ret = tplg_ops->route_setup(sdev, sroute);
+                       if (!ret)
+                               sroute->setup = true;
+               }
+
+               mutex_unlock(&swidget->setup_mutex);
+
+               if (ret < 0)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -629,7 +688,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
                struct snd_sof_widget *pipe_widget;
                struct snd_sof_pipeline *spipe;
 
-               if (!swidget)
+               if (!swidget || sdev->dspless_mode_selected)
                        continue;
 
                spipe = swidget->spipe;
@@ -746,16 +805,22 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
        const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
        int ret;
 
-       /* Send PCM_FREE IPC to reset pipeline */
-       if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
-               ret = pcm_ops->hw_free(sdev->component, substream);
-               if (ret < 0)
-                       return ret;
-       }
+       if (spcm->prepared[substream->stream]) {
+               /* stop DMA first if needed */
+               if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+                       snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
 
-       spcm->prepared[substream->stream] = false;
+               /* Send PCM_FREE IPC to reset pipeline */
+               if (pcm_ops && pcm_ops->hw_free) {
+                       ret = pcm_ops->hw_free(sdev->component, substream);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               spcm->prepared[substream->stream] = false;
+       }
 
-       /* stop the DMA */
+       /* reset the DMA */
        ret = snd_sof_pcm_platform_hw_free(sdev, substream);
        if (ret < 0)
                return ret;
index e0579af9d281543c66465c8319364c39976c8826..a090a9eb482805e7e03b892132025f2574ea1442 100644 (file)
@@ -30,9 +30,9 @@
  */
 #define SOF_WIDGET_MAX_NUM_PINS        8
 
-/* The type of a widget pin is either sink or source */
-#define SOF_PIN_TYPE_SINK      0
-#define SOF_PIN_TYPE_SOURCE    1
+/* Widget pin type */
+#define SOF_PIN_TYPE_INPUT     0
+#define SOF_PIN_TYPE_OUTPUT    1
 
 /* max number of FE PCMs before BEs */
 #define SOF_BE_PCM_BASE                16
@@ -104,6 +104,15 @@ struct snd_sof_dai_config_data {
  * @pcm_free: Function pointer for PCM free that can be used for freeing any
  *            additional memory in the SOF PCM stream structure
  * @delay: Function pointer for pcm delay calculation
+ * @reset_hw_params_during_stop: Flag indicating whether the hw_params should be reset during the
+ *                              STOP pcm trigger
+ * @ipc_first_on_start: Send IPC before invoking platform trigger during
+ *                             START/PAUSE_RELEASE triggers
+ * @platform_stop_during_hw_free: Invoke the platform trigger during hw_free. This is needed for
+ *                               IPC4 where a pipeline is only paused during stop/pause/suspend
+ *                               triggers. The FW keeps the host DMA running in this case and
+ *                               therefore the host must do the same and should stop the DMA during
+ *                               hw_free.
  */
 struct sof_ipc_pcm_ops {
        int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -117,6 +126,9 @@ struct sof_ipc_pcm_ops {
        void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
        snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
                                   struct snd_pcm_substream *substream);
+       bool reset_hw_params_during_stop;
+       bool ipc_first_on_start;
+       bool platform_stop_during_hw_free;
 };
 
 /**
@@ -256,8 +268,7 @@ enum sof_tokens {
        SOF_COMP_EXT_TOKENS,
        SOF_IN_AUDIO_FORMAT_TOKENS,
        SOF_OUT_AUDIO_FORMAT_TOKENS,
-       SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
-       SOF_COPIER_GATEWAY_CFG_TOKENS,
+       SOF_COPIER_DEEP_BUFFER_TOKENS,
        SOF_COPIER_TOKENS,
        SOF_AUDIO_FMT_NUM_TOKENS,
        SOF_COPIER_FORMAT_TOKENS,
@@ -433,31 +444,31 @@ struct snd_sof_widget {
        struct snd_sof_tuple *tuples;
 
        /*
-        * The allowed range for num_sink/source_pins is [0, SOF_WIDGET_MAX_NUM_PINS].
-        * Widgets may have zero sink or source pins, for example the tone widget has
-        * zero sink pins.
+        * The allowed range for num_input/output_pins is [0, SOF_WIDGET_MAX_NUM_PINS].
+        * Widgets may have zero input or output pins, for example the tone widget has
+        * zero input pins.
         */
-       u32 num_sink_pins;
-       u32 num_source_pins;
+       u32 num_input_pins;
+       u32 num_output_pins;
 
        /*
-        * The sink/source pin binding array, it takes the form of
+        * The input/output pin binding array, it takes the form of
         * [widget_name_connected_to_pin0, widget_name_connected_to_pin1, ...],
         * with the index as the queue ID.
         *
         * The array is used for special pin binding. Note that even if there
-        * is only one sink/source pin requires special pin binding, pin binding
-        * should be defined for all sink/source pins in topology, for pin(s) that
+        * is only one input/output pin requires special pin binding, pin binding
+        * should be defined for all input/output pins in topology, for pin(s) that
         * are not used, give the value "NotConnected".
         *
         * If pin binding is not defined in topology, nothing to parse in the kernel,
-        * sink_pin_binding and src_pin_binding shall be NULL.
+        * input_pin_binding and output_pin_binding shall be NULL.
         */
-       char **sink_pin_binding;
-       char **src_pin_binding;
+       char **input_pin_binding;
+       char **output_pin_binding;
 
-       struct ida src_queue_ida;
-       struct ida sink_queue_ida;
+       struct ida output_queue_ida;
+       struct ida input_queue_ida;
 
        void *private;          /* core does not touch this */
 };
@@ -502,6 +513,8 @@ struct snd_sof_dai {
        int number_configs;
        int current_config;
        struct list_head list;  /* list in sdev dai list */
+       /* core should not touch this */
+       const void *platform_private;
        void *private;
 };
 
index 4bdecd80248ac1871b8230912c9b8fbd4145cbe5..c0d6723aed59cd307b8e49ebacb02f733ba9df15 100644 (file)
@@ -64,7 +64,6 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev,
        struct sof_ipc_flood_priv *priv = cdev->data;
        struct device *dev = &cdev->auxdev.dev;
        struct sof_ipc_cmd_hdr hdr;
-       struct sof_ipc_reply reply;
        u64 min_response_time = U64_MAX;
        ktime_t start, end, test_end;
        u64 avg_response_time = 0;
@@ -84,7 +83,7 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev,
        /* send test IPC's */
        while (1) {
                start = ktime_get();
-               ret = sof_client_ipc_tx_message(cdev, &hdr, &reply, sizeof(reply));
+               ret = sof_client_ipc_tx_message_no_reply(cdev, &hdr);
                end = ktime_get();
 
                if (ret < 0)
index ef768db5f04d58311c14289ca371377680e4976d..5e8eb19582a86dd5b3208b2e39e4f58cc0930efd 100644 (file)
@@ -65,7 +65,6 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
 {
        struct sof_ipc_probe_dma_add_params *msg;
        size_t size = struct_size(msg, dma, 1);
-       struct sof_ipc_reply reply;
        int ret;
 
        msg = kmalloc(size, GFP_KERNEL);
@@ -77,7 +76,7 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
        msg->dma[0].stream_tag = stream_tag;
        msg->dma[0].dma_buffer_size = buffer_size;
 
-       ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+       ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
        kfree(msg);
        return ret;
 }
@@ -93,12 +92,11 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
 static int ipc3_probes_deinit(struct sof_client_dev *cdev)
 {
        struct sof_ipc_cmd_hdr msg;
-       struct sof_ipc_reply reply;
 
        msg.size = sizeof(msg);
        msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
 
-       return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
+       return sof_client_ipc_tx_message_no_reply(cdev, &msg);
 }
 
 static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
@@ -180,7 +178,6 @@ static int ipc3_probes_points_add(struct sof_client_dev *cdev,
 {
        struct sof_ipc_probe_point_add_params *msg;
        size_t size = struct_size(msg, desc, num_desc);
-       struct sof_ipc_reply reply;
        int ret;
 
        msg = kmalloc(size, GFP_KERNEL);
@@ -191,7 +188,7 @@ static int ipc3_probes_points_add(struct sof_client_dev *cdev,
        msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
        memcpy(&msg->desc[0], desc, size - sizeof(*msg));
 
-       ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+       ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
        kfree(msg);
        return ret;
 }
@@ -211,7 +208,6 @@ static int ipc3_probes_points_remove(struct sof_client_dev *cdev,
 {
        struct sof_ipc_probe_point_remove_params *msg;
        size_t size = struct_size(msg, buffer_id, num_buffer_id);
-       struct sof_ipc_reply reply;
        int ret;
 
        msg = kmalloc(size, GFP_KERNEL);
@@ -222,7 +218,7 @@ static int ipc3_probes_points_remove(struct sof_client_dev *cdev,
        msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
        memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
 
-       ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+       ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
        kfree(msg);
        return ret;
 }
index 66fa7c2f390a6b524c3ce49dbf9f478836021dda..ea21ef176c42b7e23425d701507ba1495ba303d5 100644 (file)
@@ -129,7 +129,7 @@ static int ipc4_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
        msg.data_size = sizeof(cfg);
        msg.data_ptr = &cfg;
 
-       return sof_client_ipc_tx_message(cdev, &msg, NULL, 0);
+       return sof_client_ipc_tx_message_no_reply(cdev, &msg);
 }
 
 /**
@@ -156,7 +156,7 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev)
        msg.data_size = 0;
        msg.data_ptr = NULL;
 
-       return sof_client_ipc_tx_message(cdev, &msg, NULL, 0);
+       return sof_client_ipc_tx_message_no_reply(cdev, &msg);
 }
 
 /**
index 9017f0864cdda3921f3c80c62dbc6fd2b2054c7c..d6b7caa0cf03172204719cf59211d04b851cbff0 100644 (file)
@@ -130,6 +130,9 @@ int sof_register_clients(struct snd_sof_dev *sdev)
 {
        int ret;
 
+       if (sdev->dspless_mode_selected)
+               return 0;
+
        /* Register platform independent client devices */
        ret = sof_register_ipc_flood_test(sdev);
        if (ret) {
index 2589714eaa9183777a28691777d09e840ab73a0b..10571d1ea9a772c2d9add6dde2f71cf421af6d50 100644 (file)
@@ -39,6 +39,10 @@ struct sof_client_dev {
 
 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
                              void *reply_data, size_t reply_bytes);
+static inline int sof_client_ipc_tx_message_no_reply(struct sof_client_dev *cdev, void *ipc_msg)
+{
+       return sof_client_ipc_tx_message(cdev, ipc_msg, NULL, 0);
+}
 int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
                                bool set);
 
index 5f919162a555255fb377bd8367a9b4fb7de49794..cd4f6ac126eca79671423fb340c1f3dbabe9f284 100644 (file)
@@ -48,6 +48,7 @@ struct snd_sof_pcm_stream;
 #define SOF_DBG_FORCE_NOCODEC                  BIT(10) /* ignore all codec-related
                                                         * configurations
                                                         */
+#define SOF_DBG_DSPLESS_MODE                   BIT(15) /* Do not initialize and use the DSP */
 
 /* Flag definitions used for controlling the DSP dump behavior */
 #define SOF_DBG_DUMP_REGS              BIT(0)
@@ -528,6 +529,16 @@ struct snd_sof_dev {
        spinlock_t ipc_lock;    /* lock for IPC users */
        spinlock_t hw_lock;     /* lock for HW IO access */
 
+       /*
+        * When true the DSP is not used.
+        * It is set under the following condition:
+        * User sets the SOF_DBG_DSPLESS_MODE flag in sof_debug module parameter
+        * and
+        * the platform advertises that it can support such mode
+        * pdata->desc->dspless_mode_supported is true.
+        */
+       bool dspless_mode_selected;
+
        /* Main, Base firmware image */
        struct sof_firmware basefw;
 
@@ -700,10 +711,20 @@ static inline void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
 }
 int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
                       void *reply_data, size_t reply_bytes);
+static inline int sof_ipc_tx_message_no_reply(struct snd_sof_ipc *ipc, void *msg_data,
+                                             size_t msg_bytes)
+{
+       return sof_ipc_tx_message(ipc, msg_data, msg_bytes, NULL, 0);
+}
 int sof_ipc_set_get_data(struct snd_sof_ipc *ipc, void *msg_data,
                         size_t msg_bytes, bool set);
 int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
                             void *reply_data, size_t reply_bytes);
+static inline int sof_ipc_tx_message_no_pm_no_reply(struct snd_sof_ipc *ipc, void *msg_data,
+                                                   size_t msg_bytes)
+{
+       return sof_ipc_tx_message_no_pm(ipc, msg_data, msg_bytes, NULL, 0);
+}
 int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
                     size_t reply_bytes);
 
index 9f3a038fe21add2ae6ebbe02a41a029f9bdf8eac..d3d536b0a8f54d8516d360e0c0b80f7cc8fcc022 100644 (file)
@@ -416,19 +416,19 @@ static const struct sof_topology_token led_tokens[] = {
 };
 
 static const struct sof_topology_token comp_pin_tokens[] = {
-       {SOF_TKN_COMP_NUM_SINK_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct snd_sof_widget, num_sink_pins)},
-       {SOF_TKN_COMP_NUM_SOURCE_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
-               offsetof(struct snd_sof_widget, num_source_pins)},
+       {SOF_TKN_COMP_NUM_INPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct snd_sof_widget, num_input_pins)},
+       {SOF_TKN_COMP_NUM_OUTPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+               offsetof(struct snd_sof_widget, num_output_pins)},
 };
 
-static const struct sof_topology_token comp_sink_pin_binding_tokens[] = {
-       {SOF_TKN_COMP_SINK_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+static const struct sof_topology_token comp_input_pin_binding_tokens[] = {
+       {SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
                get_token_string, 0},
 };
 
-static const struct sof_topology_token comp_src_pin_binding_tokens[] = {
-       {SOF_TKN_COMP_SRC_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+static const struct sof_topology_token comp_output_pin_binding_tokens[] = {
+       {SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
                get_token_string, 0},
 };
 
@@ -1144,8 +1144,12 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
 static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
                     int dir)
 {
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_sof_widget *host_widget;
 
+       if (sdev->dspless_mode_selected)
+               return 0;
+
        host_widget = snd_sof_find_swidget_sname(scomp,
                                                 spcm->pcm.caps[dir].name,
                                                 dir);
@@ -1231,37 +1235,43 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
 
                        continue;
                case SOF_IN_AUDIO_FORMAT_TOKENS:
-               case SOF_OUT_AUDIO_FORMAT_TOKENS:
-               case SOF_COPIER_GATEWAY_CFG_TOKENS:
-               case SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS:
-                       num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_AUDIO_FORMATS,
+                       num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS,
                                                       swidget->tuples, swidget->num_tuples);
-
                        if (num_sets < 0) {
-                               dev_err(sdev->dev, "Invalid audio format count for %s\n",
+                               dev_err(sdev->dev, "Invalid input audio format count for %s\n",
                                        swidget->widget->name);
                                ret = num_sets;
                                goto err;
                        }
-
-                       if (num_sets > 1) {
-                               struct snd_sof_tuple *new_tuples;
-
-                               num_tuples += token_list[object_token_list[i]].count * num_sets;
-                               new_tuples = krealloc(swidget->tuples,
-                                                     sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
-                               if (!new_tuples) {
-                                       ret = -ENOMEM;
-                                       goto err;
-                               }
-
-                               swidget->tuples = new_tuples;
+                       break;
+               case SOF_OUT_AUDIO_FORMAT_TOKENS:
+                       num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS,
+                                                      swidget->tuples, swidget->num_tuples);
+                       if (num_sets < 0) {
+                               dev_err(sdev->dev, "Invalid output audio format count for %s\n",
+                                       swidget->widget->name);
+                               ret = num_sets;
+                               goto err;
                        }
                        break;
                default:
                        break;
                }
 
+               if (num_sets > 1) {
+                       struct snd_sof_tuple *new_tuples;
+
+                       num_tuples += token_list[object_token_list[i]].count * num_sets;
+                       new_tuples = krealloc(swidget->tuples,
+                                             sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
+                       if (!new_tuples) {
+                               ret = -ENOMEM;
+                               goto err;
+                       }
+
+                       swidget->tuples = new_tuples;
+               }
+
                /* copy one set of tuples per token ID into swidget->tuples */
                ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
                                      object_token_list[i], num_sets, swidget->tuples,
@@ -1286,12 +1296,12 @@ static void sof_free_pin_binding(struct snd_sof_widget *swidget,
        u32 num_pins;
        int i;
 
-       if (pin_type == SOF_PIN_TYPE_SINK) {
-               pin_binding = swidget->sink_pin_binding;
-               num_pins = swidget->num_sink_pins;
+       if (pin_type == SOF_PIN_TYPE_INPUT) {
+               pin_binding = swidget->input_pin_binding;
+               num_pins = swidget->num_input_pins;
        } else {
-               pin_binding = swidget->src_pin_binding;
-               num_pins = swidget->num_source_pins;
+               pin_binding = swidget->output_pin_binding;
+               num_pins = swidget->num_output_pins;
        }
 
        if (pin_binding) {
@@ -1313,14 +1323,14 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
        int ret;
        int i;
 
-       if (pin_type == SOF_PIN_TYPE_SINK) {
-               num_pins = swidget->num_sink_pins;
-               pin_binding_token = comp_sink_pin_binding_tokens;
-               token_count = ARRAY_SIZE(comp_sink_pin_binding_tokens);
+       if (pin_type == SOF_PIN_TYPE_INPUT) {
+               num_pins = swidget->num_input_pins;
+               pin_binding_token = comp_input_pin_binding_tokens;
+               token_count = ARRAY_SIZE(comp_input_pin_binding_tokens);
        } else {
-               num_pins = swidget->num_source_pins;
-               pin_binding_token = comp_src_pin_binding_tokens;
-               token_count = ARRAY_SIZE(comp_src_pin_binding_tokens);
+               num_pins = swidget->num_output_pins;
+               pin_binding_token = comp_output_pin_binding_tokens;
+               token_count = ARRAY_SIZE(comp_output_pin_binding_tokens);
        }
 
        memset(pin_binding, 0, SOF_WIDGET_MAX_NUM_PINS * sizeof(char *));
@@ -1337,10 +1347,10 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
                        ret = -ENOMEM;
                        goto err;
                }
-               if (pin_type == SOF_PIN_TYPE_SINK)
-                       swidget->sink_pin_binding = pb;
+               if (pin_type == SOF_PIN_TYPE_INPUT)
+                       swidget->input_pin_binding = pb;
                else
-                       swidget->src_pin_binding = pb;
+                       swidget->output_pin_binding = pb;
        }
 
        return 0;
@@ -1379,8 +1389,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
        swidget->private = NULL;
        mutex_init(&swidget->setup_mutex);
 
-       ida_init(&swidget->src_queue_ida);
-       ida_init(&swidget->sink_queue_ida);
+       ida_init(&swidget->output_queue_ida);
+       ida_init(&swidget->input_queue_ida);
 
        ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens,
                               ARRAY_SIZE(comp_pin_tokens), priv->array,
@@ -1391,29 +1401,29 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
                goto widget_free;
        }
 
-       if (swidget->num_sink_pins > SOF_WIDGET_MAX_NUM_PINS ||
-           swidget->num_source_pins > SOF_WIDGET_MAX_NUM_PINS) {
-               dev_err(scomp->dev, "invalid pins for %s: [sink: %d, src: %d]\n",
-                       swidget->widget->name, swidget->num_sink_pins, swidget->num_source_pins);
+       if (swidget->num_input_pins > SOF_WIDGET_MAX_NUM_PINS ||
+           swidget->num_output_pins > SOF_WIDGET_MAX_NUM_PINS) {
+               dev_err(scomp->dev, "invalid pins for %s: [input: %d, output: %d]\n",
+                       swidget->widget->name, swidget->num_input_pins, swidget->num_output_pins);
                ret = -EINVAL;
                goto widget_free;
        }
 
-       if (swidget->num_sink_pins > 1) {
-               ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SINK);
+       if (swidget->num_input_pins > 1) {
+               ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_INPUT);
                /* on parsing error, pin binding is not allocated, nothing to free. */
                if (ret < 0) {
-                       dev_err(scomp->dev, "failed to parse sink pin binding for %s\n",
+                       dev_err(scomp->dev, "failed to parse input pin binding for %s\n",
                                w->name);
                        goto widget_free;
                }
        }
 
-       if (swidget->num_source_pins > 1) {
-               ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SOURCE);
+       if (swidget->num_output_pins > 1) {
+               ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_OUTPUT);
                /* on parsing error, pin binding is not allocated, nothing to free. */
                if (ret < 0) {
-                       dev_err(scomp->dev, "failed to parse source pin binding for %s\n",
+                       dev_err(scomp->dev, "failed to parse output pin binding for %s\n",
                                w->name);
                        goto widget_free;
                }
@@ -1422,7 +1432,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
        dev_dbg(scomp->dev,
                "tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n",
                swidget->comp_id, w->name, swidget->id, index,
-               swidget->num_sink_pins, swidget->num_source_pins,
+               swidget->num_input_pins, swidget->num_output_pins,
                strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none");
 
        widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -1643,11 +1653,11 @@ out:
        if (widget_ops && widget_ops[swidget->id].ipc_free)
                widget_ops[swidget->id].ipc_free(swidget);
 
-       ida_destroy(&swidget->src_queue_ida);
-       ida_destroy(&swidget->sink_queue_ida);
+       ida_destroy(&swidget->output_queue_ida);
+       ida_destroy(&swidget->input_queue_ida);
 
-       sof_free_pin_binding(swidget, SOF_PIN_TYPE_SINK);
-       sof_free_pin_binding(swidget, SOF_PIN_TYPE_SOURCE);
+       sof_free_pin_binding(swidget, SOF_PIN_TYPE_INPUT);
+       sof_free_pin_binding(swidget, SOF_PIN_TYPE_OUTPUT);
 
        kfree(swidget->tuples);
 
@@ -2120,7 +2130,6 @@ static int sof_complete(struct snd_soc_component *scomp)
 {
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
-       struct snd_sof_widget *swidget, *comp_swidget;
        const struct sof_ipc_tplg_widget_ops *widget_ops;
        struct snd_sof_control *scontrol;
        struct snd_sof_pipeline *spipe;
@@ -2139,37 +2148,38 @@ static int sof_complete(struct snd_soc_component *scomp)
                        }
                }
 
-       /*
-        * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the
-        * topology will be removed and all widgets will be unloaded resulting in freeing all
-        * associated memories.
-        */
-       list_for_each_entry(swidget, &sdev->widget_list, list) {
-               if (widget_ops && widget_ops[swidget->id].ipc_setup) {
-                       ret = widget_ops[swidget->id].ipc_setup(swidget);
+       /* set up the IPC structures for the pipeline widgets */
+       list_for_each_entry(spipe, &sdev->pipeline_list, list) {
+               struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+               struct snd_sof_widget *swidget;
+
+               /* Update the scheduler widget's IPC structure */
+               if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) {
+                       ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget);
                        if (ret < 0) {
                                dev_err(sdev->dev, "failed updating IPC struct for %s\n",
-                                       swidget->widget->name);
+                                       pipe_widget->widget->name);
                                return ret;
                        }
                }
-       }
-
-       /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
-       list_for_each_entry(spipe, &sdev->pipeline_list, list) {
-               struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
 
-               /*
-                * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
-                * for all widgets that have the same pipeline ID as the scheduler widget.
-                * Skip the scheduler widgets as they have their pipeline set during widget_ready
-                */
-               list_for_each_entry(comp_swidget, &sdev->widget_list, list)
-                       if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
-                           comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
-                               ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
+               /* set the pipeline and update the IPC structure for the non scheduler widgets */
+               list_for_each_entry(swidget, &sdev->widget_list, list)
+                       if (swidget->widget->id != snd_soc_dapm_scheduler &&
+                           swidget->pipeline_id == pipe_widget->pipeline_id) {
+                               ret = sof_set_widget_pipeline(sdev, spipe, swidget);
                                if (ret < 0)
                                        return ret;
+
+                               if (widget_ops && widget_ops[swidget->id].ipc_setup) {
+                                       ret = widget_ops[swidget->id].ipc_setup(swidget);
+                                       if (ret < 0) {
+                                               dev_err(sdev->dev,
+                                                       "failed updating IPC struct for %s\n",
+                                                       swidget->widget->name);
+                                               return ret;
+                                       }
+                               }
                        }
        }
 
@@ -2264,6 +2274,126 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
        .bytes_ext_ops_count    = ARRAY_SIZE(sof_bytes_ext_ops),
 };
 
+static int snd_sof_dspless_kcontrol(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       return 0;
+}
+
+static const struct snd_soc_tplg_kcontrol_ops sof_dspless_io_ops[] = {
+       {SOF_TPLG_KCTL_VOL_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+       {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+       {SOF_TPLG_KCTL_ENUM_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+       {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+};
+
+static int snd_sof_dspless_bytes_ext_get(struct snd_kcontrol *kcontrol,
+                                        unsigned int __user *binary_data,
+                                        unsigned int size)
+{
+       return 0;
+}
+
+static int snd_sof_dspless_bytes_ext_put(struct snd_kcontrol *kcontrol,
+                                        const unsigned int __user *binary_data,
+                                        unsigned int size)
+{
+       return 0;
+}
+
+static const struct snd_soc_tplg_bytes_ext_ops sof_dspless_bytes_ext_ops[] = {
+       {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_bytes_ext_get, snd_sof_dspless_bytes_ext_put},
+       {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_dspless_bytes_ext_get},
+};
+
+/* external widget init - used for any driver specific init */
+static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
+                                   struct snd_soc_dapm_widget *w,
+                                   struct snd_soc_tplg_dapm_widget *tw)
+{
+       if (WIDGET_IS_DAI(w->id)) {
+               struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+               struct snd_sof_widget *swidget;
+               struct snd_sof_dai dai;
+               int ret;
+
+               swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
+               if (!swidget)
+                       return -ENOMEM;
+
+               memset(&dai, 0, sizeof(dai));
+
+               ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+               if (ret) {
+                       kfree(swidget);
+                       return ret;
+               }
+
+               swidget->scomp = scomp;
+               swidget->widget = w;
+               mutex_init(&swidget->setup_mutex);
+               w->dobj.private = swidget;
+               list_add(&swidget->list, &sdev->widget_list);
+       }
+
+       return 0;
+}
+
+static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
+                                    struct snd_soc_dobj *dobj)
+{
+       struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+
+       if (WIDGET_IS_DAI(w->id)) {
+               struct snd_sof_widget *swidget = dobj->private;
+
+               sof_disconnect_dai_widget(scomp, w);
+
+               if (!swidget)
+                       return 0;
+
+               /* remove and free swidget object */
+               list_del(&swidget->list);
+               kfree(swidget);
+       }
+
+       return 0;
+}
+
+static int sof_dspless_link_load(struct snd_soc_component *scomp, int index,
+                                struct snd_soc_dai_link *link,
+                                struct snd_soc_tplg_link_config *cfg)
+{
+       link->platforms->name = dev_name(scomp->dev);
+
+       /* Set nonatomic property for FE dai links for FE-BE compatibility */
+       if (!link->no_pcm)
+               link->nonatomic = true;
+
+       return 0;
+}
+
+static struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
+       /* external widget init - used for any driver specific init */
+       .widget_ready   = sof_dspless_widget_ready,
+       .widget_unload  = sof_dspless_widget_unload,
+
+       /* FE DAI - used for any driver specific init */
+       .dai_load       = sof_dai_load,
+       .dai_unload     = sof_dai_unload,
+
+       /* DAI link - used for any driver specific init */
+       .link_load      = sof_dspless_link_load,
+
+       /* vendor specific kcontrol handlers available for binding */
+       .io_ops         = sof_dspless_io_ops,
+       .io_ops_count   = ARRAY_SIZE(sof_dspless_io_ops),
+
+       /* vendor specific bytes ext handlers available for binding */
+       .bytes_ext_ops = sof_dspless_bytes_ext_ops,
+       .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops),
+};
+
 int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
 {
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
@@ -2281,7 +2411,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
                return ret;
        }
 
-       ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+       if (sdev->dspless_mode_selected)
+               ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
+       else
+               ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+
        if (ret < 0) {
                dev_err(scomp->dev, "error: tplg component load failed %d\n",
                        ret);
index f6a55fa60c1bc0cc4e8c340eaf9631a6b2c7b161..688419c6b0927b49830c708a559f3407004ff2c9 100644 (file)
@@ -973,7 +973,7 @@ static int sprd_mcdt_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int sprd_mcdt_remove(struct platform_device *pdev)
+static void sprd_mcdt_remove(struct platform_device *pdev)
 {
        struct sprd_mcdt_chan *chan, *temp;
 
@@ -983,8 +983,6 @@ static int sprd_mcdt_remove(struct platform_device *pdev)
                list_del(&chan->list);
 
        mutex_unlock(&sprd_mcdt_list_mutex);
-
-       return 0;
 }
 
 static const struct of_device_id sprd_mcdt_of_match[] = {
@@ -995,7 +993,7 @@ MODULE_DEVICE_TABLE(of, sprd_mcdt_of_match);
 
 static struct platform_driver sprd_mcdt_driver = {
        .probe = sprd_mcdt_probe,
-       .remove = sprd_mcdt_remove,
+       .remove_new = sprd_mcdt_remove,
        .driver = {
                .name = "sprd-mcdt",
                .of_match_table = sprd_mcdt_of_match,
index 837c1848d9bffe0e92c341cb5b13aebb25cda405..a8fff73786413dcc2053a7b07601899fea6b4077 100644 (file)
@@ -386,12 +386,10 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int stm32_adfsdm_remove(struct platform_device *pdev)
+static void stm32_adfsdm_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static struct platform_driver stm32_adfsdm_driver = {
@@ -400,7 +398,7 @@ static struct platform_driver stm32_adfsdm_driver = {
                   .of_match_table = stm32_adfsdm_of_match,
                   },
        .probe = stm32_adfsdm_probe,
-       .remove = stm32_adfsdm_remove,
+       .remove_new = stm32_adfsdm_remove,
 };
 
 module_platform_driver(stm32_adfsdm_driver);
index f3dd9f8e621cc851259a3b7cdbfdc59f6ce77b60..3871307019604d04ca1d0b69963778a5f1adb126 100644 (file)
@@ -1066,7 +1066,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
                                     "Could not get x11k parent clock\n");
 
        /* Register mclk provider if requested */
-       if (of_find_property(np, "#clock-cells", NULL)) {
+       if (of_property_present(np, "#clock-cells")) {
                ret = stm32_i2s_add_mclk_provider(i2s);
                if (ret < 0)
                        return ret;
@@ -1097,13 +1097,11 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
        return 0;
 }
 
-static int stm32_i2s_remove(struct platform_device *pdev)
+static void stm32_i2s_remove(struct platform_device *pdev)
 {
        snd_dmaengine_pcm_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static int stm32_i2s_probe(struct platform_device *pdev)
@@ -1221,7 +1219,7 @@ static struct platform_driver stm32_i2s_driver = {
                .pm = &stm32_i2s_pm_ops,
        },
        .probe = stm32_i2s_probe,
-       .remove = stm32_i2s_remove,
+       .remove_new = stm32_i2s_remove,
 };
 
 module_platform_driver(stm32_i2s_driver);
index eb31b49e659785bd0087e940dbfd1a6e2363de8e..f6695dee353b137ccb3089775a7b6823adbd4202 100644 (file)
@@ -1394,7 +1394,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
 
        /* Get spdif iec60958 property */
        sai->spdif = false;
-       if (of_get_property(np, "st,iec60958", NULL)) {
+       if (of_property_present(np, "st,iec60958")) {
                if (!STM_SAI_HAS_SPDIF(sai) ||
                    sai->dir == SNDRV_PCM_STREAM_CAPTURE) {
                        dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n");
@@ -1480,7 +1480,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
                return 0;
 
        /* Register mclk provider if requested */
-       if (of_find_property(np, "#clock-cells", NULL)) {
+       if (of_property_present(np, "#clock-cells")) {
                ret = stm32_sai_add_mclk_provider(sai);
                if (ret < 0)
                        return ret;
@@ -1559,7 +1559,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int stm32_sai_sub_remove(struct platform_device *pdev)
+static void stm32_sai_sub_remove(struct platform_device *pdev)
 {
        struct stm32_sai_sub_data *sai = dev_get_drvdata(&pdev->dev);
 
@@ -1567,8 +1567,6 @@ static int stm32_sai_sub_remove(struct platform_device *pdev)
        snd_dmaengine_pcm_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1618,7 +1616,7 @@ static struct platform_driver stm32_sai_sub_driver = {
                .pm = &stm32_sai_sub_pm_ops,
        },
        .probe = stm32_sai_sub_probe,
-       .remove = stm32_sai_sub_remove,
+       .remove_new = stm32_sai_sub_remove,
 };
 
 module_platform_driver(stm32_sai_sub_driver);
index d399c906bb921a767b73d19e3cdf41ae9728336b..a4066f271f2dbf8969595004120c7e7779f1a219 100644 (file)
@@ -939,7 +939,7 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev,
        return 0;
 }
 
-static int stm32_spdifrx_remove(struct platform_device *pdev)
+static void stm32_spdifrx_remove(struct platform_device *pdev)
 {
        struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev);
 
@@ -952,8 +952,6 @@ static int stm32_spdifrx_remove(struct platform_device *pdev)
        snd_dmaengine_pcm_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static int stm32_spdifrx_probe(struct platform_device *pdev)
@@ -1078,7 +1076,7 @@ static struct platform_driver stm32_spdifrx_driver = {
                .pm = &stm32_spdifrx_pm_ops,
        },
        .probe = stm32_spdifrx_probe,
-       .remove = stm32_spdifrx_remove,
+       .remove_new = stm32_spdifrx_remove,
 };
 
 module_platform_driver(stm32_spdifrx_driver);
index 835dc34043670ba2e76bb5c6642739cde7a30c27..55328850aef546a459c7f5927f0925f282a2438c 100644 (file)
@@ -1821,7 +1821,7 @@ err_clk_disable:
        return ret;
 }
 
-static int sun4i_codec_remove(struct platform_device *pdev)
+static void sun4i_codec_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
@@ -1830,8 +1830,6 @@ static int sun4i_codec_remove(struct platform_device *pdev)
        if (scodec->rst)
                reset_control_assert(scodec->rst);
        clk_disable_unprepare(scodec->clk_apb);
-
-       return 0;
 }
 
 static struct platform_driver sun4i_codec_driver = {
@@ -1840,7 +1838,7 @@ static struct platform_driver sun4i_codec_driver = {
                .of_match_table = sun4i_codec_of_match,
        },
        .probe = sun4i_codec_probe,
-       .remove = sun4i_codec_remove,
+       .remove_new = sun4i_codec_remove,
 };
 module_platform_driver(sun4i_codec_driver);
 
index 6028871825baea607ce95523417fb377b964fac9..669d712bbe9f771538850dc3ede004c1bcdd885c 100644 (file)
@@ -1606,7 +1606,7 @@ err_pm_disable:
        return ret;
 }
 
-static int sun4i_i2s_remove(struct platform_device *pdev)
+static void sun4i_i2s_remove(struct platform_device *pdev)
 {
        struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev);
 
@@ -1616,8 +1616,6 @@ static int sun4i_i2s_remove(struct platform_device *pdev)
 
        if (!IS_ERR(i2s->rst))
                reset_control_assert(i2s->rst);
-
-       return 0;
 }
 
 static const struct of_device_id sun4i_i2s_match[] = {
@@ -1660,7 +1658,7 @@ static const struct dev_pm_ops sun4i_i2s_pm_ops = {
 
 static struct platform_driver sun4i_i2s_driver = {
        .probe  = sun4i_i2s_probe,
-       .remove = sun4i_i2s_remove,
+       .remove_new = sun4i_i2s_remove,
        .driver = {
                .name           = "sun4i-i2s",
                .of_match_table = sun4i_i2s_match,
index bcceebca915ac80663e3ccbbc8077503f3c6b574..ff18d4113aac0823e1a0c9c488912e724199f16d 100644 (file)
@@ -703,13 +703,11 @@ err_unregister:
        return ret;
 }
 
-static int sun4i_spdif_remove(struct platform_device *pdev)
+static void sun4i_spdif_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun4i_spdif_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops sun4i_spdif_pm = {
@@ -724,7 +722,7 @@ static struct platform_driver sun4i_spdif_driver = {
                .pm     = &sun4i_spdif_pm,
        },
        .probe          = sun4i_spdif_probe,
-       .remove         = sun4i_spdif_remove,
+       .remove_new     = sun4i_spdif_remove,
 };
 
 module_platform_driver(sun4i_spdif_driver);
index 069c993acb316bddaf6a36be468351e5bf2565c4..c10439b9e0a26c6b1f2302c391cfdd54afb60136 100644 (file)
@@ -373,13 +373,11 @@ err_disable_runtime_pm:
        return ret;
 }
 
-static int sun50i_dmic_remove(struct platform_device *pdev)
+static void sun50i_dmic_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun50i_dmic_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops sun50i_dmic_pm = {
@@ -394,7 +392,7 @@ static struct platform_driver sun50i_dmic_driver = {
                .pm     = &sun50i_dmic_pm,
        },
        .probe          = sun50i_dmic_probe,
-       .remove         = sun50i_dmic_remove,
+       .remove_new     = sun50i_dmic_remove,
 };
 
 module_platform_driver(sun50i_dmic_driver);
index 9844978d91e6e0182318ecd176ca4b0effa7e3d7..4c0d0d7d3e588a240bb5565240bc858e9437dc2b 100644 (file)
@@ -1349,13 +1349,11 @@ err_pm_disable:
        return ret;
 }
 
-static int sun8i_codec_remove(struct platform_device *pdev)
+static void sun8i_codec_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun8i_codec_runtime_suspend(&pdev->dev);
-
-       return 0;
 }
 
 static const struct sun8i_codec_quirks sun8i_a33_quirks = {
@@ -1385,7 +1383,7 @@ static struct platform_driver sun8i_codec_driver = {
                .pm = &sun8i_codec_pm_ops,
        },
        .probe = sun8i_codec_probe,
-       .remove = sun8i_codec_remove,
+       .remove_new = sun8i_codec_remove,
 };
 module_platform_driver(sun8i_codec_driver);
 
index 73a683d45526476448bd27d37d23daab94ae51cf..74effc57a7a06be099a0d865370b5881c1d9c937 100644 (file)
@@ -189,6 +189,15 @@ config SND_SOC_TEGRA_AUDIO_GRAPH_CARD
 config SND_SOC_TEGRA_MACHINE_DRV
        tristate
 
+config SND_SOC_TEGRA_RT5631
+       tristate "SoC Audio support for Tegra boards using an RT5631 codec"
+       depends on SND_SOC_TEGRA && I2C && GPIOLIB
+       select SND_SOC_TEGRA_MACHINE_DRV
+       select SND_SOC_RT5631
+       help
+         Say Y or M here if you want to add support for SoC audio on Tegra
+         boards using the RT5631 codec, such as Transformer.
+
 config SND_SOC_TEGRA_RT5640
        tristate "SoC Audio support for Tegra boards using an RT5640 codec"
        depends on I2C && GPIOLIB
@@ -254,6 +263,15 @@ config SND_SOC_TEGRA_MAX98090
          Say Y or M here if you want to add support for SoC audio on Tegra
          boards using the MAX98090 codec, such as Venice2.
 
+config SND_SOC_TEGRA_MAX98088
+       tristate "SoC Audio support for Tegra boards using a MAX9808x codec"
+       depends on I2C && GPIOLIB
+       select SND_SOC_TEGRA_MACHINE_DRV
+       select SND_SOC_MAX98088
+       help
+         Say Y or M here if you want to add support for SoC audio on Tegra
+         boards using the MAX98088 codec, such as LG X3.
+
 config SND_SOC_TEGRA_RT5677
        tristate "SoC Audio support for Tegra boards using a RT5677 codec"
        depends on I2C && GPIOLIB
index 9f12faaa609dda9e3ddea8824be6640078bc4d59..e016a6a7f7c44eaf9f29f44564990033f7023381 100644 (file)
@@ -1016,11 +1016,9 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra186_asrc_platform_remove(struct platform_device *pdev)
+static void tegra186_asrc_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra186_asrc_pm_ops = {
@@ -1037,7 +1035,7 @@ static struct platform_driver tegra186_asrc_driver = {
                .pm = &tegra186_asrc_pm_ops,
        },
        .probe = tegra186_asrc_platform_probe,
-       .remove = tegra186_asrc_platform_remove,
+       .remove_new = tegra186_asrc_platform_remove,
 };
 module_platform_driver(tegra186_asrc_driver)
 
index a74c980ee77539cd53dd8006eaea1487b8ecc653..a0ce7eb11de96bc931b62f077ecf5777627d6932 100644 (file)
@@ -524,11 +524,9 @@ static int tegra186_dspk_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra186_dspk_platform_remove(struct platform_device *pdev)
+static void tegra186_dspk_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra186_dspk_pm_ops = {
@@ -545,7 +543,7 @@ static struct platform_driver tegra186_dspk_driver = {
                .pm = &tegra186_dspk_pm_ops,
        },
        .probe = tegra186_dspk_platform_probe,
-       .remove = tegra186_dspk_platform_remove,
+       .remove_new = tegra186_dspk_platform_remove,
 };
 module_platform_driver(tegra186_dspk_driver);
 
index d23d88a1089961879706e497166f17f01dd4e163..c498145e76e05a6d874ae2ea23158652c9db5acb 100644 (file)
@@ -318,7 +318,8 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
        ac97->reset = devm_reset_control_get_exclusive(&pdev->dev, "ac97");
        if (IS_ERR(ac97->reset)) {
                dev_err(&pdev->dev, "Can't retrieve ac97 reset\n");
-               return PTR_ERR(ac97->reset);
+               ret = PTR_ERR(ac97->reset);
+               goto err;
        }
 
        ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
@@ -429,7 +430,7 @@ err:
        return ret;
 }
 
-static int tegra20_ac97_platform_remove(struct platform_device *pdev)
+static void tegra20_ac97_platform_remove(struct platform_device *pdev)
 {
        struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
 
@@ -439,8 +440,6 @@ static int tegra20_ac97_platform_remove(struct platform_device *pdev)
        clk_disable_unprepare(ac97->clk_ac97);
 
        snd_soc_set_ac97_ops(NULL);
-
-       return 0;
 }
 
 static const struct of_device_id tegra20_ac97_of_match[] = {
@@ -454,7 +453,7 @@ static struct platform_driver tegra20_ac97_driver = {
                .of_match_table = tegra20_ac97_of_match,
        },
        .probe = tegra20_ac97_platform_probe,
-       .remove = tegra20_ac97_platform_remove,
+       .remove_new = tegra20_ac97_platform_remove,
 };
 module_platform_driver(tegra20_ac97_driver);
 
index d37a9f2603e818259ddcc4524704a9f637129fbc..e1a0f50969c1e927dc3f190f7a5c238bbf541032 100644 (file)
@@ -475,13 +475,11 @@ err:
        return ret;
 }
 
-static int tegra20_i2s_platform_remove(struct platform_device *pdev)
+static void tegra20_i2s_platform_remove(struct platform_device *pdev)
 {
        tegra_pcm_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct of_device_id tegra20_i2s_of_match[] = {
@@ -503,7 +501,7 @@ static struct platform_driver tegra20_i2s_driver = {
                .pm = &tegra20_i2s_pm_ops,
        },
        .probe = tegra20_i2s_platform_probe,
-       .remove = tegra20_i2s_platform_remove,
+       .remove_new = tegra20_i2s_platform_remove,
 };
 module_platform_driver(tegra20_i2s_driver);
 
index 100a2b6e6063e459e4a65581d487cdf99cce643c..6868508585a0158f9a000d3e620d2a1294d61e67 100644 (file)
@@ -842,11 +842,9 @@ static int tegra_admaif_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra_admaif_remove(struct platform_device *pdev)
+static void tegra_admaif_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra_admaif_pm_ops = {
@@ -858,7 +856,7 @@ static const struct dev_pm_ops tegra_admaif_pm_ops = {
 
 static struct platform_driver tegra_admaif_driver = {
        .probe = tegra_admaif_probe,
-       .remove = tegra_admaif_remove,
+       .remove_new = tegra_admaif_remove,
        .driver = {
                .name = "tegra210-admaif",
                .of_match_table = tegra_admaif_of_match,
index 49691d2cce509fcd56848ff7853e3d46a3ef284d..41117c1d61fb352c1cfd9c20e7378be4dcca3bd5 100644 (file)
@@ -504,11 +504,9 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_adx_platform_remove(struct platform_device *pdev)
+static void tegra210_adx_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_adx_pm_ops = {
@@ -525,7 +523,7 @@ static struct platform_driver tegra210_adx_driver = {
                .pm = &tegra210_adx_pm_ops,
        },
        .probe = tegra210_adx_platform_probe,
-       .remove = tegra210_adx_platform_remove,
+       .remove_new = tegra210_adx_platform_remove,
 };
 module_platform_driver(tegra210_adx_driver);
 
index b38d205b69cc281351d920093bae60c0e6076c1d..8c00c09eeefbb95a7f991dd44b4afab805909bbf 100644 (file)
@@ -1410,11 +1410,9 @@ static int tegra_ahub_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra_ahub_remove(struct platform_device *pdev)
+static void tegra_ahub_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra_ahub_pm_ops = {
@@ -1426,7 +1424,7 @@ static const struct dev_pm_ops tegra_ahub_pm_ops = {
 
 static struct platform_driver tegra_ahub_driver = {
        .probe = tegra_ahub_probe,
-       .remove = tegra_ahub_remove,
+       .remove_new = tegra_ahub_remove,
        .driver = {
                .name = "tegra210-ahub",
                .of_match_table = tegra_ahub_of_match,
index d064cc67fea66654499d9e73e629463de38807e0..782a141b65c0c5d8f79b2d20bd59ebdb1b859257 100644 (file)
@@ -573,11 +573,9 @@ static int tegra210_amx_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_amx_platform_remove(struct platform_device *pdev)
+static void tegra210_amx_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_amx_pm_ops = {
@@ -594,7 +592,7 @@ static struct platform_driver tegra210_amx_driver = {
                .pm = &tegra210_amx_pm_ops,
        },
        .probe = tegra210_amx_platform_probe,
-       .remove = tegra210_amx_platform_remove,
+       .remove_new = tegra210_amx_platform_remove,
 };
 module_platform_driver(tegra210_amx_driver);
 
index db95794530f4678b322dabc1ab13dedb552acfea..763b206cd52ba2e7928e9032e6b8b350329b2db6 100644 (file)
@@ -534,11 +534,9 @@ static int tegra210_dmic_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_dmic_remove(struct platform_device *pdev)
+static void tegra210_dmic_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_dmic_pm_ops = {
@@ -561,7 +559,7 @@ static struct platform_driver tegra210_dmic_driver = {
                .pm = &tegra210_dmic_pm_ops,
        },
        .probe = tegra210_dmic_probe,
-       .remove = tegra210_dmic_remove,
+       .remove_new = tegra210_dmic_remove,
 };
 module_platform_driver(tegra210_dmic_driver)
 
index 39ffa4d76b593748be6f9b98114d63ad71be0c49..21724cd3525ec280d60a4086c01e26aa0366ab07 100644 (file)
@@ -931,11 +931,9 @@ static int tegra210_i2s_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_i2s_remove(struct platform_device *pdev)
+static void tegra210_i2s_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_i2s_pm_ops = {
@@ -958,7 +956,7 @@ static struct platform_driver tegra210_i2s_driver = {
                .pm = &tegra210_i2s_pm_ops,
        },
        .probe = tegra210_i2s_probe,
-       .remove = tegra210_i2s_remove,
+       .remove_new = tegra210_i2s_remove,
 };
 module_platform_driver(tegra210_i2s_driver)
 
index 16e679a9565825c41bc3c2a0646aa67439efbfc0..035e9035b533bcd31ba8291bc4a31eed8eb66325 100644 (file)
@@ -656,11 +656,9 @@ static int tegra210_mixer_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_mixer_platform_remove(struct platform_device *pdev)
+static void tegra210_mixer_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_mixer_pm_ops = {
@@ -677,7 +675,7 @@ static struct platform_driver tegra210_mixer_driver = {
                .pm = &tegra210_mixer_pm_ops,
        },
        .probe = tegra210_mixer_platform_probe,
-       .remove = tegra210_mixer_platform_remove,
+       .remove_new = tegra210_mixer_platform_remove,
 };
 module_platform_driver(tegra210_mixer_driver);
 
index 725385e17d8451abe4d69d86b63020bcb3c27869..44f465e11bee31e20df62cdb80e6b10ac933593d 100644 (file)
@@ -748,11 +748,9 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_mvc_platform_remove(struct platform_device *pdev)
+static void tegra210_mvc_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_mvc_pm_ops = {
@@ -769,7 +767,7 @@ static struct platform_driver tegra210_mvc_driver = {
                .pm = &tegra210_mvc_pm_ops,
        },
        .probe = tegra210_mvc_platform_probe,
-       .remove = tegra210_mvc_platform_remove,
+       .remove_new = tegra210_mvc_platform_remove,
 };
 module_platform_driver(tegra210_mvc_driver)
 
index 3dd2bdec657bcf40b9ec934d0afe28e0add6981e..98e7264326153913efbaefe10e1c79bbcd46523b 100644 (file)
@@ -347,11 +347,9 @@ static int tegra210_ope_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_ope_remove(struct platform_device *pdev)
+static void tegra210_ope_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static int __maybe_unused tegra210_ope_runtime_suspend(struct device *dev)
@@ -410,7 +408,7 @@ static struct platform_driver tegra210_ope_driver = {
                .pm = &tegra210_ope_pm_ops,
        },
        .probe = tegra210_ope_probe,
-       .remove = tegra210_ope_remove,
+       .remove_new = tegra210_ope_remove,
 };
 module_platform_driver(tegra210_ope_driver)
 
index 368f077e7bee750e92b36b8dd10a30f0abb2a72e..e9df1ffc8a584fe986fa2cc34a909588240790cb 100644 (file)
@@ -3584,11 +3584,9 @@ static int tegra210_sfc_platform_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tegra210_sfc_platform_remove(struct platform_device *pdev)
+static void tegra210_sfc_platform_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra210_sfc_pm_ops = {
@@ -3605,7 +3603,7 @@ static struct platform_driver tegra210_sfc_driver = {
                .pm = &tegra210_sfc_pm_ops,
        },
        .probe = tegra210_sfc_platform_probe,
-       .remove = tegra210_sfc_platform_remove,
+       .remove_new = tegra210_sfc_platform_remove,
 };
 module_platform_driver(tegra210_sfc_driver)
 
index ef011a488cebbc10a1d746aba12c66ada4b79cc2..d2e8078e444af3037e02df15532b65ed012dc1a6 100644 (file)
@@ -592,13 +592,11 @@ err_unset_ahub:
        return ret;
 }
 
-static int tegra30_ahub_remove(struct platform_device *pdev)
+static void tegra30_ahub_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
 
        ahub = NULL;
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra30_ahub_pm_ops = {
@@ -610,7 +608,7 @@ static const struct dev_pm_ops tegra30_ahub_pm_ops = {
 
 static struct platform_driver tegra30_ahub_driver = {
        .probe = tegra30_ahub_probe,
-       .remove = tegra30_ahub_remove,
+       .remove_new = tegra30_ahub_remove,
        .driver = {
                .name = DRV_NAME,
                .of_match_table = tegra30_ahub_of_match,
index c26f960c6afde9d320e3050a12986a7fda2bb0da..6442806030956e088e3e0dcf3fdd78351bde5c9a 100644 (file)
@@ -531,7 +531,7 @@ err:
        return ret;
 }
 
-static int tegra30_i2s_platform_remove(struct platform_device *pdev)
+static void tegra30_i2s_platform_remove(struct platform_device *pdev)
 {
        struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev);
 
@@ -545,8 +545,6 @@ static int tegra30_i2s_platform_remove(struct platform_device *pdev)
        tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
 
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 static const struct dev_pm_ops tegra30_i2s_pm_ops = {
@@ -563,7 +561,7 @@ static struct platform_driver tegra30_i2s_driver = {
                .pm = &tegra30_i2s_pm_ops,
        },
        .probe = tegra30_i2s_platform_probe,
-       .remove = tegra30_i2s_platform_remove,
+       .remove_new = tegra30_i2s_platform_remove,
 };
 module_platform_driver(tegra30_i2s_driver);
 
index 78faa8bcae2742411fa799b49f060884d19af8ab..f5092b4109268d184ab157452a66850fae87e71d 100644 (file)
@@ -51,6 +51,17 @@ static struct snd_soc_jack_gpio tegra_machine_headset_jack_gpio = {
 };
 
 /* Mic Jack */
+static int coupled_mic_hp_check(void *data)
+{
+       struct tegra_machine *machine = (struct tegra_machine *)data;
+
+       /* Detect mic insertion only if 3.5 jack is in */
+       if (gpiod_get_value_cansleep(machine->gpiod_hp_det) &&
+           gpiod_get_value_cansleep(machine->gpiod_mic_det))
+               return SND_JACK_MICROPHONE;
+
+       return 0;
+}
 
 static struct snd_soc_jack tegra_machine_mic_jack;
 
@@ -75,11 +86,11 @@ static int tegra_machine_event(struct snd_soc_dapm_widget *w,
                gpiod_set_value_cansleep(machine->gpiod_spkr_en,
                                         SND_SOC_DAPM_EVENT_ON(event));
 
-       if (!strcmp(w->name, "Mic Jack"))
+       if (!strcmp(w->name, "Mic Jack") || !strcmp(w->name, "Headset Mic"))
                gpiod_set_value_cansleep(machine->gpiod_ext_mic_en,
                                         SND_SOC_DAPM_EVENT_ON(event));
 
-       if (!strcmp(w->name, "Int Mic"))
+       if (!strcmp(w->name, "Int Mic") || !strcmp(w->name, "Internal Mic 2"))
                gpiod_set_value_cansleep(machine->gpiod_int_mic_en,
                                         SND_SOC_DAPM_EVENT_ON(event));
 
@@ -97,11 +108,12 @@ static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphones", NULL),
        SND_SOC_DAPM_SPK("Speakers", tegra_machine_event),
        SND_SOC_DAPM_SPK("Int Spk", tegra_machine_event),
+       SND_SOC_DAPM_SPK("Earpiece", NULL),
        SND_SOC_DAPM_MIC("Int Mic", tegra_machine_event),
        SND_SOC_DAPM_MIC("Mic Jack", tegra_machine_event),
        SND_SOC_DAPM_MIC("Internal Mic 1", NULL),
-       SND_SOC_DAPM_MIC("Internal Mic 2", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Internal Mic 2", tegra_machine_event),
+       SND_SOC_DAPM_MIC("Headset Mic", tegra_machine_event),
        SND_SOC_DAPM_MIC("Digital Mic", NULL),
        SND_SOC_DAPM_MIC("Mic", NULL),
        SND_SOC_DAPM_LINE("Line In Jack", NULL),
@@ -112,6 +124,7 @@ static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = {
 static const struct snd_kcontrol_new tegra_machine_controls[] = {
        SOC_DAPM_PIN_SWITCH("Speakers"),
        SOC_DAPM_PIN_SWITCH("Int Spk"),
+       SOC_DAPM_PIN_SWITCH("Earpiece"),
        SOC_DAPM_PIN_SWITCH("Int Mic"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
        SOC_DAPM_PIN_SWITCH("Internal Mic 1"),
@@ -183,8 +196,15 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
                        return err;
                }
 
+               tegra_machine_mic_jack_gpio.data = machine;
                tegra_machine_mic_jack_gpio.desc = machine->gpiod_mic_det;
 
+               if (of_property_read_bool(card->dev->of_node,
+                                         "nvidia,coupled-mic-hp-det")) {
+                       tegra_machine_mic_jack_gpio.desc = machine->gpiod_hp_det;
+                       tegra_machine_mic_jack_gpio.jack_status_check = coupled_mic_hp_check;
+               }
+
                err = snd_soc_jack_add_gpios(&tegra_machine_mic_jack, 1,
                                             &tegra_machine_mic_jack_gpio);
                if (err)
@@ -238,6 +258,32 @@ static unsigned int tegra_machine_mclk_rate_12mhz(unsigned int srate)
        return mclk;
 }
 
+static unsigned int tegra_machine_mclk_rate_6mhz(unsigned int srate)
+{
+       unsigned int mclk;
+
+       switch (srate) {
+       case 8000:
+       case 16000:
+       case 64000:
+               mclk = 8192000;
+               break;
+       case 11025:
+       case 22050:
+       case 88200:
+               mclk = 11289600;
+               break;
+       case 96000:
+               mclk = 12288000;
+               break;
+       default:
+               mclk = 256 * srate;
+               break;
+       }
+
+       return mclk;
+}
+
 static int tegra_machine_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
 {
@@ -502,7 +548,7 @@ int tegra_asoc_machine_probe(struct platform_device *pdev)
         * If clock parents are not set in DT, configure here to use clk_out_1
         * as mclk and extern1 as parent for Tegra30 and higher.
         */
-       if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) &&
+       if (!of_property_present(dev->of_node, "assigned-clock-parents") &&
            !of_machine_is_compatible("nvidia,tegra20")) {
                struct clk *clk_out_1, *clk_extern1;
 
@@ -674,6 +720,40 @@ static const struct tegra_asoc_data tegra_max98090_data = {
        .add_hp_jack = true,
 };
 
+/* MAX98088 machine */
+
+SND_SOC_DAILINK_DEFS(max98088_hifi,
+       DAILINK_COMP_ARRAY(COMP_EMPTY()),
+       DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+       DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link tegra_max98088_dai = {
+       .name = "MAX98088",
+       .stream_name = "MAX98088 PCM",
+       .init = tegra_asoc_machine_init,
+       .dai_fmt = SND_SOC_DAIFMT_I2S |
+                  SND_SOC_DAIFMT_NB_NF |
+                  SND_SOC_DAIFMT_CBS_CFS,
+       SND_SOC_DAILINK_REG(max98088_hifi),
+};
+
+static struct snd_soc_card snd_soc_tegra_max98088 = {
+       .components = "codec:max98088",
+       .dai_link = &tegra_max98088_dai,
+       .num_links = 1,
+       .fully_routed = true,
+};
+
+static const struct tegra_asoc_data tegra_max98088_data = {
+       .mclk_rate = tegra_machine_mclk_rate_12mhz,
+       .card = &snd_soc_tegra_max98088,
+       .add_common_dapm_widgets = true,
+       .add_common_controls = true,
+       .add_common_snd_ops = true,
+       .add_mic_jack = true,
+       .add_hp_jack = true,
+};
+
 /* SGTL5000 machine */
 
 SND_SOC_DAILINK_DEFS(sgtl5000_hifi,
@@ -865,15 +945,52 @@ static const struct tegra_asoc_data tegra_rt5632_data = {
        .add_headset_jack = true,
 };
 
+/* RT5631 machine */
+
+SND_SOC_DAILINK_DEFS(rt5631_hifi,
+       DAILINK_COMP_ARRAY(COMP_EMPTY()),
+       DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-hifi")),
+       DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link tegra_rt5631_dai = {
+       .name = "RT5631",
+       .stream_name = "RT5631 PCM",
+       .init = tegra_asoc_machine_init,
+       .dai_fmt = SND_SOC_DAIFMT_I2S |
+                  SND_SOC_DAIFMT_NB_NF |
+                  SND_SOC_DAIFMT_CBS_CFS,
+       SND_SOC_DAILINK_REG(rt5631_hifi),
+};
+
+static struct snd_soc_card snd_soc_tegra_rt5631 = {
+       .components = "codec:rt5631",
+       .dai_link = &tegra_rt5631_dai,
+       .num_links = 1,
+       .fully_routed = true,
+};
+
+static const struct tegra_asoc_data tegra_rt5631_data = {
+       .mclk_rate = tegra_machine_mclk_rate_6mhz,
+       .card = &snd_soc_tegra_rt5631,
+       .add_common_dapm_widgets = true,
+       .add_common_controls = true,
+       .add_common_snd_ops = true,
+       .add_mic_jack = true,
+       .add_hp_jack = true,
+};
+
 static const struct of_device_id tegra_machine_of_match[] = {
        { .compatible = "nvidia,tegra-audio-trimslice", .data = &tegra_trimslice_data },
        { .compatible = "nvidia,tegra-audio-max98090", .data = &tegra_max98090_data },
+       { .compatible = "nvidia,tegra-audio-max98088", .data = &tegra_max98088_data },
+       { .compatible = "nvidia,tegra-audio-max98089", .data = &tegra_max98088_data },
        { .compatible = "nvidia,tegra-audio-sgtl5000", .data = &tegra_sgtl5000_data },
        { .compatible = "nvidia,tegra-audio-wm9712", .data = &tegra_wm9712_data },
        { .compatible = "nvidia,tegra-audio-wm8753", .data = &tegra_wm8753_data },
        { .compatible = "nvidia,tegra-audio-rt5677", .data = &tegra_rt5677_data },
        { .compatible = "nvidia,tegra-audio-rt5640", .data = &tegra_rt5640_data },
        { .compatible = "nvidia,tegra-audio-alc5632", .data = &tegra_rt5632_data },
+       { .compatible = "nvidia,tegra-audio-rt5631", .data = &tegra_rt5631_data },
        {},
 };
 MODULE_DEVICE_TABLE(of, tegra_machine_of_match);
index 438e2fa843a03f59e15e908cff388291caac6c52..1028b5efcfff84247e732cb28d29883f9a064d2d 100644 (file)
@@ -578,7 +578,7 @@ static int ams_delta_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int ams_delta_remove(struct platform_device *pdev)
+static void ams_delta_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
@@ -586,7 +586,6 @@ static int ams_delta_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
        card->dev = NULL;
-       return 0;
 }
 
 #define DRV_NAME "ams-delta-audio"
@@ -596,7 +595,7 @@ static struct platform_driver ams_delta_driver = {
                .name = DRV_NAME,
        },
        .probe = ams_delta_probe,
-       .remove = ams_delta_remove,
+       .remove_new = ams_delta_remove,
 };
 
 module_platform_driver(ams_delta_driver);
index 3ccd0cfca008f9324de3e4e445e1a82fbb4ed24d..97dd1634b6becb67557fc243a8bf23ef8c3b4505 100644 (file)
@@ -739,7 +739,7 @@ err_put_clk:
        return ret;
 }
 
-static int davinci_i2s_remove(struct platform_device *pdev)
+static void davinci_i2s_remove(struct platform_device *pdev)
 {
        struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
 
@@ -748,8 +748,6 @@ static int davinci_i2s_remove(struct platform_device *pdev)
        clk_disable(dev->clk);
        clk_put(dev->clk);
        dev->clk = NULL;
-
-       return 0;
 }
 
 static const struct of_device_id davinci_i2s_match[] __maybe_unused = {
@@ -760,7 +758,7 @@ MODULE_DEVICE_TABLE(of, davinci_i2s_match);
 
 static struct platform_driver davinci_mcbsp_driver = {
        .probe          = davinci_i2s_probe,
-       .remove         = davinci_i2s_remove,
+       .remove_new     = davinci_i2s_remove,
        .driver         = {
                .name   = "davinci-mcbsp",
                .of_match_table = of_match_ptr(davinci_i2s_match),
index 578254549d2d6dc37ecda63a5883590d7156736a..c0892be2992b0c2446faf5791559d897984a250f 100644 (file)
@@ -2461,11 +2461,9 @@ err:
        return ret;
 }
 
-static int davinci_mcasp_remove(struct platform_device *pdev)
+static void davinci_mcasp_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -2531,7 +2529,7 @@ static const struct dev_pm_ops davinci_mcasp_pm_ops = {
 
 static struct platform_driver davinci_mcasp_driver = {
        .probe          = davinci_mcasp_probe,
-       .remove         = davinci_mcasp_remove,
+       .remove_new     = davinci_mcasp_remove,
        .driver         = {
                .name   = "davinci-mcasp",
                .pm     = &davinci_mcasp_pm_ops,
index 0dc0475670ffecc9b22be3996f0dfefb2ef59792..96c3569d76437f780bc53cba2b31e162592f70a6 100644 (file)
@@ -365,20 +365,17 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
        if (!card->dai_link)
                return -ENOMEM;
 
-       compnent = devm_kzalloc(dev, 3 * sizeof(*compnent), GFP_KERNEL);
+       compnent = devm_kzalloc(dev, 2 * sizeof(*compnent), GFP_KERNEL);
        if (!compnent)
                return -ENOMEM;
        card->dai_link->cpus            = &compnent[0];
        card->dai_link->num_cpus        = 1;
        card->dai_link->codecs          = &compnent[1];
        card->dai_link->num_codecs      = 1;
-       card->dai_link->platforms       = &compnent[2];
-       card->dai_link->num_platforms   = 1;
 
        card->dai_link->name = card->name;
        card->dai_link->stream_name = card->name;
        card->dai_link->cpus->dai_name = dev_name(ad->dssdev);
-       card->dai_link->platforms->name = dev_name(ad->dssdev);
        card->dai_link->codecs->name = "snd-soc-dummy";
        card->dai_link->codecs->dai_name = "snd-soc-dummy-dai";
        card->num_links = 1;
@@ -398,12 +395,11 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int omap_hdmi_audio_remove(struct platform_device *pdev)
+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);
-       return 0;
 }
 
 static struct platform_driver hdmi_audio_driver = {
@@ -411,7 +407,7 @@ static struct platform_driver hdmi_audio_driver = {
                .name = DRV_NAME,
        },
        .probe = omap_hdmi_audio_probe,
-       .remove = omap_hdmi_audio_remove,
+       .remove_new = omap_hdmi_audio_remove,
 };
 
 module_platform_driver(hdmi_audio_driver);
index 7c539a41a6a346b4ec1d56476b211dc85be5ffde..21fa7b9787997c6d6e5b2f5c266adf92a9be31e5 100644 (file)
@@ -1412,7 +1412,7 @@ static int asoc_mcbsp_probe(struct platform_device *pdev)
        return sdma_pcm_platform_register(&pdev->dev, "tx", "rx");
 }
 
-static int asoc_mcbsp_remove(struct platform_device *pdev)
+static void asoc_mcbsp_remove(struct platform_device *pdev)
 {
        struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
 
@@ -1421,8 +1421,6 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
 
        if (cpu_latency_qos_request_active(&mcbsp->pm_qos_req))
                cpu_latency_qos_remove_request(&mcbsp->pm_qos_req);
-
-       return 0;
 }
 
 static struct platform_driver asoc_mcbsp_driver = {
@@ -1432,7 +1430,7 @@ static struct platform_driver asoc_mcbsp_driver = {
        },
 
        .probe = asoc_mcbsp_probe,
-       .remove = asoc_mcbsp_remove,
+       .remove_new = asoc_mcbsp_remove,
 };
 
 module_platform_driver(asoc_mcbsp_driver);
index 42403ae8e31b2a81e569a76b01d0da0e4da38dd2..d90b3e4b010410af0a699b6bbfd0a25108b70589 100644 (file)
@@ -536,7 +536,7 @@ err_out_clock:
        return ret;
 }
 
-static int evea_remove(struct platform_device *pdev)
+static void evea_remove(struct platform_device *pdev)
 {
        struct evea_priv *evea = platform_get_drvdata(pdev);
 
@@ -546,8 +546,6 @@ static int evea_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(evea->clk_exiv);
        clk_disable_unprepare(evea->clk);
-
-       return 0;
 }
 
 static const struct of_device_id evea_of_match[] __maybe_unused = {
@@ -562,7 +560,7 @@ static struct platform_driver evea_codec_driver = {
                .of_match_table = of_match_ptr(evea_of_match),
        },
        .probe  = evea_probe,
-       .remove = evea_remove,
+       .remove_new = evea_remove,
 };
 module_platform_driver(evea_codec_driver);
 
index 325e75e961369e21b62cf7089364e62a67b18410..e0ab4534fe3e9520b4325266b8a78d654a9d6e0f 100644 (file)
@@ -134,7 +134,7 @@ static int mop500_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int mop500_remove(struct platform_device *pdev)
+static void mop500_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
@@ -143,8 +143,6 @@ static int mop500_remove(struct platform_device *pdev)
        snd_soc_unregister_card(card);
        mop500_ab8500_remove(card);
        mop500_of_node_put();
-
-       return 0;
 }
 
 static const struct of_device_id snd_soc_mop500_match[] = {
@@ -159,7 +157,7 @@ static struct platform_driver snd_soc_mop500_driver = {
                .of_match_table = snd_soc_mop500_match,
        },
        .probe = mop500_probe,
-       .remove = mop500_remove,
+       .remove_new = mop500_remove,
 };
 
 module_platform_driver(snd_soc_mop500_driver);
index 6fb1a5c207bc7d4624317179adfa4f48cc068292..44e88dad85841e53211ea98099c5c077d81f60b6 100644 (file)
@@ -791,7 +791,7 @@ err_reg_plat:
        return ret;
 }
 
-static int ux500_msp_drv_remove(struct platform_device *pdev)
+static void ux500_msp_drv_remove(struct platform_device *pdev)
 {
        struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
 
@@ -802,8 +802,6 @@ static int ux500_msp_drv_remove(struct platform_device *pdev)
        prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
 
        ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
-
-       return 0;
 }
 
 static const struct of_device_id ux500_msp_i2s_match[] = {
@@ -818,7 +816,7 @@ static struct platform_driver msp_i2s_driver = {
                .of_match_table = ux500_msp_i2s_match,
        },
        .probe = ux500_msp_drv_probe,
-       .remove = ux500_msp_drv_remove,
+       .remove_new = ux500_msp_drv_remove,
 };
 module_platform_driver(msp_i2s_driver);
 
index ff1fe62fea7028eed06a96d22855bbce311917b1..299cfb5e202240a45b210e0c3278c598fc5e6d4a 100644 (file)
@@ -687,7 +687,7 @@ clk_err:
        return ret;
 }
 
-static int xlnx_formatter_pcm_remove(struct platform_device *pdev)
+static void xlnx_formatter_pcm_remove(struct platform_device *pdev)
 {
        int ret = 0;
        struct xlnx_pcm_drv_data *adata = dev_get_drvdata(&pdev->dev);
@@ -703,7 +703,6 @@ static int xlnx_formatter_pcm_remove(struct platform_device *pdev)
                dev_err(&pdev->dev, "audio formatter reset failed\n");
 
        clk_disable_unprepare(adata->axi_clk);
-       return 0;
 }
 
 static const struct of_device_id xlnx_formatter_pcm_of_match[] = {
@@ -714,7 +713,7 @@ MODULE_DEVICE_TABLE(of, xlnx_formatter_pcm_of_match);
 
 static struct platform_driver xlnx_formatter_pcm_driver = {
        .probe  = xlnx_formatter_pcm_probe,
-       .remove = xlnx_formatter_pcm_remove,
+       .remove_new = xlnx_formatter_pcm_remove,
        .driver = {
                .name   = DRV_NAME,
                .of_match_table = xlnx_formatter_pcm_of_match,
index 7342048e98751c174982c32d55e3ec6f2fc64268..d52d5fc7b5b81ea01214287e3320e2ac3e6345de 100644 (file)
@@ -312,12 +312,11 @@ clk_err:
        return ret;
 }
 
-static int xlnx_spdif_remove(struct platform_device *pdev)
+static void xlnx_spdif_remove(struct platform_device *pdev)
 {
        struct spdif_dev_data *ctx = dev_get_drvdata(&pdev->dev);
 
        clk_disable_unprepare(ctx->axi_clk);
-       return 0;
 }
 
 static struct platform_driver xlnx_spdif_driver = {
@@ -326,7 +325,7 @@ static struct platform_driver xlnx_spdif_driver = {
                .of_match_table = xlnx_spdif_of_match,
        },
        .probe = xlnx_spdif_probe,
-       .remove = xlnx_spdif_remove,
+       .remove_new = xlnx_spdif_remove,
 };
 module_platform_driver(xlnx_spdif_driver);
 
index a8f156540b50078d530d457ca4ba81d8df9bad14..287407714af49332b45f6dd08a702f9dc1f8d59e 100644 (file)
@@ -605,7 +605,7 @@ err:
        return err;
 }
 
-static int xtfpga_i2s_remove(struct platform_device *pdev)
+static void xtfpga_i2s_remove(struct platform_device *pdev)
 {
        struct xtfpga_i2s *i2s = dev_get_drvdata(&pdev->dev);
 
@@ -618,7 +618,6 @@ static int xtfpga_i2s_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                xtfpga_i2s_runtime_suspend(&pdev->dev);
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -636,7 +635,7 @@ static const struct dev_pm_ops xtfpga_i2s_pm_ops = {
 
 static struct platform_driver xtfpga_i2s_driver = {
        .probe   = xtfpga_i2s_probe,
-       .remove  = xtfpga_i2s_remove,
+       .remove_new = xtfpga_i2s_remove,
        .driver  = {
                .name = "xtfpga-i2s",
                .of_match_table = of_match_ptr(xtfpga_i2s_of_match),
index 2942c8c7a2366fa4474fe7b6e3a36b6fd137fe5d..31bac355ec4d069b4291b99bed36a1443417411a 100644 (file)
@@ -2069,13 +2069,11 @@ static int cs4231_probe(struct platform_device *op)
        return -ENODEV;
 }
 
-static int cs4231_remove(struct platform_device *op)
+static void cs4231_remove(struct platform_device *op)
 {
        struct snd_cs4231 *chip = dev_get_drvdata(&op->dev);
 
        snd_card_free(chip->card);
-
-       return 0;
 }
 
 static const struct of_device_id cs4231_match[] = {
@@ -2097,7 +2095,7 @@ static struct platform_driver cs4231_driver = {
                .of_match_table = cs4231_match,
        },
        .probe          = cs4231_probe,
-       .remove         = cs4231_remove,
+       .remove_new     = cs4231_remove,
 };
 
 module_platform_driver(cs4231_driver);
index 3881e1c1b08a3ccc9ded7f0766cefa950af641ff..376aed136a45fc37d93e67c90f7d161d318151da 100644 (file)
@@ -2656,14 +2656,12 @@ _err:
        return err;
 }
 
-static int dbri_remove(struct platform_device *op)
+static void dbri_remove(struct platform_device *op)
 {
        struct snd_card *card = dev_get_drvdata(&op->dev);
 
        snd_dbri_free(card->private_data);
        snd_card_free(card);
-
-       return 0;
 }
 
 static const struct of_device_id dbri_match[] = {
@@ -2684,7 +2682,7 @@ static struct platform_driver dbri_sbus_driver = {
                .of_match_table = dbri_match,
        },
        .probe          = dbri_probe,
-       .remove         = dbri_remove,
+       .remove_new     = dbri_remove,
 };
 
 module_platform_driver(dbri_sbus_driver);
index 26268ffb82742116de347782d7717184e811e498..f6e99ced8068cca261fa77c5d8d7200b988c5223 100644 (file)
@@ -609,7 +609,6 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        case USB_SPEED_LOW:
        case USB_SPEED_FULL:
        case USB_SPEED_HIGH:
-       case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
        case USB_SPEED_SUPER_PLUS:
                break;
index 647fa054d8b1da37a3ec8db6e4608ce8393d8a12..a385e85c4650b059333d43bb97765dcbdfc6b50e 100644 (file)
@@ -916,8 +916,9 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
                      ep->iface, altset, ep->ep_num);
        err = usb_set_interface(chip->dev, ep->iface, altset);
        if (err < 0) {
-               usb_audio_err(chip, "%d:%d: usb_set_interface failed (%d)\n",
-                             ep->iface, altset, err);
+               usb_audio_err_ratelimited(
+                       chip, "%d:%d: usb_set_interface failed (%d)\n",
+                       ep->iface, altset, err);
                return err;
        }
 
@@ -1179,22 +1180,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
         */
        if (usb_pipein(ep->pipe) || ep->implicit_fb_sync) {
 
-               urb_packs = packs_per_ms;
-               /*
-                * Wireless devices can poll at a max rate of once per 4ms.
-                * For dataintervals less than 5, increase the packet count to
-                * allow the host controller to use bursting to fill in the
-                * gaps.
-                */
-               if (snd_usb_get_speed(chip->dev) == USB_SPEED_WIRELESS) {
-                       int interval = ep->datainterval;
-                       while (interval < 5) {
-                               urb_packs <<= 1;
-                               ++interval;
-                       }
-               }
                /* make capture URBs <= 1 ms and smaller than a period */
-               urb_packs = min(max_packs_per_urb, urb_packs);
+               urb_packs = min(max_packs_per_urb, packs_per_ms);
                while (urb_packs > 1 && urb_packs * maxsize >= ep->cur_period_bytes)
                        urb_packs >>= 1;
                ep->nurbs = MAX_URBS;
index a4410267bf70150e0d45c00f5fd28b0b36af368c..bf80e55d013a872b6efba0a3ac4c06bb3cd82c89 100644 (file)
@@ -108,7 +108,6 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
 {
        switch (snd_usb_get_speed(chip->dev)) {
        case USB_SPEED_HIGH:
-       case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
        case USB_SPEED_SUPER_PLUS:
                if (get_endpoint(alts, 0)->bInterval >= 1 &&
index f5a8dca66457f586ab93369e1830be1374132dc5..38a85b2c9a73e0d62d0b73a4491a6a34a80b3a87 100644 (file)
@@ -67,6 +67,8 @@ struct snd_usb_audio {
 
 #define usb_audio_err(chip, fmt, args...) \
        dev_err(&(chip)->dev->dev, fmt, ##args)
+#define usb_audio_err_ratelimited(chip, fmt, args...) \
+       dev_err_ratelimited(&(chip)->dev->dev, fmt, ##args)
 #define usb_audio_warn(chip, fmt, args...) \
        dev_warn(&(chip)->dev->dev, fmt, ##args)
 #define usb_audio_info(chip, fmt, args...) \
index 05f1749ae19d62d699b933c1f5ce191b5a2f5989..c95d63e553f4d2eec044968c9c36d77313d570c8 100644 (file)
@@ -63,6 +63,7 @@ static void find_controls(void)
        struct card_data *card_data;
        struct ctl_data *ctl_data;
        snd_config_t *config;
+       char *card_name, *card_longname;
 
        card = -1;
        if (snd_card_next(&card) < 0 || card < 0)
@@ -84,6 +85,15 @@ static void find_controls(void)
                        goto next_card;
                }
 
+               err = snd_card_get_name(card, &card_name);
+               if (err != 0)
+                       card_name = "Unknown";
+               err = snd_card_get_longname(card, &card_longname);
+               if (err != 0)
+                       card_longname = "Unknown";
+               ksft_print_msg("Card %d - %s (%s)\n", card,
+                              card_name, card_longname);
+
                /* Count controls */
                snd_ctl_elem_list_malloc(&card_data->ctls);
                snd_ctl_elem_list(card_data->handle, card_data->ctls);
@@ -422,6 +432,9 @@ static void test_ctl_name(struct ctl_data *ctl)
        bool name_ok = true;
        bool check;
 
+       ksft_print_msg("%d.%d %s\n", ctl->card->card, ctl->elem,
+                      ctl->name);
+
        /* Only boolean controls should end in Switch */
        if (strend(ctl->name, " Switch")) {
                if (snd_ctl_elem_info_get_type(ctl->info) != SND_CTL_ELEM_TYPE_BOOLEAN) {
@@ -445,6 +458,48 @@ static void test_ctl_name(struct ctl_data *ctl)
                         ctl->card->card, ctl->elem);
 }
 
+static void show_values(struct ctl_data *ctl, snd_ctl_elem_value_t *orig_val,
+                       snd_ctl_elem_value_t *read_val)
+{
+       long long orig_int, read_int;
+       int i;
+
+       for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+               switch (snd_ctl_elem_info_get_type(ctl->info)) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       orig_int = snd_ctl_elem_value_get_boolean(orig_val, i);
+                       read_int = snd_ctl_elem_value_get_boolean(read_val, i);
+                       break;
+
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       orig_int = snd_ctl_elem_value_get_integer(orig_val, i);
+                       read_int = snd_ctl_elem_value_get_integer(read_val, i);
+                       break;
+
+               case SND_CTL_ELEM_TYPE_INTEGER64:
+                       orig_int = snd_ctl_elem_value_get_integer64(orig_val,
+                                                                   i);
+                       read_int = snd_ctl_elem_value_get_integer64(read_val,
+                                                                   i);
+                       break;
+
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       orig_int = snd_ctl_elem_value_get_enumerated(orig_val,
+                                                                    i);
+                       read_int = snd_ctl_elem_value_get_enumerated(read_val,
+                                                                    i);
+                       break;
+
+               default:
+                       return;
+               }
+
+               ksft_print_msg("%s.%d orig %lld read %lld, is_volatile %d\n",
+                              ctl->name, i, orig_int, read_int,
+                              snd_ctl_elem_info_is_volatile(ctl->info));
+       }
+}
+
 static bool show_mismatch(struct ctl_data *ctl, int index,
                          snd_ctl_elem_value_t *read_val,
                          snd_ctl_elem_value_t *expected_val)
@@ -584,12 +639,14 @@ static int write_and_verify(struct ctl_data *ctl,
                        if (err < 1) {
                                ksft_print_msg("No event generated for %s\n",
                                               ctl->name);
+                               show_values(ctl, initial_val, read_val);
                                ctl->event_missing++;
                        }
                } else {
                        if (err != 0) {
                                ksft_print_msg("Spurious event generated for %s\n",
                                               ctl->name);
+                               show_values(ctl, initial_val, read_val);
                                ctl->event_spurious++;
                        }
                }
@@ -755,7 +812,6 @@ static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
 static void test_ctl_write_valid(struct ctl_data *ctl)
 {
        bool pass;
-       int err;
 
        /* If the control is turned off let's be polite */
        if (snd_ctl_elem_info_is_inactive(ctl->info)) {
@@ -797,9 +853,7 @@ static void test_ctl_write_valid(struct ctl_data *ctl)
        }
 
        /* Restore the default value to minimise disruption */
-       err = write_and_verify(ctl, ctl->def_val, NULL);
-       if (err < 0)
-               pass = false;
+       write_and_verify(ctl, ctl->def_val, NULL);
 
        ksft_test_result(pass, "write_valid.%d.%d\n",
                         ctl->card->card, ctl->elem);
@@ -1015,9 +1069,7 @@ static void test_ctl_write_invalid(struct ctl_data *ctl)
        }
 
        /* Restore the default value to minimise disruption */
-       err = write_and_verify(ctl, ctl->def_val, NULL);
-       if (err < 0)
-               pass = false;
+       write_and_verify(ctl, ctl->def_val, NULL);
 
        ksft_test_result(pass, "write_invalid.%d.%d\n",
                         ctl->card->card, ctl->elem);
index 58b525a4a32ca2b1a15883a94633bacbe8d68a0e..3e390fe67eb93ed7588161047ce748cffcdcc4a8 100644 (file)
@@ -149,6 +149,7 @@ static void missing_devices(int card, snd_config_t *card_config)
 static void find_pcms(void)
 {
        char name[32], key[64];
+       char *card_name, *card_longname;
        int card, dev, subdev, count, direction, err;
        snd_pcm_stream_t stream;
        struct pcm_data *pcm_data;
@@ -175,6 +176,15 @@ static void find_pcms(void)
                        goto next_card;
                }
 
+               err = snd_card_get_name(card, &card_name);
+               if (err != 0)
+                       card_name = "Unknown";
+               err = snd_card_get_longname(card, &card_longname);
+               if (err != 0)
+                       card_longname = "Unknown";
+               ksft_print_msg("Card %d - %s (%s)\n", card,
+                              card_name, card_longname);
+
                card_config = conf_by_card(card);
 
                card_data = calloc(1, sizeof(*card_data));
@@ -489,17 +499,18 @@ __close:
        }
 
        if (!skip)
-               ksft_test_result(pass, "%s.%s.%d.%d.%d.%s%s%s\n",
+               ksft_test_result(pass, "%s.%s.%d.%d.%d.%s\n",
                                 test_class_name, test_name,
                                 data->card, data->device, data->subdevice,
-                                snd_pcm_stream_name(data->stream),
-                                msg[0] ? " " : "", msg);
+                                snd_pcm_stream_name(data->stream));
        else
-               ksft_test_result_skip("%s.%s.%d.%d.%d.%s%s%s\n",
+               ksft_test_result_skip("%s.%s.%d.%d.%d.%s\n",
                                 test_class_name, test_name,
                                 data->card, data->device, data->subdevice,
-                                snd_pcm_stream_name(data->stream),
-                                msg[0] ? " " : "", msg);
+                                snd_pcm_stream_name(data->stream));
+
+       if (msg[0])
+               ksft_print_msg("%s\n", msg);
 
        pthread_mutex_unlock(&results_lock);