aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-11-03 07:49:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-11-03 07:49:25 -0700
commitff0700f03609b9f0defacd4ce96d9519d721e0a2 (patch)
tree4b7ac6cf015e39f82ef0706ce465a224a43dac42 /sound
parentMerge tag 'devicetree-for-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux (diff)
parentALSA: usb-audio: Add quirk for Audient iD14 (diff)
downloadlinux-dev-ff0700f03609b9f0defacd4ce96d9519d721e0a2.tar.xz
linux-dev-ff0700f03609b9f0defacd4ce96d9519d721e0a2.zip
Merge tag 'sound-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "Lots of code development have been see in ASoC side as usual, while the continued development on memalloc helper and USB-audio low- latency support are found in the rest. Note that a few changes in the unusual places like arch/sh are included, which are a part of ASoC DAI format cleanups. ALSA core: - Continued memalloc helper updates and cleanups, now supporting non-coherent and non-contiguous pages - Fixes for races in mixer OSS layer ASoC: - A new version of the audio graph card which supports a wider range of systems - Several conversions to YAML DT bindings - Continuing cleanups to the SOF and Intel code - Move of the Cirrus DSP framework into drivers/firmware to allow for future use by non-audio DSPs - An overhaul of the cs42l42 driver, correcting many problems - DAI format terminology conversions over many drivers for cleanups - Support for AMD Vangogh and Yelow Cap, Cirrus CS35L41, Maxim MAX98520 and MAX98360A, Mediatek MT8195, Nuvoton NAU8821, nVidia Tegra210, NXP i.MX8ULP, Qualcomm AudioReach, Realtek ALC5682I-VS, RT5682S, and RT9120 and Rockchip RV1126 and RK3568 USB-audio: - Continued improvements on low-latency playback - Quirks for Pioneer devices, Line6 HX-Stomp XL, Audient iD14 HD-audio: - Reduce excessive udelay() calls on Intel platforms; this should reduce the CPU load with PulseAudio - Quirks for HP and Clevo laptops FireWire: - Support for meter information on MOTU" * tag 'sound-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (513 commits) ALSA: usb-audio: Add quirk for Audient iD14 ALSA: hda/realtek: Add quirk for Clevo PC70HS ALSA: usb-audio: Line6 HX-Stomp XL USB_ID for 48k-fixed quirk ALSA: usb-audio: Add registration quirk for JBL Quantum 400 ASoC: rsnd: Fix an error handling path in 'rsnd_node_count()' ASoC: tlv320aic3x: Make aic3x_remove() return void ASoC: Intel: soc-acpi: use const for all uses of snd_soc_acpi_codecs ASoC: Intel: soc-acpi-cht: shrink tables using compatible IDs ASoC: Intel: soc-acpi-byt: shrink tables using compatible IDs ASoC: Intel: sof_rt5682: use comp_ids to enumerate rt5682s ASoC: Intel: sof_rt5682: detect codec variant in probe function ASoC: soc-acpi: add comp_ids field for machine driver matching ASoC: mediatek: mt8195: add mt8195-mt6359-rt1011-rt5682 bindings document ASoC: mediatek: mt8195: add machine driver with mt6359, rt1011 and rt5682 ASoC: Stop dummy from overriding hwparams ASoC: topology: Change topology device to card device ASoC: topology: Use correct device for prints ASoC: topology: Check for dapm widget completeness ASoC: topology: Add header payload_size verification ASoC: core: Remove invalid snd_soc_component_set_jack call ...
Diffstat (limited to 'sound')
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/memalloc.c207
-rw-r--r--sound/core/memalloc_local.h1
-rw-r--r--sound/core/oss/mixer_oss.c44
-rw-r--r--sound/core/pcm_compat.c4
-rw-r--r--sound/core/pcm_lib.c5
-rw-r--r--sound/core/pcm_local.h7
-rw-r--r--sound/core/pcm_memory.c13
-rw-r--r--sound/core/pcm_native.c66
-rw-r--r--sound/core/sgbuf.c201
-rw-r--r--sound/firewire/motu/Makefile3
-rw-r--r--sound/firewire/motu/amdtp-motu.c11
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c181
-rw-r--r--sound/firewire/motu/motu-hwdep.c119
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c14
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c14
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c420
-rw-r--r--sound/firewire/motu/motu-stream.c10
-rw-r--r--sound/firewire/motu/motu.c10
-rw-r--r--sound/firewire/motu/motu.h23
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c7
-rw-r--r--sound/firewire/oxfw/oxfw.c8
-rw-r--r--sound/firewire/oxfw/oxfw.h5
-rw-r--r--sound/hda/ext/hdac_ext_stream.c46
-rw-r--r--sound/hda/hdac_stream.c4
-rw-r--r--sound/hda/intel-dsp-config.c22
-rw-r--r--sound/isa/Kconfig2
-rw-r--r--sound/isa/gus/gus_dma.c2
-rw-r--r--sound/pci/Kconfig1
-rw-r--r--sound/pci/hda/hda_intel.c49
-rw-r--r--sound/pci/hda/patch_realtek.c19
-rw-r--r--sound/soc/amd/Kconfig40
-rw-r--r--sound/soc/amd/Makefile2
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c6
-rw-r--r--sound/soc/amd/acp-pcm-dma.c6
-rw-r--r--sound/soc/amd/acp-rt5645.c2
-rw-r--r--sound/soc/amd/acp.h2
-rw-r--r--sound/soc/amd/acp/Kconfig61
-rw-r--r--sound/soc/amd/acp/Makefile26
-rw-r--r--sound/soc/amd/acp/acp-i2s.c340
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c104
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c600
-rw-r--r--sound/soc/amd/acp/acp-mach.h57
-rw-r--r--sound/soc/amd/acp/acp-platform.c315
-rw-r--r--sound/soc/amd/acp/acp-renoir.c163
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c131
-rw-r--r--sound/soc/amd/acp/amd.h145
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h76
-rw-r--r--sound/soc/amd/vangogh/Makefile2
-rw-r--r--sound/soc/amd/vangogh/acp5x-i2s.c2
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c386
-rw-r--r--sound/soc/amd/vangogh/acp5x.h2
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c3
-rw-r--r--sound/soc/amd/yc/Makefile9
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c194
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c448
-rw-r--r--sound/soc/amd/yc/acp6x.h107
-rw-r--r--sound/soc/amd/yc/acp6x_chip_offset_byte.h444
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c338
-rw-r--r--sound/soc/atmel/atmel-i2s.c6
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c26
-rw-r--r--sound/soc/atmel/atmel_wm8904.c2
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c10
-rw-r--r--sound/soc/atmel/mikroe-proto.c4
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c2
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c2
-rw-r--r--sound/soc/au1x/db1200.c6
-rw-r--r--sound/soc/au1x/i2sc.c6
-rw-r--r--sound/soc/au1x/psc-i2s.c10
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c56
-rw-r--r--sound/soc/bcm/cygnus-ssp.c6
-rw-r--r--sound/soc/cirrus/edb93xx.c2
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c10
-rw-r--r--sound/soc/cirrus/snappercl15.c2
-rw-r--r--sound/soc/codecs/88pm860x-codec.c18
-rw-r--r--sound/soc/codecs/Kconfig45
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/ab8500-codec.c20
-rw-r--r--sound/soc/codecs/ad1836.c6
-rw-r--r--sound/soc/codecs/ad193x.c10
-rw-r--r--sound/soc/codecs/adau1372.c14
-rw-r--r--sound/soc/codecs/adau1373.c14
-rw-r--r--sound/soc/codecs/adau1701.c6
-rw-r--r--sound/soc/codecs/adau17x1.c6
-rw-r--r--sound/soc/codecs/adau1977.c31
-rw-r--r--sound/soc/codecs/adav80x.c6
-rw-r--r--sound/soc/codecs/ak4104.c4
-rw-r--r--sound/soc/codecs/ak4118.c20
-rw-r--r--sound/soc/codecs/ak4458.c12
-rw-r--r--sound/soc/codecs/ak4642.c8
-rw-r--r--sound/soc/codecs/ak4671.c6
-rw-r--r--sound/soc/codecs/ak5558.c10
-rw-r--r--sound/soc/codecs/alc5623.c8
-rw-r--r--sound/soc/codecs/alc5632.c8
-rw-r--r--sound/soc/codecs/cpcap.c18
-rw-r--r--sound/soc/codecs/cros_ec_codec.c4
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c115
-rw-r--r--sound/soc/codecs/cs35l41-spi.c140
-rw-r--r--sound/soc/codecs/cs35l41-tables.c594
-rw-r--r--sound/soc/codecs/cs35l41.c1445
-rw-r--r--sound/soc/codecs/cs35l41.h775
-rw-r--r--sound/soc/codecs/cs42l42.c465
-rw-r--r--sound/soc/codecs/cs42l42.h64
-rw-r--r--sound/soc/codecs/cs47l15.c26
-rw-r--r--sound/soc/codecs/cs47l24.c20
-rw-r--r--sound/soc/codecs/cs47l35.c26
-rw-r--r--sound/soc/codecs/cs47l85.c34
-rw-r--r--sound/soc/codecs/cs47l90.c36
-rw-r--r--sound/soc/codecs/cs47l92.c20
-rw-r--r--sound/soc/codecs/es8316.c8
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c3
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c25
-rw-r--r--sound/soc/codecs/lpass-va-macro.c3
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c1
-rw-r--r--sound/soc/codecs/madera.c18
-rw-r--r--sound/soc/codecs/max98390.c2
-rw-r--r--sound/soc/codecs/max98520.c769
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max98927.c25
-rw-r--r--sound/soc/codecs/max98927.h1
-rw-r--r--sound/soc/codecs/mt6359.c2
-rw-r--r--sound/soc/codecs/nau8821.c1714
-rw-r--r--sound/soc/codecs/nau8821.h533
-rw-r--r--sound/soc/codecs/nau8824.c70
-rw-r--r--sound/soc/codecs/nau8824.h3
-rw-r--r--sound/soc/codecs/nau8825.c48
-rw-r--r--sound/soc/codecs/pcm5102a.c2
-rw-r--r--sound/soc/codecs/rt1011.c10
-rw-r--r--sound/soc/codecs/rt1015.c2
-rw-r--r--sound/soc/codecs/rt1016.c2
-rw-r--r--sound/soc/codecs/rt1019.c2
-rw-r--r--sound/soc/codecs/rt1305.c2
-rw-r--r--sound/soc/codecs/rt1308.c2
-rw-r--r--sound/soc/codecs/rt5514.c2
-rw-r--r--sound/soc/codecs/rt5616.c2
-rw-r--r--sound/soc/codecs/rt5640.c2
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/rt5651.c9
-rw-r--r--sound/soc/codecs/rt5659.c2
-rw-r--r--sound/soc/codecs/rt5660.c2
-rw-r--r--sound/soc/codecs/rt5663.c2
-rw-r--r--sound/soc/codecs/rt5665.c2
-rw-r--r--sound/soc/codecs/rt5668.c2
-rw-r--r--sound/soc/codecs/rt5670.c2
-rw-r--r--sound/soc/codecs/rt5677.c2
-rw-r--r--sound/soc/codecs/rt5682-i2c.c17
-rw-r--r--sound/soc/codecs/rt5682.c132
-rw-r--r--sound/soc/codecs/rt5682.h23
-rw-r--r--sound/soc/codecs/rt5682s.c3197
-rw-r--r--sound/soc/codecs/rt5682s.h1474
-rw-r--r--sound/soc/codecs/rt9120.c495
-rw-r--r--sound/soc/codecs/tfa989x.c21
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c4
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c4
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c4
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h2
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c4
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c4
-rw-r--r--sound/soc/codecs/tlv320aic3x.c3
-rw-r--r--sound/soc/codecs/tlv320aic3x.h2
-rw-r--r--sound/soc/codecs/wcd9335.c2
-rw-r--r--sound/soc/codecs/wm2200.c30
-rw-r--r--sound/soc/codecs/wm5102.c16
-rw-r--r--sound/soc/codecs/wm5110.c24
-rw-r--r--sound/soc/codecs/wm8731.c6
-rw-r--r--sound/soc/codecs/wm8900.c6
-rw-r--r--sound/soc/codecs/wm8962.c13
-rw-r--r--sound/soc/codecs/wm_adsp.c3173
-rw-r--r--sound/soc/codecs/wm_adsp.h105
-rw-r--r--sound/soc/codecs/wmfw.h200
-rw-r--r--sound/soc/codecs/zl38060.c4
-rw-r--r--sound/soc/dwc/dwc-i2s.c12
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c2
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c54
-rw-r--r--sound/soc/fsl/fsl_audmix.c8
-rw-r--r--sound/soc/fsl/fsl_esai.c28
-rw-r--r--sound/soc/fsl/fsl_mqs.c4
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c47
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h12
-rw-r--r--sound/soc/fsl/fsl_sai.c34
-rw-r--r--sound/soc/fsl/fsl_sai.h2
-rw-r--r--sound/soc/fsl/fsl_spdif.c85
-rw-r--r--sound/soc/fsl/fsl_ssi.c38
-rw-r--r--sound/soc/fsl/imx-audmix.c12
-rw-r--r--sound/soc/fsl/imx-card.c6
-rw-r--r--sound/soc/fsl/imx-es8328.c2
-rw-r--r--sound/soc/fsl/imx-hdmi.c6
-rw-r--r--sound/soc/fsl/imx-rpmsg.c2
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c2
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c16
-rw-r--r--sound/soc/fsl/p1022_ds.c16
-rw-r--r--sound/soc/fsl/p1022_rdk.c2
-rw-r--r--sound/soc/generic/Kconfig20
-rw-r--r--sound/soc/generic/Makefile6
-rw-r--r--sound/soc/generic/audio-graph-card.c4
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.c183
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.dtsi227
-rw-r--r--sound/soc/generic/audio-graph-card2.c1281
-rw-r--r--sound/soc/generic/simple-card-utils.c50
-rw-r--r--sound/soc/generic/test-component.c659
-rw-r--r--sound/soc/intel/boards/Kconfig18
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c2
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c2
-rw-r--r--sound/soc/intel/boards/broadwell.c2
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c4
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c2
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c4
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c4
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c4
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c4
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c120
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c122
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c4
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c4
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c4
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c6
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c2
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c56
-rw-r--r--sound/soc/intel/boards/haswell.c2
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98357a.c4
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98927.c6
-rw-r--r--sound/soc/intel/boards/kbl_rt5660.c2
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c6
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c4
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c4
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c4
-rw-r--r--sound/soc/intel/boards/skl_rt286.c2
-rw-r--r--sound/soc/intel/boards/sof_es8336.c569
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c96
-rw-r--r--sound/soc/intel/boards/sof_sdw.c10
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c24
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c8
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-byt-match.c68
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cht-match.c69
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c8
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c17
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c27
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-kbl-match.c12
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-skl-match.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c60
-rw-r--r--sound/soc/intel/skylake/skl-topology.c6
-rw-r--r--sound/soc/mediatek/Kconfig23
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c3
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c2
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c2
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c2
-rw-r--r--sound/soc/mediatek/mt8195/Makefile1
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c10
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.c152
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c8
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c4
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c1155
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c54
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c2
-rw-r--r--sound/soc/meson/axg-card.c1
-rw-r--r--sound/soc/meson/axg-tdm-interface.c26
-rw-r--r--sound/soc/meson/meson-card-utils.c1
-rw-r--r--sound/soc/meson/meson-codec-glue.c3
-rw-r--r--sound/soc/qcom/Kconfig25
-rw-r--r--sound/soc/qcom/apq8096.c2
-rw-r--r--sound/soc/qcom/common.c4
-rw-r--r--sound/soc/qcom/qdsp6/Makefile11
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c1130
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h726
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c187
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c689
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c416
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c260
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c822
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h152
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c186
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h30
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c627
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h22
-rw-r--r--sound/soc/qcom/qdsp6/q6prm-clocks.c85
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c202
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.h78
-rw-r--r--sound/soc/qcom/qdsp6/topology.c1113
-rw-r--r--sound/soc/qcom/sm8250.c79
-rw-r--r--sound/soc/rockchip/Kconfig11
-rw-r--r--sound/soc/rockchip/Makefile5
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c3
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c1762
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.h398
-rw-r--r--sound/soc/rockchip/rockchip_pcm.c44
-rw-r--r--sound/soc/rockchip/rockchip_pcm.h11
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c112
-rw-r--r--sound/soc/rockchip/rockchip_pdm.h6
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c2
-rw-r--r--sound/soc/sh/rcar/core.c1
-rw-r--r--sound/soc/soc-acpi.c24
-rw-r--r--sound/soc/soc-component.c87
-rw-r--r--sound/soc/soc-compress.c43
-rw-r--r--sound/soc/soc-core.c54
-rw-r--r--sound/soc/soc-dapm.c2
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c6
-rw-r--r--sound/soc/soc-pcm.c27
-rw-r--r--sound/soc/soc-topology.c52
-rw-r--r--sound/soc/soc-utils.c13
-rw-r--r--sound/soc/sof/Kconfig6
-rw-r--r--sound/soc/sof/Makefile6
-rw-r--r--sound/soc/sof/compress.c158
-rw-r--r--sound/soc/sof/compress.h32
-rw-r--r--sound/soc/sof/control.c192
-rw-r--r--sound/soc/sof/core.c35
-rw-r--r--sound/soc/sof/debug.c87
-rw-r--r--sound/soc/sof/imx/Kconfig2
-rw-r--r--sound/soc/sof/imx/imx-ops.h10
-rw-r--r--sound/soc/sof/imx/imx8.c47
-rw-r--r--sound/soc/sof/imx/imx8m.c41
-rw-r--r--sound/soc/sof/intel/Makefile5
-rw-r--r--sound/soc/sof/intel/apl.c7
-rw-r--r--sound/soc/sof/intel/atom.c5
-rw-r--r--sound/soc/sof/intel/bdw.c19
-rw-r--r--sound/soc/sof/intel/byt.c30
-rw-r--r--sound/soc/sof/intel/cnl.c7
-rw-r--r--sound/soc/sof/intel/hda-dai.c269
-rw-r--r--sound/soc/sof/intel/hda-dsp.c61
-rw-r--r--sound/soc/sof/intel/hda-ipc.c15
-rw-r--r--sound/soc/sof/intel/hda-loader.c11
-rw-r--r--sound/soc/sof/intel/hda-probes.c (renamed from sound/soc/sof/intel/hda-compress.c)0
-rw-r--r--sound/soc/sof/intel/hda-stream.c92
-rw-r--r--sound/soc/sof/intel/hda.c232
-rw-r--r--sound/soc/sof/intel/hda.h52
-rw-r--r--sound/soc/sof/intel/icl.c7
-rw-r--r--sound/soc/sof/intel/pci-apl.c2
-rw-r--r--sound/soc/sof/intel/pci-cnl.c3
-rw-r--r--sound/soc/sof/intel/pci-icl.c2
-rw-r--r--sound/soc/sof/intel/pci-tgl.c5
-rw-r--r--sound/soc/sof/intel/pci-tng.c16
-rw-r--r--sound/soc/sof/intel/tgl.c7
-rw-r--r--sound/soc/sof/ipc.c217
-rw-r--r--sound/soc/sof/loader.c161
-rw-r--r--sound/soc/sof/ops.c3
-rw-r--r--sound/soc/sof/ops.h77
-rw-r--r--sound/soc/sof/pcm.c71
-rw-r--r--sound/soc/sof/pm.c12
-rw-r--r--sound/soc/sof/probe.h85
-rw-r--r--sound/soc/sof/sof-audio.c715
-rw-r--r--sound/soc/sof/sof-audio.h52
-rw-r--r--sound/soc/sof/sof-of-dev.c24
-rw-r--r--sound/soc/sof/sof-priv.h154
-rw-r--r--sound/soc/sof/sof-probes.c (renamed from sound/soc/sof/probe.c)280
-rw-r--r--sound/soc/sof/sof-probes.h38
-rw-r--r--sound/soc/sof/stream-ipc.c (renamed from sound/soc/sof/intel/intel-ipc.c)56
-rw-r--r--sound/soc/sof/topology.c417
-rw-r--r--sound/soc/sof/trace.c5
-rw-r--r--sound/soc/sof/utils.c28
-rw-r--r--sound/soc/sof/xtensa/core.c2
-rw-r--r--sound/soc/tegra/Kconfig48
-rw-r--r--sound/soc/tegra/Makefile10
-rw-r--r--sound/soc/tegra/tegra210_adx.c531
-rw-r--r--sound/soc/tegra/tegra210_adx.h72
-rw-r--r--sound/soc/tegra/tegra210_ahub.c511
-rw-r--r--sound/soc/tegra/tegra210_amx.c600
-rw-r--r--sound/soc/tegra/tegra210_amx.h93
-rw-r--r--sound/soc/tegra/tegra210_mixer.c674
-rw-r--r--sound/soc/tegra/tegra210_mixer.h100
-rw-r--r--sound/soc/tegra/tegra210_mvc.c645
-rw-r--r--sound/soc/tegra/tegra210_mvc.h117
-rw-r--r--sound/soc/tegra/tegra210_sfc.c3549
-rw-r--r--sound/soc/tegra/tegra210_sfc.h78
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c62
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.h1
-rw-r--r--sound/soc/ti/Kconfig2
-rw-r--r--sound/soc/ti/davinci-evm.c2
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c2
-rw-r--r--sound/soc/ux500/mop500_ab8500.c2
-rw-r--r--sound/soc/ux500/mop500_ab8500.h2
-rw-r--r--sound/usb/6fire/comm.c2
-rw-r--r--sound/usb/6fire/firmware.c6
-rw-r--r--sound/usb/card.h11
-rw-r--r--sound/usb/clock.c8
-rw-r--r--sound/usb/endpoint.c230
-rw-r--r--sound/usb/endpoint.h13
-rw-r--r--sound/usb/format.c1
-rw-r--r--sound/usb/implicit.c2
-rw-r--r--sound/usb/line6/driver.c14
-rw-r--r--sound/usb/line6/driver.h2
-rw-r--r--sound/usb/line6/podhd.c6
-rw-r--r--sound/usb/line6/toneport.c2
-rw-r--r--sound/usb/misc/ua101.c4
-rw-r--r--sound/usb/mixer.c42
-rw-r--r--sound/usb/mixer_quirks.c34
-rw-r--r--sound/usb/pcm.c164
-rw-r--r--sound/usb/quirks-table.h58
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c11
-rw-r--r--sound/virtio/virtio_pcm_msg.c5
391 files changed, 41478 insertions, 7387 deletions
diff --git a/sound/core/Makefile b/sound/core/Makefile
index d774792850f3..350d704ced98 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -9,7 +9,9 @@ ifneq ($(CONFIG_SND_PROC_FS),)
snd-y += info.o
snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
endif
+ifneq ($(CONFIG_M68K),y)
snd-$(CONFIG_ISA_DMA_API) += isadma.o
+endif
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
@@ -17,7 +19,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
pcm_memory.o memalloc.o
snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
-snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index c7c943c661e6..99cd0f67daa1 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/genalloc.h>
+#include <linux/highmem.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_X86
#include <asm/set_memory.h>
@@ -39,9 +40,11 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
}
/**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
+ * type and direction
* @type: the DMA buffer type
* @device: the device pointer
+ * @dir: DMA direction
* @size: the buffer size to allocate
* @dmab: buffer allocation record to store the allocated data
*
@@ -51,8 +54,9 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
* Return: Zero if the buffer with the given size is allocated successfully,
* otherwise a negative value on error.
*/
-int snd_dma_alloc_pages(int type, struct device *device, size_t size,
- struct snd_dma_buffer *dmab)
+int snd_dma_alloc_dir_pages(int type, struct device *device,
+ enum dma_data_direction dir, size_t size,
+ struct snd_dma_buffer *dmab)
{
if (WARN_ON(!size))
return -ENXIO;
@@ -62,6 +66,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
size = PAGE_ALIGN(size);
dmab->dev.type = type;
dmab->dev.dev = device;
+ dmab->dev.dir = dir;
dmab->bytes = 0;
dmab->addr = 0;
dmab->private_data = NULL;
@@ -71,7 +76,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
dmab->bytes = size;
return 0;
}
-EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
/**
* snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -129,9 +134,10 @@ static void __snd_release_pages(struct device *dev, void *res)
}
/**
- * snd_devm_alloc_pages - allocate the buffer and manage with devres
+ * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
* @dev: the device pointer
* @type: the DMA buffer type
+ * @dir: DMA direction
* @size: the buffer size to allocate
*
* Allocate buffer pages depending on the given type and manage using devres.
@@ -144,7 +150,8 @@ static void __snd_release_pages(struct device *dev, void *res)
* The function returns the snd_dma_buffer object at success, or NULL if failed.
*/
struct snd_dma_buffer *
-snd_devm_alloc_pages(struct device *dev, int type, size_t size)
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+ enum dma_data_direction dir, size_t size)
{
struct snd_dma_buffer *dmab;
int err;
@@ -157,7 +164,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
if (!dmab)
return NULL;
- err = snd_dma_alloc_pages(type, dev, size, dmab);
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
if (err < 0) {
devres_free(dmab);
return NULL;
@@ -166,7 +173,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
devres_add(dev, dmab);
return dmab;
}
-EXPORT_SYMBOL_GPL(snd_devm_alloc_pages);
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
/**
* snd_dma_buffer_mmap - perform mmap of the given DMA buffer
@@ -185,6 +192,26 @@ int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
}
EXPORT_SYMBOL(snd_dma_buffer_mmap);
+#ifdef CONFIG_HAS_DMA
+/**
+ * snd_dma_buffer_sync - sync DMA buffer between CPU and device
+ * @dmab: buffer allocation information
+ * @mode: sync mode
+ */
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ const struct snd_malloc_ops *ops;
+
+ if (!dmab || !dmab->dev.need_sync)
+ return;
+ ops = snd_dma_get_ops(dmab);
+ if (ops && ops->sync)
+ ops->sync(dmab, mode);
+}
+EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
+#endif /* CONFIG_HAS_DMA */
+
/**
* snd_sgbuf_get_addr - return the physical address at the corresponding offset
* @dmab: buffer allocation information
@@ -468,6 +495,161 @@ static const struct snd_malloc_ops snd_dma_wc_ops = {
.mmap = snd_dma_wc_mmap,
};
#endif /* CONFIG_X86 */
+
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct sg_table *sgt;
+ void *p;
+
+ sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+ DEFAULT_GFP, 0);
+ if (!sgt)
+ return NULL;
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
+ p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
+ if (p)
+ dmab->private_data = sgt;
+ else
+ dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
+ return p;
+}
+
+static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
+{
+ dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
+ dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
+ dmab->dev.dir);
+}
+
+static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_noncontiguous(dmab->dev.dev, area,
+ dmab->bytes, dmab->private_data);
+}
+
+static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir == DMA_TO_DEVICE)
+ return;
+ dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
+ } else {
+ if (dmab->dev.dir == DMA_FROM_DEVICE)
+ return;
+ flush_kernel_vmap_range(dmab->area, dmab->bytes);
+ dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ }
+}
+
+static const struct snd_malloc_ops snd_dma_noncontig_ops = {
+ .alloc = snd_dma_noncontig_alloc,
+ .free = snd_dma_noncontig_free,
+ .mmap = snd_dma_noncontig_mmap,
+ .sync = snd_dma_noncontig_sync,
+ /* re-use vmalloc helpers for get_* ops */
+ .get_addr = snd_dma_vmalloc_get_addr,
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+
+/* x86-specific SG-buffer with WC pages */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define vmalloc_to_virt(v) (unsigned long)page_to_virt(vmalloc_to_page(v))
+
+static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p = snd_dma_noncontig_alloc(dmab, size);
+ size_t ofs;
+
+ if (!p)
+ return NULL;
+ for (ofs = 0; ofs < size; ofs += PAGE_SIZE)
+ set_memory_uc(vmalloc_to_virt(p + ofs), 1);
+ return p;
+}
+
+static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
+{
+ size_t ofs;
+
+ for (ofs = 0; ofs < dmab->bytes; ofs += PAGE_SIZE)
+ set_memory_wb(vmalloc_to_virt(dmab->area + ofs), 1);
+ snd_dma_noncontig_free(dmab);
+}
+
+static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ /* FIXME: dma_mmap_noncontiguous() works? */
+ return -ENOENT; /* continue with the default mmap handler */
+}
+
+const struct snd_malloc_ops snd_dma_sg_wc_ops = {
+ .alloc = snd_dma_sg_wc_alloc,
+ .free = snd_dma_sg_wc_free,
+ .mmap = snd_dma_sg_wc_mmap,
+ .sync = snd_dma_noncontig_sync,
+ .get_addr = snd_dma_vmalloc_get_addr,
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
+
+/*
+ * Non-coherent pages allocator
+ */
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
+ return dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+ dmab->dev.dir, DEFAULT_GFP);
+}
+
+static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
+ dmab->addr, dmab->dev.dir);
+}
+
+static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = vm_get_page_prot(area->vm_flags);
+ return dma_mmap_pages(dmab->dev.dev, area,
+ area->vm_end - area->vm_start,
+ virt_to_page(dmab->area));
+}
+
+static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir != DMA_TO_DEVICE)
+ dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ } else {
+ if (dmab->dev.dir != DMA_FROM_DEVICE)
+ dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ }
+}
+
+static const struct snd_malloc_ops snd_dma_noncoherent_ops = {
+ .alloc = snd_dma_noncoherent_alloc,
+ .free = snd_dma_noncoherent_free,
+ .mmap = snd_dma_noncoherent_mmap,
+ .sync = snd_dma_noncoherent_sync,
+};
+
#endif /* CONFIG_HAS_DMA */
/*
@@ -479,14 +661,15 @@ static const struct snd_malloc_ops *dma_ops[] = {
#ifdef CONFIG_HAS_DMA
[SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
[SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
+ [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
+ [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
+#ifdef CONFIG_SND_DMA_SGBUF
+ [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops,
+#endif
#ifdef CONFIG_GENERIC_ALLOCATOR
[SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
#endif /* CONFIG_GENERIC_ALLOCATOR */
#endif /* CONFIG_HAS_DMA */
-#ifdef CONFIG_SND_DMA_SGBUF
- [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops,
- [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops,
-#endif
};
static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h
index 9f2e0a608b49..a6f3a87194da 100644
--- a/sound/core/memalloc_local.h
+++ b/sound/core/memalloc_local.h
@@ -10,6 +10,7 @@ struct snd_malloc_ops {
unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab,
unsigned int ofs, unsigned int size);
int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area);
+ void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode);
};
#ifdef CONFIG_SND_DMA_SGBUF
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 6a5abdd4271b..9620115cfdc0 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -130,11 +130,13 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume || pslot->put_recsrc)
result |= 1 << chn;
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -146,11 +148,13 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume && pslot->stereo)
result |= 1 << chn;
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -161,6 +165,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
result = mixer->mask_recsrc;
} else {
@@ -172,6 +177,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
result |= 1 << chn;
}
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -182,12 +188,12 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
- int err;
unsigned int index;
- err = mixer->get_recsrc(fmixer, &index);
- if (err < 0)
- return err;
+ result = mixer->get_recsrc(fmixer, &index);
+ if (result < 0)
+ goto unlock;
result = 1 << index;
} else {
struct snd_mixer_oss_slot *pslot;
@@ -202,7 +208,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
}
}
}
- return mixer->oss_recsrc = result;
+ mixer->oss_recsrc = result;
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
+ return result;
}
static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
@@ -215,6 +224,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
if (recsrc & ~mixer->oss_recsrc)
recsrc &= ~mixer->oss_recsrc;
@@ -240,6 +250,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
}
}
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -251,6 +262,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
if (mixer == NULL || slot > 30)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
left = pslot->volume[0];
right = pslot->volume[1];
@@ -258,15 +270,21 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
result = pslot->get_volume(fmixer, pslot, &left, &right);
if (!pslot->stereo)
right = left;
- if (snd_BUG_ON(left < 0 || left > 100))
- return -EIO;
- if (snd_BUG_ON(right < 0 || right > 100))
- return -EIO;
+ if (snd_BUG_ON(left < 0 || left > 100)) {
+ result = -EIO;
+ goto unlock;
+ }
+ if (snd_BUG_ON(right < 0 || right > 100)) {
+ result = -EIO;
+ goto unlock;
+ }
if (result >= 0) {
pslot->volume[0] = left;
pslot->volume[1] = right;
result = (left & 0xff) | ((right & 0xff) << 8);
}
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -279,6 +297,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (mixer == NULL || slot > 30)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
if (left > 100)
left = 100;
@@ -289,10 +308,13 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (pslot->put_volume)
result = pslot->put_volume(fmixer, pslot, left, right);
if (result < 0)
- return result;
+ goto unlock;
pslot->volume[0] = left;
pslot->volume[1] = right;
- return (left & 0xff) | ((right & 0xff) << 8);
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
+ return result;
}
static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index dfe5a64e19d2..e4e176854ce7 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -453,6 +453,8 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
sstatus.suspended_state = status->suspended_state;
sstatus.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
@@ -533,6 +535,8 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream,
sync_ptr.s.status.suspended_state = status->suspended_state;
sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
return -EFAULT;
return 0;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a144a3f68e9e..4f4b4739f987 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -106,6 +106,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
frames -= transfer;
ofs = 0;
}
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
}
#ifdef CONFIG_SND_DEBUG
@@ -2256,8 +2257,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
goto _end_unlock;
}
snd_pcm_stream_unlock_irq(substream);
+ if (!is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
err = writer(substream, appl_ofs, data, offset, frames,
transfer);
+ if (is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
snd_pcm_stream_lock_irq(substream);
if (err < 0)
goto _end_unlock;
diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h
index fe9689b8a6a6..ecb21697ae3a 100644
--- a/sound/core/pcm_local.h
+++ b/sound/core/pcm_local.h
@@ -73,4 +73,11 @@ void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
for ((subs) = (pcm)->streams[str].substream; (subs); \
(subs) = (subs)->next)
+static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
+ enum snd_dma_sync_mode mode)
+{
+ if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
+}
+
#endif /* __SOUND_CORE_PCM_LOCAL_H */
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 7fbd1ccbb5b0..b70ce3b69ab4 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -32,15 +32,20 @@ module_param(max_alloc_per_card, ulong, 0644);
MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
- size_t size, struct snd_dma_buffer *dmab)
+ int str, size_t size, struct snd_dma_buffer *dmab)
{
+ enum dma_data_direction dir;
int err;
if (max_alloc_per_card &&
card->total_pcm_alloc_bytes + size > max_alloc_per_card)
return -ENOMEM;
- err = snd_dma_alloc_pages(type, dev, size, dmab);
+ if (str == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = DMA_TO_DEVICE;
+ else
+ dir = DMA_FROM_DEVICE;
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
if (!err) {
mutex_lock(&card->memory_mutex);
card->total_pcm_alloc_bytes += dmab->bytes;
@@ -77,7 +82,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream,
do {
err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
- size, dmab);
+ substream->stream, size, dmab);
if (err != -ENOMEM)
return err;
if (no_fallback)
@@ -177,6 +182,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
if (do_alloc_pages(card,
substream->dma_buffer.dev.type,
substream->dma_buffer.dev.dev,
+ substream->stream,
size, &new_dmab) < 0) {
buffer->error = -ENOMEM;
pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
@@ -418,6 +424,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
if (do_alloc_pages(card,
substream->dma_buffer.dev.type,
substream->dma_buffer.dev.dev,
+ substream->stream,
size, dmab) < 0) {
kfree(dmab);
pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index d233cb3b41d8..621883e71194 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2685,6 +2685,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
goto error;
}
+ /* automatically set EXPLICIT_SYNC flag in the managed mode whenever
+ * the DMA buffer requires it
+ */
+ if (substream->managed_buffer_alloc &&
+ substream->dma_buffer.dev.need_sync)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC;
+
*rsubstream = substream;
return 0;
@@ -2912,6 +2919,8 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
ret = rewind_appl_ptr(substream, frames,
snd_pcm_hw_avail(substream));
snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
@@ -2929,35 +2938,31 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
ret = forward_appl_ptr(substream, frames,
snd_pcm_avail(substream));
snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
-{
- int err;
-
- snd_pcm_stream_lock_irq(substream);
- err = do_pcm_hwsync(substream);
- snd_pcm_stream_unlock_irq(substream);
- return err;
-}
-
static int snd_pcm_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *delay)
{
int err;
- snd_pcm_sframes_t n = 0;
snd_pcm_stream_lock_irq(substream);
err = do_pcm_hwsync(substream);
- if (!err)
- n = snd_pcm_calc_delay(substream);
+ if (delay && !err)
+ *delay = snd_pcm_calc_delay(substream);
snd_pcm_stream_unlock_irq(substream);
- if (!err)
- *delay = n;
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+
return err;
}
+static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_delay(substream, NULL);
+}
+
static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
struct snd_pcm_sync_ptr __user *_sync_ptr)
{
@@ -3000,6 +3005,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
sync_ptr.s.status.suspended_state = status->suspended_state;
sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
return -EFAULT;
return 0;
@@ -3096,6 +3103,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
sstatus.suspended_state = status->suspended_state;
sstatus.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
@@ -3218,6 +3227,9 @@ static int snd_pcm_common_ioctl(struct file *file,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
res = snd_power_wait(substream->pcm->card);
if (res < 0)
return res;
@@ -3272,7 +3284,7 @@ static int snd_pcm_common_ioctl(struct file *file,
return snd_pcm_hwsync(substream);
case SNDRV_PCM_IOCTL_DELAY:
{
- snd_pcm_sframes_t delay;
+ snd_pcm_sframes_t delay = 0;
snd_pcm_sframes_t __user *res = arg;
int err;
@@ -3344,6 +3356,9 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
snd_pcm_uframes_t *frames = arg;
snd_pcm_sframes_t result;
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
switch (cmd) {
case SNDRV_PCM_IOCTL_FORWARD:
{
@@ -3386,7 +3401,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -3410,7 +3426,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -3436,7 +3453,8 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!iter_is_iovec(to))
return -EINVAL;
@@ -3472,7 +3490,8 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!iter_is_iovec(from))
return -EINVAL;
@@ -3511,6 +3530,9 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
return ok | EPOLLERR;
runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return ok | EPOLLERR;
+
poll_wait(file, &runtime->sleep, wait);
mask = 0;
@@ -3820,6 +3842,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
@@ -3856,6 +3880,8 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
return fasync_helper(fd, file, on, &runtime->fasync);
}
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644
index 8352a5cdb19f..000000000000
--- a/sound/core/sgbuf.c
+++ /dev/null
@@ -1,201 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Scatter-Gather buffer
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/export.h>
-#include <sound/memalloc.h>
-#include "memalloc_local.h"
-
-struct snd_sg_page {
- void *buf;
- dma_addr_t addr;
-};
-
-struct snd_sg_buf {
- int size; /* allocated byte size */
- int pages; /* allocated pages */
- int tblsize; /* allocated table size */
- struct snd_sg_page *table; /* address table */
- struct page **page_table; /* page table (for vmap/vunmap) */
- struct device *dev;
-};
-
-/* table entries are align to 32 */
-#define SGBUF_TBL_ALIGN 32
-#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-
-static void snd_dma_sg_free(struct snd_dma_buffer *dmab)
-{
- struct snd_sg_buf *sgbuf = dmab->private_data;
- struct snd_dma_buffer tmpb;
- int i;
-
- if (!sgbuf)
- return;
-
- vunmap(dmab->area);
- dmab->area = NULL;
-
- tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
- if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
- tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC;
- tmpb.dev.dev = sgbuf->dev;
- for (i = 0; i < sgbuf->pages; i++) {
- if (!(sgbuf->table[i].addr & ~PAGE_MASK))
- continue; /* continuous pages */
- tmpb.area = sgbuf->table[i].buf;
- tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
- tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
- snd_dma_free_pages(&tmpb);
- }
-
- kfree(sgbuf->table);
- kfree(sgbuf->page_table);
- kfree(sgbuf);
- dmab->private_data = NULL;
-}
-
-#define MAX_ALLOC_PAGES 32
-
-static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
-{
- struct snd_sg_buf *sgbuf;
- unsigned int i, pages, chunk, maxpages;
- struct snd_dma_buffer tmpb;
- struct snd_sg_page *table;
- struct page **pgtable;
- int type = SNDRV_DMA_TYPE_DEV;
- pgprot_t prot = PAGE_KERNEL;
- void *area;
-
- dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
- if (!sgbuf)
- return NULL;
- if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) {
- type = SNDRV_DMA_TYPE_DEV_WC;
-#ifdef pgprot_noncached
- prot = pgprot_noncached(PAGE_KERNEL);
-#endif
- }
- sgbuf->dev = dmab->dev.dev;
- pages = snd_sgbuf_aligned_pages(size);
- sgbuf->tblsize = sgbuf_align_table(pages);
- table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
- if (!table)
- goto _failed;
- sgbuf->table = table;
- pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
- if (!pgtable)
- goto _failed;
- sgbuf->page_table = pgtable;
-
- /* allocate pages */
- maxpages = MAX_ALLOC_PAGES;
- while (pages > 0) {
- chunk = pages;
- /* don't be too eager to take a huge chunk */
- if (chunk > maxpages)
- chunk = maxpages;
- chunk <<= PAGE_SHIFT;
- if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev,
- chunk, &tmpb) < 0) {
- if (!sgbuf->pages)
- goto _failed;
- size = sgbuf->pages * PAGE_SIZE;
- break;
- }
- chunk = tmpb.bytes >> PAGE_SHIFT;
- for (i = 0; i < chunk; i++) {
- table->buf = tmpb.area;
- table->addr = tmpb.addr;
- if (!i)
- table->addr |= chunk; /* mark head */
- table++;
- *pgtable++ = virt_to_page(tmpb.area);
- tmpb.area += PAGE_SIZE;
- tmpb.addr += PAGE_SIZE;
- }
- sgbuf->pages += chunk;
- pages -= chunk;
- if (chunk < maxpages)
- maxpages = chunk;
- }
-
- sgbuf->size = size;
- area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot);
- if (!area)
- goto _failed;
- return area;
-
- _failed:
- snd_dma_sg_free(dmab); /* free the table */
- return NULL;
-}
-
-static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab,
- size_t offset)
-{
- struct snd_sg_buf *sgbuf = dmab->private_data;
- dma_addr_t addr;
-
- addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
- addr &= ~((dma_addr_t)PAGE_SIZE - 1);
- return addr + offset % PAGE_SIZE;
-}
-
-static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab,
- size_t offset)
-{
- struct snd_sg_buf *sgbuf = dmab->private_data;
- unsigned int idx = offset >> PAGE_SHIFT;
-
- if (idx >= (unsigned int)sgbuf->pages)
- return NULL;
- return sgbuf->page_table[idx];
-}
-
-static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab,
- unsigned int ofs,
- unsigned int size)
-{
- struct snd_sg_buf *sg = dmab->private_data;
- unsigned int start, end, pg;
-
- start = ofs >> PAGE_SHIFT;
- end = (ofs + size - 1) >> PAGE_SHIFT;
- /* check page continuity */
- pg = sg->table[start].addr >> PAGE_SHIFT;
- for (;;) {
- start++;
- if (start > end)
- break;
- pg++;
- if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
- return (start << PAGE_SHIFT) - ofs;
- }
- /* ok, all on continuous pages */
- return size;
-}
-
-static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab,
- struct vm_area_struct *area)
-{
- if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
- area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
- return -ENOENT; /* continue with the default mmap handler */
-}
-
-const struct snd_malloc_ops snd_dma_sg_ops = {
- .alloc = snd_dma_sg_alloc,
- .free = snd_dma_sg_free,
- .get_addr = snd_dma_sg_get_addr,
- .get_page = snd_dma_sg_get_page,
- .get_chunk_size = snd_dma_sg_get_chunk_size,
- .mmap = snd_dma_sg_mmap,
-};
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index acdf66564fb0..3bef2a0b1e2e 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -4,5 +4,6 @@ CFLAGS_amdtp-motu.o := -I$(src)
snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
motu-protocol-v2.o motu-protocol-v3.o \
- motu-protocol-v1.o
+ motu-protocol-v1.o motu-register-dsp-message-parser.o \
+ motu-command-dsp-message-parser.o
obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index a18c2c033e83..2fb52f481d12 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -333,6 +333,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
struct amdtp_motu *p = s->protocol;
unsigned int pcm_frames = 0;
int i;
@@ -357,6 +358,14 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
read_midi_messages(s, buf, data_blocks);
}
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ snd_motu_register_dsp_message_parser_parse(motu, descs, packets,
+ s->data_block_quadlets);
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ snd_motu_command_dsp_message_parser_parse(motu, descs, packets,
+ s->data_block_quadlets);
+ }
+
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
@@ -415,8 +424,6 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
if (p->midi_ports)
write_midi_messages(s, buf, data_blocks);
- // TODO: how to interact control messages between userspace?
-
write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
}
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
new file mode 100644
index 000000000000..9efe4d364baf
--- /dev/null
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP function by command transferred in
+// asynchronous transaction:
+// * 828 mk3 (FireWire only and Hybrid)
+// * 896 mk3 (FireWire only and Hybrid)
+// * Ultralite mk3 (FireWire only and Hybrid)
+// * Traveler mk3
+// * Track 16
+//
+// Isochronous packets from the above models includes messages to report state of hardware meter.
+
+#include "motu.h"
+
+enum msg_parser_state {
+ INITIALIZED,
+ FRAGMENT_DETECTED,
+ AVAILABLE,
+};
+
+struct msg_parser {
+ spinlock_t lock;
+ enum msg_parser_state state;
+ unsigned int interval;
+ unsigned int message_count;
+ unsigned int fragment_pos;
+ unsigned int value_index;
+ u64 value;
+ struct snd_firewire_motu_command_dsp_meter meter;
+};
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ motu->message_parser = parser;
+
+ return 0;
+}
+
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->state = INITIALIZED;
+
+ // All of data blocks don't have messages with meaningful information.
+ switch (sfc) {
+ case CIP_SFC_176400:
+ case CIP_SFC_192000:
+ parser->interval = 4;
+ break;
+ case CIP_SFC_88200:
+ case CIP_SFC_96000:
+ parser->interval = 2;
+ break;
+ case CIP_SFC_32000:
+ case CIP_SFC_44100:
+ case CIP_SFC_48000:
+ default:
+ parser->interval = 1;
+ break;
+ }
+
+ return 0;
+}
+
+#define FRAGMENT_POS 6
+#define MIDI_BYTE_POS 7
+#define MIDI_FLAG_POS 8
+// One value of hardware meter consists of 4 messages.
+#define FRAGMENTS_PER_VALUE 4
+#define VALUES_AT_IMAGE_END 0xffffffffffffffff
+
+void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+ unsigned int desc_count, unsigned int data_block_quadlets)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int interval = parser->interval;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < desc_count; ++i) {
+ const struct pkt_desc *desc = descs + i;
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ buffer += data_block_quadlets;
+
+ switch (parser->state) {
+ case INITIALIZED:
+ {
+ u8 fragment = b[FRAGMENT_POS];
+
+ if (fragment > 0) {
+ parser->value = fragment;
+ parser->message_count = 1;
+ parser->state = FRAGMENT_DETECTED;
+ }
+ break;
+ }
+ case FRAGMENT_DETECTED:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->state = AVAILABLE;
+ parser->fragment_pos = 0;
+ parser->value_index = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ case AVAILABLE:
+ default:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+ ++parser->fragment_pos;
+
+ if (parser->fragment_pos == 4) {
+ // Skip the last two quadlets since they could be
+ // invalid value (0xffffffff) as floating point
+ // number.
+ if (parser->value_index <
+ SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
+ u32 val = (u32)(parser->value >> 32);
+ parser->meter.data[parser->value_index] = val;
+ }
+ ++parser->value_index;
+ parser->fragment_pos = 0;
+ }
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->value_index = 0;
+ parser->fragment_pos = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
index b5ced5d27758..a900fc0e7644 100644
--- a/sound/firewire/motu/motu-hwdep.c
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -16,6 +16,14 @@
#include "motu.h"
+static bool has_dsp_event(struct snd_motu *motu)
+{
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
+ else
+ return false;
+}
+
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
@@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
- while (!motu->dev_lock_changed && motu->msg == 0) {
+ while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&motu->lock);
schedule();
@@ -40,20 +48,46 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (motu->dev_lock_count > 0);
motu->dev_lock_changed = false;
+ spin_unlock_irq(&motu->lock);
- count = min_t(long, count, sizeof(event.lock_status));
- } else {
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (motu->msg > 0) {
event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
event.motu_notification.message = motu->msg;
motu->msg = 0;
+ spin_unlock_irq(&motu->lock);
- count = min_t(long, count, sizeof(event.motu_notification));
- }
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (has_dsp_event(motu)) {
+ size_t consumed = 0;
+ u32 __user *ptr;
+ u32 ev;
- spin_unlock_irq(&motu->lock);
+ spin_unlock_irq(&motu->lock);
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ // Header is filled later.
+ consumed += sizeof(event.motu_register_dsp_change);
+
+ while (consumed < count &&
+ snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
+ ptr = (u32 __user *)(buf + consumed);
+ if (put_user(ev, ptr))
+ return -EFAULT;
+ consumed += sizeof(ev);
+ }
+
+ event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
+ event.motu_register_dsp_change.count =
+ (consumed - sizeof(event.motu_register_dsp_change)) / 4;
+ if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
+ return -EFAULT;
+
+ count = consumed;
+ }
return count;
}
@@ -67,7 +101,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock);
- if (motu->dev_lock_changed || motu->msg)
+ if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
events = EPOLLIN | EPOLLRDNORM;
else
events = 0;
@@ -155,6 +189,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
return hwdep_lock(motu);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(motu);
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
+ {
+ struct snd_firewire_motu_register_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
+ {
+ struct snd_firewire_motu_command_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
+
+ err = copy_to_user((void __user *)arg, param, sizeof(*param));
+ kfree(param);
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
default:
return -ENOIOCTLCMD;
}
@@ -193,5 +292,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu)
hwdep->private_data = motu;
hwdep->exclusive = true;
+ motu->hwdep = hwdep;
+
return 0;
}
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
index 2bd4485e4bc7..a5f70efa2e88 100644
--- a/sound/firewire/motu/motu-protocol-v2.c
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = {
.name = "828mk2",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 0},
.rx_fixed_pcm_chunks = {14, 14, 0},
};
@@ -283,7 +284,7 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = {
const struct snd_motu_spec snd_motu_spec_896hd = {
.name = "896HD",
.protocol_version = SND_MOTU_PROTOCOL_V2,
- // No support for MIDI.
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 8},
.rx_fixed_pcm_chunks = {14, 14, 8},
};
@@ -292,7 +293,8 @@ const struct snd_motu_spec snd_motu_spec_traveler = {
.name = "Traveler",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 8},
.rx_fixed_pcm_chunks = {14, 14, 8},
};
@@ -301,7 +303,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite = {
.name = "UltraLite",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 0},
.rx_fixed_pcm_chunks = {14, 14, 0},
};
@@ -310,7 +313,8 @@ const struct snd_motu_spec snd_motu_spec_8pre = {
.name = "8pre",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
// Two dummy chunks always in the end of data block.
.tx_fixed_pcm_chunks = {10, 10, 0},
.rx_fixed_pcm_chunks = {6, 6, 0},
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
index 56e4504e7ec9..05608e8ca0bc 100644
--- a/sound/firewire/motu/motu-protocol-v3.c
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -261,12 +261,12 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
return 0;
}
-
const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
.name = "828mk3",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
.tx_fixed_pcm_chunks = {18, 18, 14},
.rx_fixed_pcm_chunks = {14, 14, 10},
};
@@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
.name = "828mk3",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
.tx_fixed_pcm_chunks = {18, 18, 14},
.rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
};
@@ -284,7 +285,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
.name = "UltraLiteMk3",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
.tx_fixed_pcm_chunks = {18, 14, 10},
.rx_fixed_pcm_chunks = {14, 14, 14},
};
@@ -293,7 +295,8 @@ const struct snd_motu_spec snd_motu_spec_audio_express = {
.name = "AudioExpress",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {10, 10, 0},
.rx_fixed_pcm_chunks = {10, 10, 0},
};
@@ -301,6 +304,7 @@ const struct snd_motu_spec snd_motu_spec_audio_express = {
const struct snd_motu_spec snd_motu_spec_4pre = {
.name = "4pre",
.protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {10, 10, 0},
.rx_fixed_pcm_chunks = {10, 10, 0},
};
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
new file mode 100644
index 000000000000..0c587567540f
--- /dev/null
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP functions by asynchronous transaction
+// to access their internal registers.
+// * 828 mk2
+// * 896hd
+// * Traveler
+// * 8 pre
+// * Ultralite
+// * 4 pre
+// * Audio Express
+//
+// Additionally, isochronous packets from the above models include messages to notify state of
+// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user
+// operates hardware components such as dial and switch, corresponding messages are transferred.
+// The messages include Hardware metering and MIDI messages as well.
+
+#include "motu.h"
+
+#define MSG_FLAG_POS 4
+#define MSG_FLAG_TYPE_MASK 0xf8
+#define MSG_FLAG_MIDI_MASK 0x01
+#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x06
+#define MSG_FLAG_8PRE 0x00
+#define MSG_FLAG_ULTRALITE 0x04
+#define MSG_FLAG_TRAVELER 0x04
+#define MSG_FLAG_828MK2 0x04
+#define MSG_FLAG_896HD 0x04
+#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_TYPE_SHIFT 3
+#define MSG_VALUE_POS 5
+#define MSG_MIDI_BYTE_POS 6
+#define MSG_METER_IDX_POS 7
+
+// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte
+// is in 7th byte.
+#define MSG_METER_IDX_POS_4PRE_AE 6
+#define MSG_MIDI_BYTE_POS_4PRE_AE 7
+#define MSG_FLAG_MIDI_POS_4PRE_AE 8
+
+enum register_dsp_msg_type {
+ // Used for messages with no information.
+ INVALID = 0x00,
+ MIXER_SELECT = 0x01,
+ MIXER_SRC_GAIN = 0x02,
+ MIXER_SRC_PAN = 0x03,
+ MIXER_SRC_FLAG = 0x04,
+ MIXER_OUTPUT_PAIRED_VOLUME = 0x05,
+ MIXER_OUTPUT_PAIRED_FLAG = 0x06,
+ MAIN_OUTPUT_PAIRED_VOLUME = 0x07,
+ HP_OUTPUT_PAIRED_VOLUME = 0x08,
+ HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,
+ // Transferred by all models but the purpose is still unknown.
+ UNKNOWN_0 = 0x0a,
+ // Specific to 828mk2, 896hd, Traveler.
+ UNKNOWN_2 = 0x0c,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_BOOST = 0x0d,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_NOMINAL_LEVEL = 0x0e,
+ // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).
+ INPUT_GAIN_AND_INVERT = 0x15,
+ // Specific to 4 pre, and Audio express.
+ INPUT_FLAG = 0x16,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_BALANCE = 0x17,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_WIDTH = 0x18,
+ // Transferred by all models. This type of message interposes the series of the other
+ // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and
+ // Traveler, one of physical outputs is selected for the message. The selection is done
+ // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+ METER = 0x1f,
+};
+
+#define EVENT_QUEUE_SIZE 16
+
+struct msg_parser {
+ spinlock_t lock;
+ struct snd_firewire_motu_register_dsp_meter meter;
+ bool meter_pos_quirk;
+
+ struct snd_firewire_motu_register_dsp_parameter param;
+ u8 prev_mixer_src_type;
+ u8 mixer_ch;
+ u8 mixer_src_ch;
+
+ u8 input_ch;
+ u8 prev_msg_type;
+
+ u32 event_queue[EVENT_QUEUE_SIZE];
+ unsigned int push_pos;
+ unsigned int pull_pos;
+};
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)
+ parser->meter_pos_quirk = true;
+ motu->message_parser = parser;
+ return 0;
+}
+
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->prev_mixer_src_type = INVALID;
+ parser->mixer_ch = 0xff;
+ parser->mixer_src_ch = 0xff;
+ parser->prev_msg_type = INVALID;
+
+ return 0;
+}
+
+// Rough implementaion of queue without overrun check.
+static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->push_pos;
+ u32 entry;
+
+ if (!motu->hwdep || motu->hwdep->used == 0)
+ return;
+
+ entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;
+ parser->event_queue[pos] = entry;
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->push_pos = pos;
+}
+
+void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+ unsigned int desc_count, unsigned int data_block_quadlets)
+{
+ struct msg_parser *parser = motu->message_parser;
+ bool meter_pos_quirk = parser->meter_pos_quirk;
+ unsigned int pos = parser->push_pos;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < desc_count; ++i) {
+ const struct pkt_desc *desc = descs + i;
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
+ u8 val = b[MSG_VALUE_POS];
+
+ buffer += data_block_quadlets;
+
+ switch (msg_type) {
+ case MIXER_SELECT:
+ {
+ u8 mixer_ch = val / 0x20;
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ parser->mixer_src_ch = 0;
+ parser->mixer_ch = mixer_ch;
+ }
+ break;
+ }
+ case MIXER_SRC_GAIN:
+ case MIXER_SRC_PAN:
+ case MIXER_SRC_FLAG:
+ case MIXER_SRC_PAIRED_BALANCE:
+ case MIXER_SRC_PAIRED_WIDTH:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+ u8 mixer_src_ch = parser->mixer_src_ch;
+
+ if (msg_type != parser->prev_mixer_src_type)
+ mixer_src_ch = 0;
+ else
+ ++mixer_src_ch;
+ parser->prev_mixer_src_type = msg_type;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&
+ mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {
+ u8 mixer_ch = parser->mixer_ch;
+
+ switch (msg_type) {
+ case MIXER_SRC_GAIN:
+ if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAN:
+ if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_FLAG:
+ if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_BALANCE:
+ if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_WIDTH:
+ if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+
+ parser->mixer_src_ch = mixer_src_ch;
+ }
+ break;
+ }
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ switch (msg_type) {
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ if (param->mixer.output.paired_volume[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_volume[mixer_ch] = val;
+ }
+ break;
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ if (param->mixer.output.paired_flag[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_flag[mixer_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case MAIN_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.main_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.main_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.hp_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_ASSIGNMENT:
+ if (parser->param.output.hp_paired_assignment != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_assignment = val;
+ }
+ break;
+ case LINE_INPUT_BOOST:
+ if (parser->param.line_input.boost_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.boost_flag = val;
+ }
+ break;
+ case LINE_INPUT_NOMINAL_LEVEL:
+ if (parser->param.line_input.nominal_level_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.nominal_level_flag = val;
+ }
+ break;
+ case INPUT_GAIN_AND_INVERT:
+ case INPUT_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 input_ch = parser->input_ch;
+
+ if (parser->prev_msg_type != msg_type)
+ input_ch = 0;
+ else
+ ++input_ch;
+
+ if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {
+ switch (msg_type) {
+ case INPUT_GAIN_AND_INVERT:
+ if (param->input.gain_and_invert[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.gain_and_invert[input_ch] = val;
+ }
+ break;
+ case INPUT_FLAG:
+ if (param->input.flag[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.flag[input_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ parser->input_ch = input_ch;
+ }
+ break;
+ }
+ case UNKNOWN_0:
+ case UNKNOWN_2:
+ break;
+ case METER:
+ {
+ u8 pos;
+
+ if (!meter_pos_quirk)
+ pos = b[MSG_METER_IDX_POS];
+ else
+ pos = b[MSG_METER_IDX_POS_4PRE_AE];
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {
+ parser->meter.data[pos] = val;
+ } else if (pos >= 0x80) {
+ pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)
+ parser->meter.data[pos] = val;
+ }
+
+ // The message for meter is interruptible to the series of other
+ // types of messages. Don't cache it.
+ fallthrough;
+ }
+ case INVALID:
+ default:
+ // Don't cache it.
+ continue;
+ }
+
+ parser->prev_msg_type = msg_type;
+ }
+ }
+
+ if (pos != parser->push_pos)
+ wake_up(&motu->hwdep_wait);
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *param)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(param, &parser->param, sizeof(*param));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ if (parser->pull_pos > parser->push_pos)
+ return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
+ else
+ return parser->push_pos - parser->pull_pos;
+}
+
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->pull_pos;
+ unsigned long flags;
+
+ if (pos == parser->push_pos)
+ return false;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ *event = parser->event_queue[pos];
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->pull_pos = pos;
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+
+ return true;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 9e6ca39ebd7f..64aec9c3eefd 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -255,6 +255,16 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
if (err < 0)
return err;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_init(motu);
+ if (err < 0)
+ return err;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);
+ if (err < 0)
+ return err;
+ }
+
err = begin_session(motu);
if (err < 0) {
dev_err(&motu->unit->device,
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index f65426238d4c..5fc7ae475537 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -112,6 +112,16 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent
if (err < 0)
goto error;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ }
+
err = snd_card_register(card);
if (err < 0)
goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index f1a830b358d4..79704ae6a73e 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -74,10 +74,13 @@ struct snd_motu {
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
+ struct snd_hwdep *hwdep;
struct amdtp_domain domain;
struct amdtp_motu_cache cache;
+
+ void *message_parser;
};
enum snd_motu_spec_flags {
@@ -85,6 +88,8 @@ enum snd_motu_spec_flags {
SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
+ SND_MOTU_SPEC_REGISTER_DSP = 0x0010,
+ SND_MOTU_SPEC_COMMAND_DSP = 0x0020,
};
#define SND_MOTU_CLOCK_RATE_COUNT 6
@@ -270,4 +275,22 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
return -ENXIO;
}
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
+void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+ unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter);
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *params);
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu);
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event);
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
+void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+ unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter);
+
#endif
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index fff18b5d4e05..f4a702def397 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
-#define READY_TIMEOUT_MS 200
+#define READY_TIMEOUT_MS 600
/*
* According to datasheet of Oxford Semiconductor:
@@ -367,6 +367,11 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
// Just after changing sampling transfer frequency, many cycles are
// skipped for packet transmission.
tx_init_skip_cycles = 400;
+ } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) {
+ // It takes a bit time for target device to adjust event frequency
+ // according to nominal event frequency in isochronous packets from
+ // ALSA oxfw driver.
+ tx_init_skip_cycles = 4000;
} else {
replay_seq = true;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index daf731364695..b496f87841ae 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -25,6 +25,7 @@
#define MODEL_SATELLITE 0x00200f
#define MODEL_SCS1M 0x001000
#define MODEL_DUET_FW 0x01dddd
+#define MODEL_ONYX_1640I 0x001640
#define SPECIFIER_1394TA 0x00a02d
#define VERSION_AVC 0x010001
@@ -192,6 +193,13 @@ static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id
// OXFW971-based models may transfer events by blocking method.
if (!(oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD))
oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+ if (model == MODEL_ONYX_1640I) {
+ //Unless receiving packets without NOINFO packet, the device transfers
+ //mostly half of events in packets than expected.
+ oxfw->quirks |= SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET |
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY;
+ }
}
return 0;
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index c13034f6c2ca..d728e451a25c 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -47,6 +47,11 @@ enum snd_oxfw_quirk {
// the device to process audio data even if the value is invalid in a point of
// IEC 61883-1/6.
SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET = 0x10,
+ // Loud Technologies Mackie Onyx 1640i seems to configure OXFW971 ASIC so that it decides
+ // event frequency according to events in received isochronous packets. The device looks to
+ // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
+ // are ignored, thus driver should transfer packets with timestamp.
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
};
/* This is an arbitrary number for convinience. */
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 0c005d67fa89..37154ed43bd5 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -106,20 +106,14 @@ void snd_hdac_stream_free_all(struct hdac_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
-/**
- * snd_hdac_ext_stream_decouple - decouple the hdac stream
- * @bus: HD-audio core bus
- * @stream: HD-audio ext core stream object to initialize
- * @decouple: flag to decouple
- */
-void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
- struct hdac_ext_stream *stream, bool decouple)
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+ struct hdac_ext_stream *stream,
+ bool decouple)
{
struct hdac_stream *hstream = &stream->hstream;
u32 val;
int mask = AZX_PPCTL_PROCEN(hstream->index);
- spin_lock_irq(&bus->reg_lock);
val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
if (decouple && !val)
@@ -128,6 +122,20 @@ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
stream->decoupled = decouple;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
+
+/**
+ * snd_hdac_ext_stream_decouple - decouple the hdac stream
+ * @bus: HD-audio core bus
+ * @stream: HD-audio ext core stream object to initialize
+ * @decouple: flag to decouple
+ */
+void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
+ struct hdac_ext_stream *stream, bool decouple)
+{
+ spin_lock_irq(&bus->reg_lock);
+ snd_hdac_ext_stream_decouple_locked(bus, stream, decouple);
spin_unlock_irq(&bus->reg_lock);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
@@ -252,6 +260,7 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
return NULL;
}
+ spin_lock_irq(&bus->reg_lock);
list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream = container_of(stream,
struct hdac_ext_stream,
@@ -266,17 +275,16 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
}
if (!hstream->link_locked) {
- snd_hdac_ext_stream_decouple(bus, hstream, true);
+ snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
res = hstream;
break;
}
}
if (res) {
- spin_lock_irq(&bus->reg_lock);
res->link_locked = 1;
res->link_substream = substream;
- spin_unlock_irq(&bus->reg_lock);
}
+ spin_unlock_irq(&bus->reg_lock);
return res;
}
@@ -292,6 +300,7 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
return NULL;
}
+ spin_lock_irq(&bus->reg_lock);
list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream = container_of(stream,
struct hdac_ext_stream,
@@ -301,18 +310,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
if (!stream->opened) {
if (!hstream->decoupled)
- snd_hdac_ext_stream_decouple(bus, hstream, true);
+ snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
res = hstream;
break;
}
}
if (res) {
- spin_lock_irq(&bus->reg_lock);
res->hstream.opened = 1;
res->hstream.running = 0;
res->hstream.substream = substream;
- spin_unlock_irq(&bus->reg_lock);
}
+ spin_unlock_irq(&bus->reg_lock);
return res;
}
@@ -378,15 +386,17 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
break;
case HDAC_EXT_STREAM_TYPE_HOST:
+ spin_lock_irq(&bus->reg_lock);
if (stream->decoupled && !stream->link_locked)
- snd_hdac_ext_stream_decouple(bus, stream, false);
+ snd_hdac_ext_stream_decouple_locked(bus, stream, false);
+ spin_unlock_irq(&bus->reg_lock);
snd_hdac_stream_release(&stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_LINK:
- if (stream->decoupled && !stream->hstream.opened)
- snd_hdac_ext_stream_decouple(bus, stream, false);
spin_lock_irq(&bus->reg_lock);
+ if (stream->decoupled && !stream->hstream.opened)
+ snd_hdac_ext_stream_decouple_locked(bus, stream, false);
stream->link_locked = 0;
stream->link_substream = NULL;
spin_unlock_irq(&bus->reg_lock);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 1eb8563db2df..9867555883c3 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -296,6 +296,7 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
int key = (substream->pcm->device << 16) | (substream->number << 2) |
(substream->stream + 1);
+ spin_lock_irq(&bus->reg_lock);
list_for_each_entry(azx_dev, &bus->stream_list, list) {
if (azx_dev->direction != substream->stream)
continue;
@@ -309,13 +310,12 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
res = azx_dev;
}
if (res) {
- spin_lock_irq(&bus->reg_lock);
res->opened = 1;
res->running = 0;
res->assigned_key = key;
res->substream = substream;
- spin_unlock_irq(&bus->reg_lock);
}
+ spin_unlock_irq(&bus->reg_lock);
return res;
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index c9d0ba353463..b9ac9e9e45a4 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -31,6 +31,7 @@ struct config_entry {
u16 device;
u8 acpi_hid[ACPI_ID_LEN];
const struct dmi_system_id *dmi_table;
+ u8 codec_hid[ACPI_ID_LEN];
};
/*
@@ -56,7 +57,7 @@ static const struct config_entry config_table[] = {
/*
* Apollolake (Broxton-P)
* the legacy HDAudio driver is used except on Up Squared (SOF) and
- * Chromebooks (SST)
+ * Chromebooks (SST), as well as devices based on the ES8336 codec
*/
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
{
@@ -73,6 +74,11 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SOF,
+ .device = 0x5a98,
+ .codec_hid = "ESSX8336",
+ },
#endif
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
{
@@ -137,7 +143,7 @@ static const struct config_entry config_table[] = {
/*
* Geminilake uses legacy HDAudio driver except for Google
- * Chromebooks
+ * Chromebooks and devices based on the ES8336 codec
*/
/* Geminilake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
@@ -154,6 +160,11 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SOF,
+ .device = 0x3198,
+ .codec_hid = "ESSX8336",
+ },
#endif
/*
@@ -311,6 +322,11 @@ static const struct config_entry config_table[] = {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x43c8,
},
+ {
+ .flags = FLAG_SOF,
+ .device = 0xa0c8,
+ .codec_hid = "ESSX8336",
+ },
#endif
/* Elkhart Lake */
@@ -354,6 +370,8 @@ static const struct config_entry *snd_intel_dsp_find_config
continue;
if (table->dmi_table && !dmi_check_system(table->dmi_table))
continue;
+ if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
+ continue;
return table;
}
return NULL;
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 6ffa48dd5983..570b88e0b201 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -22,7 +22,7 @@ config SND_SB16_DSP
menuconfig SND_ISA
bool "ISA sound devices"
depends on ISA || COMPILE_TEST
- depends on ISA_DMA_API
+ depends on ISA_DMA_API && !M68K
default y
help
Support for sound devices connected via the ISA bus.
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index a1c770d826dd..6d664dd8dde0 100644
--- a/sound/isa/gus/gus_dma.c
+++ b/sound/isa/gus/gus_dma.c
@@ -126,6 +126,8 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
}
block = snd_gf1_dma_next_block(gus);
spin_unlock(&gus->dma_lock);
+ if (!block)
+ return;
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
kfree(block);
#if 0
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 93bc9bef7641..41ce12597177 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -279,6 +279,7 @@ config SND_CS46XX_NEW_DSP
config SND_CS5530
tristate "CS5530 Audio"
depends on ISA_DMA_API && (X86_32 || COMPILE_TEST)
+ depends on !M68K
select SND_SB16_DSP
help
Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4d22e7adeee8..7762718cf429 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -638,13 +638,17 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
* the update-IRQ timing. The IRQ is issued before actually the
* data is processed. So, we need to process it afterwords in a
* workqueue.
+ *
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
*/
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
int stream = substream->stream;
u32 wallclk;
unsigned int pos;
+ snd_pcm_uframes_t hwptr, target;
wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
@@ -681,6 +685,24 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
/* NG - it's below the first next period boundary */
return chip->bdl_pos_adj ? 0 : -1;
azx_dev->core.start_wallclk += wallclk;
+
+ if (azx_dev->core.no_period_wakeup)
+ return 1; /* OK, no need to check period boundary */
+
+ if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt)
+ return 1; /* OK, already in hwptr updating process */
+
+ /* check whether the period gets really elapsed */
+ pos = bytes_to_frames(runtime, pos);
+ hwptr = runtime->hw_ptr_base + pos;
+ if (hwptr < runtime->status->hw_ptr)
+ hwptr += runtime->buffer_size;
+ target = runtime->hw_ptr_interrupt + runtime->period_size;
+ if (hwptr < target) {
+ /* too early wakeup, process it later */
+ return chip->bdl_pos_adj ? 0 : -1;
+ }
+
return 1; /* OK, it's fine */
}
@@ -859,31 +881,6 @@ static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
return substream->runtime->delay;
}
-static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- return _snd_hdac_chip_readl(azx_bus(chip),
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- azx_dev->core.index));
-}
-
-/* get the current DMA position with correction on SKL+ chips */
-static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
-{
- /* DPIB register gives a more accurate position for playback */
- if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return azx_skl_get_dpib_pos(chip, azx_dev);
-
- /* For capture, we need to read posbuf, but it requires a delay
- * for the possible boundary overlap; the read of DPIB fetches the
- * actual posbuf
- */
- udelay(20);
- azx_skl_get_dpib_pos(chip, azx_dev);
- return azx_get_pos_posbuf(chip, azx_dev);
-}
-
static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
{
azx_stop_chip(chip);
@@ -1573,7 +1570,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_POSBUF] = azx_get_pos_posbuf,
[POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib,
- [POS_FIX_SKL] = azx_get_pos_skl,
+ [POS_FIX_SKL] = azx_get_pos_posbuf,
[POS_FIX_FIFO] = azx_get_pos_fifo,
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 965b096f416f..8a3e2fe42106 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2539,6 +2539,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED),
@@ -4355,6 +4356,16 @@ static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
}
+static void alc245_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
/* turn on/off mic-mute LED per capture hook via VREF change */
static int vref_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
@@ -6709,6 +6720,7 @@ enum {
ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
ALC287_FIXUP_HP_GPIO_LED,
ALC256_FIXUP_HP_HEADSET_MIC,
+ ALC245_FIXUP_HP_GPIO_LED,
ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
ALC282_FIXUP_ACER_DISABLE_LINEOUT,
ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
@@ -7333,6 +7345,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC245_FIXUP_HP_X360_AMP] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_fixup_hp_x360_amp,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
},
[ALC288_FIXUP_DELL_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
@@ -8432,6 +8446,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc256_fixup_tongfang_reset_persistent_settings,
},
+ [ALC245_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_gpio_led,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8617,6 +8635,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 49ff5e73e9ba..2c6af3f8f296 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -6,6 +6,7 @@ config SND_SOC_AMD_ACP
config SND_SOC_AMD_CZ_DA7219MX98357_MACH
tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
+ select CLK_FIXED_FCH
select SND_SOC_DA7219
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
@@ -30,13 +31,14 @@ config SND_SOC_AMD_ACP3x
config SND_SOC_AMD_RV_RT5682_MACH
tristate "AMD RV support for RT5682"
+ select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
select SND_SOC_CROS_EC_CODEC
select I2C_CROS_EC_TUNNEL
select SND_SOC_RT1015
select SND_SOC_RT1015P
- depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC
+ depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC && GPIOLIB
help
This option enables machine driver for RT5682 and MAX9835.
@@ -49,7 +51,7 @@ config SND_SOC_AMD_RENOIR
config SND_SOC_AMD_RENOIR_MACH
tristate "AMD Renoir support for DMIC"
select SND_SOC_DMIC
- depends on SND_SOC_AMD_RENOIR
+ depends on SND_SOC_AMD_RENOIR && GPIOLIB
help
This option enables machine driver for DMIC
@@ -61,3 +63,37 @@ config SND_SOC_AMD_ACP5x
By enabling this flag build will trigger for ACP PCI driver,
ACP DMA driver, CPU DAI driver.
+
+config SND_SOC_AMD_VANGOGH_MACH
+ tristate "AMD Vangogh support for NAU8821 CS35L41"
+ select SND_SOC_NAU8821
+ select SND_SOC_CS35L41_SPI
+ depends on SND_SOC_AMD_ACP5x && I2C
+ help
+ This option enables machine driver for Vangogh platform
+ using NAU8821 and CS35L41 codecs.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP6x
+ tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e ACP v6.x support on
+ AMD Yellow Carp platform. By enabling this flag build will be
+ triggered for ACP PCI driver, ACP PDM DMA driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_YC_MACH
+ tristate "AMD YC support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_ACP6x
+ help
+ This option enables machine driver for Yellow Carp platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+source "sound/soc/amd/acp/Kconfig"
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index 07150d26f315..f1d42bbda709 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
+obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index b3df98a9f9f3..b2065f3fe42c 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -33,7 +33,7 @@ static struct clk *da7219_dai_wclk;
static struct clk *da7219_dai_bclk;
static struct clk *rt5682_dai_wclk;
static struct clk *rt5682_dai_bclk;
-extern bool bt_uart_enable;
+
void *acp_soc_is_rltk_max(struct device *dev);
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
@@ -760,8 +760,8 @@ static int cz_probe(struct platform_device *pdev)
"devm_snd_soc_register_card(%s) failed\n",
card->name);
}
- bt_uart_enable = !device_property_read_bool(&pdev->dev,
- "bt-pad-enable");
+ acp_bt_uart_enable = !device_property_read_bool(&pdev->dev,
+ "bt-pad-enable");
return 0;
}
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 11b3c4f39eba..1f322accd9ea 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -36,8 +36,8 @@
#define ST_MIN_BUFFER ST_MAX_BUFFER
#define DRV_NAME "acp_audio_dma"
-bool bt_uart_enable = true;
-EXPORT_SYMBOL(bt_uart_enable);
+bool acp_bt_uart_enable = true;
+EXPORT_SYMBOL(acp_bt_uart_enable);
static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -596,7 +596,7 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
/* For BT instance change pins from UART to BT */
- if (!bt_uart_enable) {
+ if (!acp_bt_uart_enable) {
val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL);
val |= ACP_BT_UART_PAD_SELECT_MASK;
acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL);
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index d6ba94677ac2..6d5c547a32de 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -91,7 +91,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static struct snd_soc_ops cz_aif1_ops = {
+static const struct snd_soc_ops cz_aif1_ops = {
.hw_params = cz_aif1_hw_params,
};
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index e5ab6c6040a6..85529ed7e5f5 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -204,4 +204,6 @@ typedef struct acp_dma_dscr_transfer {
u32 reserved;
} acp_dma_dscr_transfer_t;
+extern bool acp_bt_uart_enable;
+
#endif /*__ACP_HW_H */
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
new file mode 100644
index 000000000000..52a1371f9e61
--- /dev/null
+++ b/sound/soc/amd/acp/Kconfig
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+#
+
+config SND_SOC_AMD_ACP_COMMON
+ tristate "AMD Audio ACP Common support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables common modules for Audio-Coprocessor i.e. ACP
+ IP block on AMD platforms.
+
+if SND_SOC_AMD_ACP_COMMON
+
+config SND_SOC_AMD_ACP_I2S
+ tristate
+
+config SND_SOC_AMD_ACP_PCM
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+config SND_AMD_ASOC_RENOIR
+ tristate "AMD ACP ASOC Renoir Support"
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ depends on X86 && PCI
+ help
+ This option enables Renoir I2S support on AMD platform.
+
+config SND_SOC_AMD_MACH_COMMON
+ tristate
+ depends on X86 && PCI && I2C
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_RT1019
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5682S
+ help
+ This option enables common Machine driver module for ACP.
+
+config SND_SOC_AMD_LEGACY_MACH
+ tristate "AMD Legacy Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ depends on X86 && PCI && I2C
+ help
+ This option enables legacy sound card support for ACP audio.
+
+config SND_SOC_AMD_SOF_MACH
+ tristate "AMD SOF Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ depends on X86 && PCI && I2C
+ help
+ This option enables SOF sound card support for ACP audio.
+
+endif # SND_SOC_AMD_ACP_COMMON
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
new file mode 100644
index 000000000000..16c144c2965c
--- /dev/null
+++ b/sound/soc/amd/acp/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#common acp driver
+snd-acp-pcm-objs := acp-platform.o
+snd-acp-i2s-objs := acp-i2s.o
+
+#platform specific driver
+snd-acp-renoir-objs := acp-renoir.o
+
+#machine specific driver
+snd-acp-mach-objs := acp-mach-common.o
+snd-acp-legacy-mach-objs := acp-legacy-mach.o
+snd-acp-sof-mach-objs := acp-sof-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
+
+obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+
+obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
new file mode 100644
index 000000000000..ce9aca8dd6f5
--- /dev/null
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio I2S controller
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_playcap"
+
+static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata;
+ u32 val;
+ u32 xfer_resolution;
+ u32 reg_val;
+
+ adata = snd_soc_dai_get_drvdata(dai);
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+
+ val = readl(adata->acp_base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (xfer_resolution << 3);
+ writel(val, adata->acp_base + reg_val);
+
+ return 0;
+}
+
+static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
+
+ period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_BTTDM_ITER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_BTTDM_IRER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+ writel(period_bytes, adata->acp_base + water_val);
+ writel(buf_size, adata->acp_base + buf_reg);
+ val = readl(adata->acp_base + reg_val);
+ val = val | BIT(0);
+ writel(val, adata->acp_base + reg_val);
+ writel(1, adata->acp_base + ier_val);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+ val = readl(adata->acp_base + reg_val);
+ val = val & ~BIT(0);
+ writel(val, adata->acp_base + reg_val);
+
+ if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_BTTDM_IER);
+ if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_I2STDM_IER);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
+ u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR;
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE;
+
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR;
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE;
+
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, adata->acp_base + reg_dma_size);
+ writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
+ writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
+
+ ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD);
+
+ writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+
+ return 0;
+}
+
+static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ unsigned int dir = substream->stream;
+ unsigned int irq_bit = 0;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(I2S_TX_THRESHOLD);
+ stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
+ stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(I2S_RX_THRESHOLD);
+ stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
+ stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(BT_TX_THRESHOLD);
+ stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
+ stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(BT_RX_THRESHOLD);
+ stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
+ stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ /* Save runtime dai configuration in stream */
+ stream->id = dai->driver->id + dir;
+ stream->dai_id = dai->driver->id;
+ stream->irq_bit = irq_bit;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
+ .startup = acp_i2s_startup,
+ .hw_params = acp_i2s_hwparams,
+ .prepare = acp_i2s_prepare,
+ .trigger = acp_i2s_trigger,
+};
+EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
+
+int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ unsigned int val;
+
+ if (!adata->acp_base) {
+ dev_err(dev, "I2S base is NULL\n");
+ return -EINVAL;
+ }
+
+ val = readl(adata->acp_base + ACP_I2S_PIN_CONFIG);
+ if (val != I2S_MODE) {
+ dev_err(dev, "I2S Mode not supported val %x\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
new file mode 100644
index 000000000000..de0f8024e2fb
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Machine Driver Legacy Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = NONE,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = NONE,
+};
+
+static const struct snd_kcontrol_new acp_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+
+};
+
+static const struct snd_soc_dapm_widget acp_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static int acp_asoc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->dapm_widgets = acp_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
+ card->controls = acp_controls;
+ card->num_controls = ARRAY_SIZE(acp_controls);
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+
+ acp_legacy_dai_links_create(card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "rn_rt5682_rt1019",
+ .driver_data = (kernel_ulong_t)&rt5682_rt1019_data,
+ },
+ { }
+};
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .name = "acp_mach",
+ },
+ .probe = acp_asoc_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
+MODULE_DESCRIPTION("ACP chrome audio support");
+MODULE_ALIAS("platform:rn_rt5682_rt1019");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
new file mode 100644
index 000000000000..7785f12aa006
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Machine Driver Interface for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "../../codecs/rt5682.h"
+#include "../../codecs/rt1019.h"
+#include "../../codecs/rt5682s.h"
+#include "acp-mach.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+#define FOUR_CHANNEL 4
+
+static struct snd_soc_jack pco_jack;
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp_clk_enable(struct acp_card_drvdata *drvdata)
+{
+ clk_set_rate(drvdata->wclk, 48000);
+ clk_set_rate(drvdata->bclk, 48000 * 64);
+
+ return clk_prepare_enable(drvdata->wclk);
+}
+
+/* Declare RT5682 codec components */
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+
+static const struct snd_soc_dapm_route rt5682_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+/* Define card ops for RT5682 CODEC */
+static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682)
+ return -EINVAL;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682_map, ARRAY_SIZE(rt5682_map));
+}
+
+static int acp_card_hs_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 acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ ret = acp_clk_enable(drvdata);
+ if (ret < 0)
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+
+ return ret;
+}
+
+static void acp_card_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ clk_disable_unprepare(drvdata->wclk);
+}
+
+static const struct snd_soc_ops acp_card_rt5682_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+};
+
+/* Define RT5682S CODEC component*/
+SND_SOC_DAILINK_DEF(rt5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
+
+static const struct snd_soc_dapm_route rt5682s_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682S)
+ return -EINVAL;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map));
+}
+
+static const struct snd_soc_ops acp_card_rt5682s_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+};
+
+/* Declare RT1019 codec components */
+SND_SOC_DAILINK_DEF(rt1019,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"),
+ COMP_CODEC("i2c-10EC1019:02", "rt1019-aif")));
+
+static const struct snd_soc_dapm_route rt1019_map_lr[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static struct snd_soc_codec_conf rt1019_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"),
+ .name_prefix = "Right",
+ },
+};
+
+static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt1019_map_lr,
+ ARRAY_SIZE(rt1019_map_lr));
+}
+
+static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai;
+ int srate, i, ret = 0;
+
+ srate = params_rate(params);
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1019-aif"))
+ continue;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1019_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int acp_card_amp_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 acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ ret = acp_clk_enable(drvdata);
+ if (ret < 0)
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_card_rt1019_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt1019_hw_params,
+};
+
+/* Declare Maxim codec components */
+SND_SOC_DAILINK_DEF(max98360a,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi")));
+
+static const struct snd_soc_dapm_route max98360a_map[] = {
+ {"Spk", NULL, "Speaker"},
+};
+
+static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (drvdata->amp_codec_id != MAX98360A)
+ return -EINVAL;
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98360a_map,
+ ARRAY_SIZE(max98360a_map));
+}
+
+static const struct snd_soc_ops acp_card_maxim_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+};
+
+/* Declare DMIC codec components */
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+/* Declare ACP CPU components */
+static struct snd_soc_dai_link_component dummy_codec[] = {
+ {
+ .name = "snd-soc-dummy",
+ .dai_name = "snd-soc-dummy-dai",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ .name = "acp_asoc_renoir.0",
+ }
+};
+
+static struct snd_soc_dai_link_component sof_component[] = {
+ {
+ .name = "0000:04:00.5",
+ }
+};
+
+SND_SOC_DAILINK_DEF(i2s_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
+SND_SOC_DAILINK_DEF(sof_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
+SND_SOC_DAILINK_DEF(sof_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * num_links, GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].id = DMIC_BE_ID;
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ links[i].cpus = sof_dmic;
+ links[i].num_cpus = ARRAY_SIZE(sof_dmic);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_sofdsp_dai_links_create, SND_SOC_AMD_MACH);
+
+int acp_legacy_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * num_links, GFP_KERNEL);
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].dpcm_playback = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, SND_SOC_AMD_MACH);
+
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
new file mode 100644
index 000000000000..5dc47cfbff10
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __ACP_MACH_H
+#define __ACP_MACH_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
+ DMIC_BE_ID,
+};
+
+enum cpu_endpoints {
+ NONE = 0,
+ I2S_SP,
+ I2S_BT,
+ DMIC,
+};
+
+enum codec_endpoints {
+ DUMMY = 0,
+ RT5682,
+ RT1019,
+ MAX98360A,
+ RT5682S,
+};
+
+struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ struct clk *wclk;
+ struct clk *bclk;
+};
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
+int acp_legacy_dai_links_create(struct snd_soc_card *card);
+
+#endif
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
new file mode 100644
index 000000000000..65a809e2c29f
--- /dev/null
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic interface for ACP audio blck PCM component
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_dma"
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+int acp_machine_select(struct acp_dev_data *adata)
+{
+ struct snd_soc_acpi_mach *mach;
+ int size;
+
+ size = sizeof(*adata->machines);
+ mach = snd_soc_acpi_find_machine(adata->machines);
+ if (!mach) {
+ dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
+ return -EINVAL;
+ }
+
+ adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ if (IS_ERR(adata->mach_dev))
+ dev_warn(adata->dev, "Unable to register Machine device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
+
+static irqreturn_t i2s_irq_handler(int irq, void *data)
+{
+ struct acp_dev_data *adata = data;
+ struct acp_stream *stream;
+ u16 i2s_flag = 0;
+ u32 val, i;
+
+ if (!adata)
+ return IRQ_NONE;
+
+ val = readl(adata->acp_base + ACP_EXTERNAL_INTR_STAT);
+
+ for (i = 0; i < ACP_MAX_STREAM; i++) {
+ stream = adata->stream[i];
+ if (stream && (val & stream->irq_bit)) {
+ writel(stream->irq_bit, adata->acp_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ break;
+ }
+ }
+
+ if (i2s_flag)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
+{
+ u32 pte_reg, pte_size, reg_val;
+
+ /* Use ATU base Group5 */
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
+ stream->reg_offset = 0x02000000;
+
+ /* Group Enable */
+ reg_val = ACP_SRAM_PTE_OFFSET;
+ writel(reg_val | BIT(31), adata->acp_base + pte_reg);
+ writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
+}
+
+static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
+{
+ struct acp_stream *stream = adata->stream[cpu_id];
+ struct snd_pcm_substream *substream = stream->substream;
+ dma_addr_t addr = substream->dma_buffer.addr;
+ int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ u32 low, high, val;
+ u16 page_idx;
+
+ val = stream->pte_offset;
+
+ for (page_idx = 0; page_idx < num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+ writel(low, adata->acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, adata->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream;
+ int stream_id = cpu_dai->driver->id * 2 + substream->stream;
+ int ret;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->substream = substream;
+ adata->stream[stream_id] = stream;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp_pcm_hardware_playback;
+ else
+ runtime->hw = acp_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+ runtime->private_data = stream;
+
+ writel(1, adata->acp_base + ACP_EXTERNAL_INTR_ENB);
+
+ return ret;
+}
+
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
+ struct acp_stream *stream = substream->runtime->private_data;
+ int stream_id = cpu_dai->driver->id * 2 + substream->stream;
+ u64 size = params_buffer_bytes(params);
+
+ /* Configure ACP DMA block with params */
+ config_pte_for_stream(adata, stream);
+ config_acp_dma(adata, stream_id, size);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
+
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+
+ pos = do_div(bytescount, buffersize);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_dma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream;
+ int stream_id = cpu_dai->driver->id * 2 + substream->stream;
+
+ stream = adata->stream[stream_id];
+ kfree(stream);
+ adata->stream[stream_id] = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp_pcm_component = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .pointer = acp_dma_pointer,
+ .mmap = acp_dma_mmap,
+ .pcm_construct = acp_dma_new,
+};
+
+int acp_platform_register(struct device *dev)
+{
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct snd_soc_dai_driver;
+ unsigned int status;
+
+ status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler,
+ IRQF_SHARED, "ACP_I2S_IRQ", adata);
+ if (status) {
+ dev_err(dev, "ACP I2S IRQ request failed\n");
+ return status;
+ }
+
+ status = devm_snd_soc_register_component(dev, &acp_pcm_component,
+ adata->dai_driver,
+ adata->num_dai);
+ if (status) {
+ dev_err(dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
+
+int acp_platform_unregister(struct device *dev)
+{
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata->mach_dev)
+ platform_device_unregister(adata->mach_dev);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON);
+
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
new file mode 100644
index 000000000000..9b321a055b52
--- /dev/null
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_asoc_renoir"
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "rn_rt5682_rt1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-acp",
+ },
+ {},
+};
+
+static struct snd_soc_dai_driver acp_renoir_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+};
+
+static int renoir_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_dev_data *adata;
+ struct resource *res;
+
+ adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->i2s_irq = res->start;
+ adata->dev = dev;
+ adata->dai_driver = acp_renoir_dai;
+ adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
+
+ adata->machines = snd_soc_acpi_amd_acp_machines;
+ acp_machine_select(adata);
+
+ dev_set_drvdata(dev, adata);
+ acp_platform_register(dev);
+
+ return 0;
+}
+
+static int renoir_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ acp_platform_unregister(dev);
+ return 0;
+}
+
+static struct platform_driver renoir_driver = {
+ .probe = renoir_audio_probe,
+ .remove = renoir_audio_remove,
+ .driver = {
+ .name = "acp_asoc_renoir",
+ },
+};
+
+module_platform_driver(renoir_driver);
+
+MODULE_DESCRIPTION("AMD ACP Renoir Driver");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
new file mode 100644
index 000000000000..854eb7214cea
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * SOF Machine Driver Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata sof_rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static const struct snd_kcontrol_new acp_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget acp_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static int acp_sof_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->dapm_widgets = acp_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
+ card->controls = acp_controls;
+ card->num_controls = ARRAY_SIZE(acp_controls);
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+
+ acp_sofdsp_dai_links_create(card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "rt5682-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data
+ },
+ {
+ .name = "rt5682-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data
+ },
+ {
+ .name = "rt5682s-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
+ },
+ { }
+};
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .name = "sof_mach",
+ },
+ .probe = acp_sof_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
+MODULE_DESCRIPTION("ACP chrome SOF audio support");
+MODULE_ALIAS("platform:rt5682-rt1019");
+MODULE_ALIAS("platform:rt5682-max");
+MODULE_ALIAS("platform:rt5682s-max");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
new file mode 100644
index 000000000000..8eee3d34774b
--- /dev/null
+++ b/sound/soc/amd/acp/amd.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef __AMD_ACP_H
+#define __AMD_ACP_H
+
+#include <sound/pcm.h>
+#include <sound/soc-acpi.h>
+#include "chip_offset_byte.h"
+
+#define I2S_SP_INSTANCE 0x00
+#define I2S_BT_INSTANCE 0x01
+
+#define MEM_WINDOW_START 0x4000000
+
+#define ACP_I2S_REG_START 0x1242400
+#define ACP_I2S_REG_END 0x1242810
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+#define I2S_MODE 0x04
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define BT_TX_THRESHOLD 26
+#define BT_RX_THRESHOLD 25
+
+#define ACP_SRAM_PTE_OFFSET 0x02052800
+
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
+#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define BT_PB_FIFO_ADDR_OFFSET 0x900
+#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER 65536
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+#define ACP_MAX_STREAM 6
+
+struct acp_stream {
+ struct snd_pcm_substream *substream;
+ int irq_bit;
+ int dai_id;
+ int id;
+ u64 bytescount;
+ u32 reg_offset;
+ u32 pte_offset;
+ u32 fifo_offset;
+};
+
+struct acp_dev_data {
+ char *name;
+ struct device *dev;
+ void __iomem *acp_base;
+ unsigned int i2s_irq;
+
+ /* SOC specific dais */
+ struct snd_soc_dai_driver *dai_driver;
+ int num_dai;
+
+ struct acp_stream *stream[ACP_MAX_STREAM];
+
+ struct snd_soc_acpi_mach *machines;
+ struct platform_device *mach_dev;
+};
+
+extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+
+int asoc_acp_i2s_probe(struct snd_soc_dai *dai);
+int acp_platform_register(struct device *dev);
+int acp_platform_unregister(struct device *dev);
+
+int acp_machine_select(struct acp_dev_data *adata);
+
+static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
+{
+ u64 byte_count, low = 0, high = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
+ return -EINVAL;
+ }
+ }
+ /* Get 64 bit value from two 32 bit registers */
+ byte_count = (high << 32) | low;
+
+ return byte_count;
+}
+
+#endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
new file mode 100644
index 000000000000..c7f77e975dc7
--- /dev/null
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef _ACP_IP_OFFSET_HEADER
+#define _ACP_IP_OFFSET_HEADER
+
+#define ACPAXI2AXI_ATU_CTRL 0xC40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+#define ACP_EXTERNAL_INTR_ENB 0x1800
+#define ACP_EXTERNAL_INTR_CNTL 0x1804
+#define ACP_EXTERNAL_INTR_STAT 0x1808
+#define ACP_I2S_PIN_CONFIG 0x1400
+#define ACP_SCRATCH_REG_0 0x12800
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+
+#define ACP_I2S_RX_RINGBUFADDR 0x2000
+#define ACP_I2S_RX_RINGBUFSIZE 0x2004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x2008
+#define ACP_I2S_RX_FIFOADDR 0x200C
+#define ACP_I2S_RX_FIFOSIZE 0x2010
+#define ACP_I2S_RX_DMA_SIZE 0x2014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x2018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x2020
+#define ACP_I2S_TX_RINGBUFADDR 0x2024
+#define ACP_I2S_TX_RINGBUFSIZE 0x2028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x202C
+#define ACP_I2S_TX_FIFOADDR 0x2030
+#define ACP_I2S_TX_FIFOSIZE 0x2034
+#define ACP_I2S_TX_DMA_SIZE 0x2038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x2040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x2044
+#define ACP_BT_RX_RINGBUFADDR 0x2048
+#define ACP_BT_RX_RINGBUFSIZE 0x204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x2050
+#define ACP_BT_RX_FIFOADDR 0x2054
+#define ACP_BT_RX_FIFOSIZE 0x2058
+#define ACP_BT_RX_DMA_SIZE 0x205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x2060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x2064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x2068
+#define ACP_BT_TX_RINGBUFADDR 0x206C
+#define ACP_BT_TX_RINGBUFSIZE 0x2070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x2074
+#define ACP_BT_TX_FIFOADDR 0x2078
+#define ACP_BT_TX_FIFOSIZE 0x207C
+#define ACP_BT_TX_DMA_SIZE 0x2080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x2084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x2088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x208C
+
+#define ACP_I2STDM_IER 0x2400
+#define ACP_I2STDM_IRER 0x2404
+#define ACP_I2STDM_RXFRMT 0x2408
+#define ACP_I2STDM_ITER 0x240C
+#define ACP_I2STDM_TXFRMT 0x2410
+
+/* Registers from ACP_BT_TDM block */
+
+#define ACP_BTTDM_IER 0x2800
+#define ACP_BTTDM_IRER 0x2804
+#define ACP_BTTDM_RXFRMT 0x2808
+#define ACP_BTTDM_ITER 0x280C
+#define ACP_BTTDM_TXFRMT 0x2810
+
+#endif
diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile
index 3353f93dc610..c9e53e04e247 100644
--- a/sound/soc/amd/vangogh/Makefile
+++ b/sound/soc/amd/vangogh/Makefile
@@ -3,7 +3,9 @@
snd-pci-acp5x-objs := pci-acp5x.o
snd-acp5x-i2s-objs := acp5x-i2s.o
snd-acp5x-pcm-dma-objs := acp5x-pcm-dma.o
+snd-soc-acp5x-mach-objs := acp5x-mach.o
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
index 2705e57eb713..002db3971ca9 100644
--- a/sound/soc/amd/vangogh/acp5x-i2s.c
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -348,7 +348,7 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
+static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
.hw_params = acp5x_i2s_hwparams,
.trigger = acp5x_i2s_trigger,
.set_fmt = acp5x_i2s_set_fmt,
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
new file mode 100644
index 000000000000..14cf325e4b23
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41
+ * codecs.
+ *
+ * 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/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/dmi.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
+
+static unsigned long acp5x_machine_id;
+static struct snd_soc_jack vg_headset;
+
+static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ 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(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &vg_headset, acp5x_nau8821_jack_pins,
+ ARRAY_SIZE(acp5x_nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ 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;
+}
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const unsigned int channels[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp5x_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return 0;
+}
+
+static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ 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;
+
+ ret = snd_soc_dai_set_sysclk(codec_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);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return 0;
+}
+
+static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai;
+ int ret, i;
+ unsigned int num_codecs = rtd->num_codecs;
+ unsigned int bclk_val;
+
+ for (i = 0; i < num_codecs; i++) {
+ codec_dai = asoc_rtd_to_codec(rtd, i);
+ if ((strcmp(codec_dai->name, "spi-VLV1776:00") == 0) ||
+ (strcmp(codec_dai->name, "spi-VLV1776:01") == 0)) {
+ switch (params_rate(params)) {
+ case 48000:
+ bclk_val = 1536000;
+ break;
+ default:
+ dev_err(card->dev, "Invalid Samplerate:0x%x\n",
+ params_rate(params));
+ 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");
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+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[] = {
+ {
+ .dlc = COMP_CODEC_CONF("spi-VLV1776:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("spi-VLV1776:01"),
+ .name_prefix = "Right",
+ },
+};
+
+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("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[] = {
+ {
+ .name = "acp5x-8825-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-CS35L41-Stereo",
+ .stream_name = "CS35L41 Stereo Playback",
+ .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;
+ }
+ }
+ 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[] = {
+ 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_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static struct snd_soc_card acp5x_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),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+
+static int acp5x_vg_quirk_cb(const struct dmi_system_id *id)
+{
+ acp5x_machine_id = VG_JUPITER;
+ return 1;
+}
+
+static const struct dmi_system_id acp5x_vg_quirk_table[] = {
+ {
+ .callback = acp5x_vg_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ }
+ },
+ {}
+};
+
+static int acp5x_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct acp5x_platform_info *machine;
+ struct snd_soc_card *card;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp5x_platform_info),
+ 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;
+ break;
+ default:
+ return -ENODEV;
+ }
+ 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);
+ }
+ return 0;
+}
+
+static struct platform_driver acp5x_mach_driver = {
+ .driver = {
+ .name = "acp5x_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp5x_probe,
+};
+
+module_platform_driver(acp5x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h
index a808635f9740..fe5e1fa98974 100644
--- a/sound/soc/amd/vangogh/acp5x.h
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -23,7 +23,7 @@
#define ACP_ERR_INTR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
-#define ACP5x_DEVS 3
+#define ACP5x_DEVS 4
#define ACP5x_REG_START 0x1240000
#define ACP5x_REG_END 0x1250200
#define ACP5x_I2STDM_REG_START 0x1242400
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
index a57b762d9f2e..2b6b9edc36e2 100644
--- a/sound/soc/amd/vangogh/pci-acp5x.c
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -213,6 +213,9 @@ static int snd_acp5x_probe(struct pci_dev *pci,
pdevinfo[2].num_res = 1;
pdevinfo[2].res = &adata->res[2];
+ pdevinfo[3].name = "acp5x_mach";
+ pdevinfo[3].id = 0;
+ pdevinfo[3].parent = &pci->dev;
for (i = 0; i < ACP5x_DEVS; i++) {
adata->pdev[i] =
platform_device_register_full(&pdevinfo[i]);
diff --git a/sound/soc/amd/yc/Makefile b/sound/soc/amd/yc/Makefile
new file mode 100644
index 000000000000..dc2974440388
--- /dev/null
+++ b/sound/soc/amd/yc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Yellow Carp platform Support
+snd-pci-acp6x-objs := pci-acp6x.o
+snd-acp6x-pdm-dma-objs := acp6x-pdm-dma.o
+snd-soc-acp6x-mach-objs := acp6x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-pci-acp6x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-acp6x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_YC_MACH) += snd-soc-acp6x-mach.o
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
new file mode 100644
index 000000000000..9a767f47b89f
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Yellow Carp platform using DMIC
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_mach"
+
+SND_SOC_DAILINK_DEF(acp6x_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_yc_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_yc_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp6x_dai_pdm[] = {
+ {
+ .name = "acp6x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp6x_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp6x_card = {
+ .name = "acp6x",
+ .owner = THIS_MODULE,
+ .dai_link = acp6x_dai_pdm,
+ .num_links = 1,
+};
+
+static const struct dmi_system_id yc_acp_quirk_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21AW"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21AX"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21BN"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21BQ"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D8"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D9"),
+ }
+ },
+ {}
+};
+
+static int acp6x_probe(struct platform_device *pdev)
+{
+ struct acp6x_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ int ret;
+ const struct dmi_system_id *dmi_id;
+
+ dmi_id = dmi_first_match(yc_acp_quirk_table);
+ if (!dmi_id)
+ return -ENODEV;
+ card = &acp6x_card;
+ acp6x_card.dev = &pdev->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",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp6x_mach_driver = {
+ .driver = {
+ .name = "acp_yc_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp6x_probe,
+};
+
+module_platform_driver(acp6x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
new file mode 100644
index 000000000000..e604f4ea524f
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Yellow Carp PDM Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_pdm_dma"
+
+static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp6x_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+ acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp6x_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp6x_enable_pdm_clock(acp_base);
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp6x_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp6x_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp6x_base = adata->acp6x_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp6x_config_dma(rtd, substream->stream);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp6x_base);
+ return 0;
+}
+
+static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp6x_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp6x_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (!pdm_status)
+ ret = acp6x_start_pdm_dma(rtd->acp6x_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (pdm_status)
+ ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
+ .trigger = acp6x_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp6x_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp6x_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp6x_pdm_dma_open,
+ .close = acp6x_pdm_dma_close,
+ .hw_params = acp6x_pdm_dma_hw_params,
+ .pointer = acp6x_pdm_dma_pointer,
+ .pcm_construct = acp6x_pdm_dma_new,
+};
+
+static int acp6x_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ int status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp6x_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp6x_pdm_component,
+ &acp6x_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+ return 0;
+}
+
+static int 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)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp6x_base);
+ }
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int __maybe_unused acp6x_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp6x_pdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
+};
+
+static struct platform_driver acp6x_pdm_dma_driver = {
+ .probe = acp6x_pdm_audio_probe,
+ .remove = acp6x_pdm_audio_remove,
+ .driver = {
+ .name = "acp_yc_pdm_dma",
+ .pm = &acp6x_pdm_pm_ops,
+ },
+};
+
+module_platform_driver(acp6x_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h
new file mode 100644
index 000000000000..74b596e6807a
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+#define ACP6x_REG_START 0x1240000
+#define ACP6x_REG_END 0x1250200
+#define ACP6x_DEVS 3
+#define ACP6x_PDM_MODE 1
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp6x_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp6x_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 acp6x_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp6x_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
diff --git a/sound/soc/amd/yc/acp6x_chip_offset_byte.h b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..f05fb2dfb5da
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp6x_OFFSET_HEADER
+#define _acp6x_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_9 0x1240C44
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_9 0x1240C48
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_10 0x1240C4C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_10 0x1240C50
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_11 0x1240C54
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_11 0x1240C58
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_12 0x1240C5C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_12 0x1240C60
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_13 0x1240C64
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_13 0x1240C68
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_14 0x1240C6C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_14 0x1240C70
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_15 0x1240C74
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_15 0x1240C78
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_16 0x1240C7C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_16 0x1240C80
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_ZSC_DSP_CTRL 0x1241014
+#define ACP_ZSC_STS 0x1241018
+#define ACP_PGFSM_CONTROL 0x1241024
+#define ACP_PGFSM_STATUS 0x1241028
+#define ACP_CLKMUX_SEL 0x124102C
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+#define ACP_SW_PAD_KEEPER_EN 0x1241454
+#define ACP_SW_WAKE_EN 0x1241458
+#define ACP_I2S_WAKE_EN 0x124145C
+#define ACP_SW1_WAKE_EN 0x1241460
+
+/* Registers from ACP_P1_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241A00
+#define ACP_EXTERNAL_INTR_CNTL 0x1241A04
+#define ACP_EXTERNAL_INTR_CNTL1 0x1241A08
+#define ACP_EXTERNAL_INTR_STAT 0x1241A0C
+#define ACP_EXTERNAL_INTR_STAT1 0x1241A10
+#define ACP_ERROR_STATUS 0x1241A4C
+#define ACP_P1_SW_I2S_ERROR_REASON 0x1241A50
+#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x1241A6C
+#define ACP_P1_SW_I2S_TX_DMA_POS 0x1241A70
+#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x1241A74
+#define ACP_P1_SW_I2S_RX_DMA_POS 0x1241A78
+#define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x1241A7C
+#define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x1241A80
+#define ACP_SCRATCH_REG_BASE_ADDR 0x1241A84
+#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x1241A88
+#define ACP_P1_SW_BT_TX_DMA_POS 0x1241A8C
+#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x1241A90
+#define ACP_P1_SW_HS_TX_DMA_POS 0x1241A94
+#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x1241A98
+#define ACP_P1_SW_BT_RX_DMA_POS 0x1241A9C
+#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x1241AA0
+#define ACP_P1_SW_HS_RX_DMA_POS 0x1241AA4
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+
+/* Registers from ACP_WOV block */
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_WAKE 0x1242C54
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#define ACP_PDM_CLKDIV 0x1242C6C
+
+/* Registers from ACP_P1_AUDIO_BUFFERS block */
+#define ACP_P1_I2S_RX_RINGBUFADDR 0x1243A00
+#define ACP_P1_I2S_RX_RINGBUFSIZE 0x1243A04
+#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x1243A08
+#define ACP_P1_I2S_RX_FIFOADDR 0x1243A0C
+#define ACP_P1_I2S_RX_FIFOSIZE 0x1243A10
+#define ACP_P1_I2S_RX_DMA_SIZE 0x1243A14
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C
+#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x1243A20
+#define ACP_P1_I2S_TX_RINGBUFADDR 0x1243A24
+#define ACP_P1_I2S_TX_RINGBUFSIZE 0x1243A28
+#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x1243A2C
+#define ACP_P1_I2S_TX_FIFOADDR 0x1243A30
+#define ACP_P1_I2S_TX_FIFOSIZE 0x1243A34
+#define ACP_P1_I2S_TX_DMA_SIZE 0x1243A38
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1243A40
+#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x1243A44
+#define ACP_P1_BT_RX_RINGBUFADDR 0x1243A48
+#define ACP_P1_BT_RX_RINGBUFSIZE 0x1243A4C
+#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x1243A50
+#define ACP_P1_BT_RX_FIFOADDR 0x1243A54
+#define ACP_P1_BT_RX_FIFOSIZE 0x1243A58
+#define ACP_P1_BT_RX_DMA_SIZE 0x1243A5C
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1243A60
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x1243A64
+#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x1243A68
+#define ACP_P1_BT_TX_RINGBUFADDR 0x1243A6C
+#define ACP_P1_BT_TX_RINGBUFSIZE 0x1243A70
+#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x1243A74
+#define ACP_P1_BT_TX_FIFOADDR 0x1243A78
+#define ACP_P1_BT_TX_FIFOSIZE 0x1243A7C
+#define ACP_P1_BT_TX_DMA_SIZE 0x1243A80
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1243A84
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x1243A88
+#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x1243A8C
+#define ACP_P1_HS_RX_RINGBUFADDR 0x1243A90
+#define ACP_P1_HS_RX_RINGBUFSIZE 0x1243A94
+#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x1243A98
+#define ACP_P1_HS_RX_FIFOADDR 0x1243A9C
+#define ACP_P1_HS_RX_FIFOSIZE 0x1243AA0
+#define ACP_P1_HS_RX_DMA_SIZE 0x1243AA4
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x1243AA8
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x1243AAC
+#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x1243AB0
+#define ACP_P1_HS_TX_RINGBUFADDR 0x1243AB4
+#define ACP_P1_HS_TX_RINGBUFSIZE 0x1243AB8
+#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x1243ABC
+#define ACP_P1_HS_TX_FIFOADDR 0x1243AC0
+#define ACP_P1_HS_TX_FIFOSIZE 0x1243AC4
+#define ACP_P1_HS_TX_DMA_SIZE 0x1243AC8
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x1243ACC
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x1243AD0
+#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x1243AD4
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+#endif
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
new file mode 100644
index 000000000000..957eeb6fb8e3
--- /dev/null
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD Yellow Carp ACP PCI Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+struct acp6x_dev_data {
+ void __iomem *acp6x_base;
+ struct resource *res;
+ bool acp6x_audio_mode;
+ struct platform_device *pdev[ACP6x_DEVS];
+};
+
+static int acp6x_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ acp6x_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp6x_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp6x_disable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp6x_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ acp6x_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ acp6x_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int acp6x_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ acp6x_disable_interrupts(acp_base);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ acp6x_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
+{
+ struct acp6x_dev_data *adata;
+ struct pdm_dev_data *yc_pdm_data;
+ u32 val;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+
+ val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (val & BIT(PDM_DMA_STAT)) {
+ yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
+ acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (yc_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int snd_acp6x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp6x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP6x_DEVS];
+ int ret, index;
+ int val = 0x00;
+ u32 addr;
+ unsigned int irqflags;
+
+ irqflags = IRQF_SHARED;
+ /* Yellow Carp device check */
+ if (pci->revision != 0x60)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_info(&pci->dev, "Audio Mode %d\n", val);
+ break;
+ default:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource),
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res->name = "acp_iomem";
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
+
+ adata->acp6x_audio_mode = ACP6x_PDM_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_yc_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 1;
+ pdevinfo[0].res = adata->res;
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+
+ pdevinfo[2].name = "acp_yc_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+
+ for (index = 0; index < ACP6x_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ }
+ ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto unregister_devs;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp6x_deinit(adata->acp6x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp6x_suspend(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int __maybe_unused snd_acp6x_resume(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops acp6x_pm = {
+ SET_RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
+};
+
+static void snd_acp6x_remove(struct pci_dev *pci)
+{
+ struct acp6x_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
+ for (index = 0; index < ACP6x_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ }
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp6x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
+
+static struct pci_driver yc_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp6x_ids,
+ .probe = snd_acp6x_probe,
+ .remove = snd_acp6x_remove,
+ .driver = {
+ .pm = &acp6x_pm,
+ }
+};
+
+module_pci_driver(yc_acp6x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
index 6b3d9c05eaf2..1934767690b5 100644
--- a/sound/soc/atmel/atmel-i2s.c
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -342,8 +342,8 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
/* codec is slave, so cpu is master */
mr |= ATMEL_I2SC_MR_MODE_MASTER;
ret = atmel_i2s_get_gck_param(dev, params_rate(params));
@@ -351,7 +351,7 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return ret;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* codec is master, so cpu is slave */
mr |= ATMEL_I2SC_MR_MODE_SLAVE;
dev->gck_param = NULL;
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 6a63e8797a0b..26e2bc690d86 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -209,8 +209,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
if (frame_size < 0)
return frame_size;
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFC:
if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
&& ssc->clk_from_rk_pin)
/* Receiver Frame Synchro (i.e. capture)
@@ -220,7 +220,7 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
mck_div = 3;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
&& !ssc->clk_from_rk_pin)
/* Transmit Frame Synchro (i.e. playback)
@@ -232,8 +232,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
break;
}
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
r.num = ssc_p->mck_rate / mck_div / frame_size;
ret = snd_interval_ratnum(i, 1, &r, &num, &den);
@@ -243,8 +243,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
}
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ case SND_SOC_DAIFMT_CBP_CFP:
t.min = 8000;
t.max = ssc_p->mck_rate / mck_div / frame_size;
t.openmin = t.openmax = 0;
@@ -429,9 +429,9 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
/* Is the cpu-dai master of the frame clock? */
static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
{
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFC:
+ case SND_SOC_DAIFMT_CBC_CFC:
return 1;
}
return 0;
@@ -440,9 +440,9 @@ static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
/* Is the cpu-dai master of the bit clock? */
static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
{
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
return 1;
}
return 0;
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index 9e237580afa9..9c974c4e187d 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -66,7 +66,7 @@ static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
.stream_name = "WM8904 PCM",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &atmel_asoc_wm8904_ops,
SND_SOC_DAILINK_REG(pcm),
};
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
index 8988f024a732..6d1227a1d67b 100644
--- a/sound/soc/atmel/mchp-i2s-mcc.c
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -350,7 +350,7 @@ static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
/* We can't generate only FSYNC */
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS)
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_CBP_CFC)
return -EINVAL;
/* We can only reconfigure the IP when it's stopped */
@@ -546,20 +546,20 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
/* cpu is BCLK and LRC master */
mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
if (dev->sysclk)
mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
set_divs = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
/* cpu is BCLK master */
mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
set_divs = 1;
fallthrough;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* cpu is slave */
mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
if (dev->sysclk)
diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c
index 0be7b4221c14..f9331f7e80fe 100644
--- a/sound/soc/atmel/mikroe-proto.c
+++ b/sound/soc/atmel/mikroe-proto.c
@@ -129,9 +129,9 @@ static int snd_proto_probe(struct platform_device *pdev)
}
if (bitclkmaster) {
if (codec_np == bitclkmaster)
- dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
else
- dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
} else {
dai_fmt |= snd_soc_daifmt_parse_clock_provider_as_flag(np, NULL);
}
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index ed1f69b57024..915da92e1ec8 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -126,7 +126,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
.stream_name = "WM8731 PCM",
.init = at91sam9g20ek_wm8731_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
SND_SOC_DAILINK_REG(pcm),
};
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
index 7745250fd743..7c45dc4f8c1b 100644
--- a/sound/soc/atmel/sam9x5_wm8731.c
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -115,7 +115,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
dai->codecs->dai_name = "wm8731-hifi";
dai->init = sam9x5_wm8731_init;
dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM;
+ | SND_SOC_DAIFMT_CBP_CFP;
ret = snd_soc_of_parse_card_name(card, "atmel,model");
if (ret) {
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
index 50c3dc6936f9..1b3a31296c9b 100644
--- a/sound/soc/atmel/tse850-pcm5142.c
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -304,7 +304,7 @@ static struct snd_soc_dai_link tse850_dailink = {
.stream_name = "TSE-850-PCM",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFS,
+ | SND_SOC_DAIFMT_CBP_CFC,
SND_SOC_DAILINK_REG(pcm),
};
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index 5f8baad37a40..400eaf9f8b14 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -117,7 +117,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
SND_SOC_DAILINK_REG(db1200_i2s),
};
@@ -138,7 +138,7 @@ static struct snd_soc_dai_link db1300_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
SND_SOC_DAILINK_REG(db1300_i2s),
};
@@ -159,7 +159,7 @@ static struct snd_soc_dai_link db1550_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
SND_SOC_DAILINK_REG(db1550_i2s),
};
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
index 65bd39f5032d..740d4e052e4d 100644
--- a/sound/soc/au1x/i2sc.c
+++ b/sound/soc/au1x/i2sc.c
@@ -119,9 +119,9 @@ static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
goto out;
}
- /* I2S controller only supports master */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
+ /* I2S controller only supports provider */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC: /* CODEC consumer */
break;
default:
goto out;
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 767ce950d0da..b2b8896bb593 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -90,12 +90,12 @@ static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
goto out;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */
- ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP: /* CODEC provider */
+ ct |= PSC_I2SCFG_MS; /* PSC I2S consumer mode */
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
- ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */
+ case SND_SOC_DAIFMT_CBC_CFC: /* CODEC consumer */
+ ct &= ~PSC_I2SCFG_MS; /* PSC I2S provider mode */
break;
default:
goto out;
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index 3d668f449bc1..e3fc4bee8cfd 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -127,14 +127,14 @@ struct bcm2835_i2s_dev {
static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
{
- unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
if (dev->clk_prepared)
return;
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ switch (provider) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ case SND_SOC_DAIFMT_CBC_CFP:
clk_prepare_enable(dev->clk);
dev->clk_prepared = true;
break;
@@ -337,8 +337,8 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
unsigned int rx_mask, tx_mask;
unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos;
unsigned int mode, format;
- bool bit_clock_master = false;
- bool frame_sync_master = false;
+ bool bit_clock_provider = false;
+ bool frame_sync_provider = false;
bool frame_start_falling_edge = false;
uint32_t csreg;
int ret = 0;
@@ -383,36 +383,36 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
if (data_length > slot_width)
return -EINVAL;
- /* Check if CPU is bit clock master */
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- bit_clock_master = true;
+ /* Check if CPU is bit clock provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ case SND_SOC_DAIFMT_CBC_CFP:
+ bit_clock_provider = true;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBM_CFM:
- bit_clock_master = false;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ case SND_SOC_DAIFMT_CBP_CFP:
+ bit_clock_provider = false;
break;
default:
return -EINVAL;
}
- /* Check if CPU is frame sync master */
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- frame_sync_master = true;
+ /* Check if CPU is frame sync provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ frame_sync_provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFM:
- frame_sync_master = false;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFP:
+ frame_sync_provider = false;
break;
default:
return -EINVAL;
}
/* Clock should only be set up here if CPU is clock master */
- if (bit_clock_master &&
+ if (bit_clock_provider &&
(!dev->clk_prepared || dev->clk_rate != bclk_rate)) {
if (dev->clk_prepared)
bcm2835_i2s_stop_clock(dev);
@@ -501,11 +501,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/*
* Transmitting data immediately after frame start, eg
* in left-justified or DSP mode A, only works stable
- * if bcm2835 is the frame clock master.
+ * if bcm2835 is the frame clock provider.
*/
- if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master)
+ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider)
dev_warn(dev->dev,
- "Unstable slave config detected, L/R may be swapped");
+ "Unstable consumer config detected, L/R may be swapped");
/*
* Set format for both streams.
@@ -538,11 +538,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
mode |= BCM2835_I2S_FSLEN(framesync_length);
/* CLKM selects bcm2835 clock slave mode */
- if (!bit_clock_master)
+ if (!bit_clock_provider)
mode |= BCM2835_I2S_CLKM;
/* FSM selects bcm2835 frame sync slave mode */
- if (!frame_sync_master)
+ if (!frame_sync_provider)
mode |= BCM2835_I2S_FSM;
/* CLKI selects normal clocking mode, sampling on rising edge */
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
index fca5a3f2eec5..9698f4531c90 100644
--- a/sound/soc/bcm/cygnus-ssp.c
+++ b/sound/soc/bcm/cygnus-ssp.c
@@ -848,12 +848,12 @@ static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
ssp_newcfg = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 0;
break;
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
index 7b6cdc9c8a23..385290202912 100644
--- a/sound/soc/cirrus/edb93xx.c
+++ b/sound/soc/cirrus/edb93xx.c
@@ -60,7 +60,7 @@ static struct snd_soc_dai_link edb93xx_dai = {
.name = "CS4271",
.stream_name = "CS4271 HiFi",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &edb93xx_ops,
SND_SOC_DAILINK_REG(hifi),
};
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 0d26550d0df8..06c315a4d20a 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -245,14 +245,14 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* CPU is master */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* CPU is provider */
clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Codec is master */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Codec is provider */
clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
break;
diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
index c4b112921661..a286f5beeaeb 100644
--- a/sound/soc/cirrus/snappercl15.c
+++ b/sound/soc/cirrus/snappercl15.c
@@ -70,7 +70,7 @@ static struct snd_soc_dai_link snappercl15_dai = {
.name = "tlv320aic23",
.stream_name = "AIC23",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &snappercl15_ops,
SND_SOC_DAILINK_REG(aic23),
};
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index cac7e557edc8..c6043fa58c74 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -968,16 +968,16 @@ static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
if (pm860x->dir == PM860X_CLK_DIR_OUT) {
inf |= PCM_INF2_MASTER;
ret = 0;
}
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN) {
inf &= ~PCM_INF2_MASTER;
ret = 0;
@@ -1072,15 +1072,15 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
if (pm860x->dir == PM860X_CLK_DIR_OUT)
inf |= PCM_INF2_MASTER;
else
return -EINVAL;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN)
inf &= ~PCM_INF2_MASTER;
else
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 216cea04ad70..326f2d611ad4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -61,6 +61,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS35L34
imply SND_SOC_CS35L35
imply SND_SOC_CS35L36
+ imply SND_SOC_CS35L41_SPI
+ imply SND_SOC_CS35L41_I2C
imply SND_SOC_CS42L42
imply SND_SOC_CS42L51_I2C
imply SND_SOC_CS42L52
@@ -115,6 +117,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_MAX98357A
imply SND_SOC_MAX98371
imply SND_SOC_MAX98504
+ imply SND_SOC_MAX98520
imply SND_SOC_MAX9867
imply SND_SOC_MAX98925
imply SND_SOC_MAX98926
@@ -136,6 +139,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_NAU8315
imply SND_SOC_NAU8540
imply SND_SOC_NAU8810
+ imply SND_SOC_NAU8821
imply SND_SOC_NAU8822
imply SND_SOC_NAU8824
imply SND_SOC_NAU8825
@@ -180,6 +184,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT5677
imply SND_SOC_RT5682_I2C
imply SND_SOC_RT5682_SDW
+ imply SND_SOC_RT5682S
imply SND_SOC_RT700_SDW
imply SND_SOC_RT711_SDW
imply SND_SOC_RT711_SDCA_SDW
@@ -187,6 +192,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT715_SDCA_SDW
imply SND_SOC_RT1308_SDW
imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT9120
imply SND_SOC_SDW_MOCKUP
imply SND_SOC_SGTL5000
imply SND_SOC_SI476X
@@ -330,6 +336,7 @@ config SND_SOC_WM_HUBS
config SND_SOC_WM_ADSP
tristate
+ select CS_DSP
select SND_SOC_COMPRESS
default y if SND_SOC_MADERA=y
default y if SND_SOC_CS47L24=y
@@ -602,6 +609,16 @@ config SND_SOC_CS35L36
tristate "Cirrus Logic CS35L36 CODEC"
depends on I2C
+config SND_SOC_CS35L41_SPI
+ tristate "Cirrus Logic CS35L41 CODEC (SPI)"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+
+config SND_SOC_CS35L41_I2C
+ tristate "Cirrus Logic CS35L41 CODEC (I2C)"
+ depends on I2C
+ select REGMAP_I2C
+
config SND_SOC_CS42L42
tristate "Cirrus Logic CS42L42 CODEC"
depends on I2C
@@ -922,6 +939,17 @@ config SND_SOC_MAX98927
tristate "Maxim Integrated MAX98927 Speaker Amplifier"
depends on I2C
+config SND_SOC_MAX98520
+ tristate "Maxim Integrated MAX98520 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Maxim Integrated MAX98520 audio
+ amplifier, which implements a tripler charge pump
+ based boost converter and supports sample rates of
+ 8KHz to 192KHz.
+
+ To compile this driver as a module, choose M here.
+
config SND_SOC_MAX98373
tristate
@@ -1249,6 +1277,10 @@ config SND_SOC_RT5682_SDW
select SND_SOC_RT5682
select REGMAP_SOUNDWIRE
+config SND_SOC_RT5682S
+ tristate
+ depends on I2C
+
config SND_SOC_RT700
tristate
@@ -1288,6 +1320,15 @@ config SND_SOC_RT715_SDCA_SDW
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
+config SND_SOC_RT9120
+ tristate "Richtek RT9120 Stereo Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ help
+ Enable support for Richtek RT9120 20W, stereo, inductor-less,
+ high-efficiency Class-D audio amplifier.
+
config SND_SOC_SDW_MOCKUP
tristate "SoundWire mockup codec"
depends on EXPERT
@@ -1905,6 +1946,10 @@ config SND_SOC_NAU8810
tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
depends on I2C
+config SND_SOC_NAU8821
+ tristate "Nuvoton Technology Corporation NAU88L21 CODEC"
+ depends on I2C
+
config SND_SOC_NAU8822
tristate "Nuvoton Technology Corporation NAU88C22 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 8dcea2c4604a..9acfbcbfc46d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -54,6 +54,8 @@ snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs35l34-objs := cs35l34.o
snd-soc-cs35l35-objs := cs35l35.o
snd-soc-cs35l36-objs := cs35l36.o
+snd-soc-cs35l41-spi-objs := cs35l41-spi.o cs35l41.o cs35l41-tables.o
+snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o cs35l41.o cs35l41-tables.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -123,6 +125,7 @@ snd-soc-max9867-objs := max9867.o
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-max98373-objs := max98373.o
snd-soc-max98373-i2c-objs := max98373-i2c.o
snd-soc-max98373-sdw-objs := max98373-sdw.o
@@ -141,6 +144,7 @@ snd-soc-mt6660-objs := mt6660.o
snd-soc-nau8315-objs := nau8315.o
snd-soc-nau8540-objs := nau8540.o
snd-soc-nau8810-objs := nau8810.o
+snd-soc-nau8821-objs := nau8821.o
snd-soc-nau8822-objs := nau8822.o
snd-soc-nau8824-objs := nau8824.o
snd-soc-nau8825-objs := nau8825.o
@@ -198,11 +202,13 @@ snd-soc-rt5677-spi-objs := rt5677-spi.o
snd-soc-rt5682-objs := rt5682.o
snd-soc-rt5682-sdw-objs := rt5682-sdw.o
snd-soc-rt5682-i2c-objs := rt5682-i2c.o
+snd-soc-rt5682s-objs := rt5682s.o
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-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
snd-soc-sdw-mockup-objs := sdw-mockup.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
@@ -385,6 +391,8 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o
+obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o
+obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
@@ -450,6 +458,7 @@ obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
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_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
@@ -468,6 +477,7 @@ obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o
obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o
obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
@@ -526,11 +536,13 @@ obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o
obj-$(CONFIG_SND_SOC_RT5682_I2C) += snd-soc-rt5682-i2c.o
obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o
+obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o
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_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
obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 5525e1ccab76..aefafb0b7b97 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2104,26 +2104,26 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
BIT(AB8500_DIGIFCONF3_IF0MASTER);
val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
dev_dbg(dai->component->dev,
- "%s: IF0 Master-mode: AB8500 master.\n", __func__);
+ "%s: IF0 Master-mode: AB8500 provider.\n", __func__);
val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & FRM slave */
+ case SND_SOC_DAIFMT_CBC_CFC:
dev_dbg(dai->component->dev,
- "%s: IF0 Master-mode: AB8500 slave.\n", __func__);
+ "%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
dev_err(dai->component->dev,
- "%s: ERROR: The device is either a master or a slave.\n",
+ "%s: ERROR: The device is either a provider or a consumer.\n",
__func__);
fallthrough;
default:
dev_err(dai->component->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ "%s: ERROR: Unsupporter clocking mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
}
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 08a5651bed9f..29e1689da67f 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -148,9 +148,9 @@ static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- /* ALCLK,ABCLK are both output, AD1836 can only be master */
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ /* ALCLK,ABCLK are both output, AD1836 can only be provider */
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 278a55af158b..30b98b4267e1 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -243,22 +243,22 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
if (fmt & SND_SOC_DAIFMT_DSP_A)
dac_fmt ^= AD193X_DAC_LEFT_HIGH;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
adc_fmt |= AD193X_ADC_LCR_MASTER;
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+ case SND_SOC_DAIFMT_CBC_CFP:
adc_fmt |= AD193X_ADC_LCR_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ case SND_SOC_DAIFMT_CBP_CFC:
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index 6811a8b3866d..1faa4c426365 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -30,7 +30,7 @@ struct adau1372 {
void (*switch_mode)(struct device *dev);
bool use_pll;
bool enabled;
- bool master;
+ bool clock_provider;
struct snd_pcm_hw_constraint_list rate_constraints;
unsigned int slot_width;
@@ -578,13 +578,13 @@ static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
unsigned int sai0 = 0, sai1 = 0;
bool invert_lrclk = false;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- adau1372->master = true;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adau1372->clock_provider = true;
sai1 |= ADAU1372_SAI1_MS;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- adau1372->master = false;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1372->clock_provider = false;
break;
default:
return -EINVAL;
@@ -714,7 +714,7 @@ static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 4:
sai0 = ADAU1372_SAI0_SAI_TDM4;
- if (adau1372->master)
+ if (adau1372->clock_provider)
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER;
else
adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4;
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 9887aa6f0be5..46128aaceae9 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -28,7 +28,7 @@ struct adau1373_dai {
unsigned int clk_src;
unsigned int sysclk;
bool enable_src;
- bool master;
+ bool clock_provider;
};
struct adau1373 {
@@ -827,7 +827,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
dai = sink->name[3] - '1';
- if (!adau1373->dais[dai].master)
+ if (!adau1373->dais[dai].clock_provider)
return 0;
if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
@@ -1102,14 +1102,14 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
unsigned int ctrl;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl = ADAU1373_DAI_MASTER;
- adau1373_dai->master = true;
+ adau1373_dai->clock_provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl = 0;
- adau1373_dai->master = false;
+ adau1373_dai->clock_provider = false;
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 5ce74697564a..c5bf461c0b7e 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -482,13 +482,13 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int serictl = 0x00, seroctl = 0x00;
bool invert_lrclk;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
/* master, 64-bits per sample, 1 frame per sample */
seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
| ADAU1701_SEROCTL_OLF1024;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 8aae7ab74091..af05463af4ac 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -556,12 +556,12 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int ctrl0_mask;
int lrclk_pol;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
adau->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl0 = 0;
adau->master = false;
break;
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index e347a48131d1..5fcbdf2ec313 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -124,10 +124,10 @@ struct adau1977 {
struct device *dev;
void (*switch_mode)(struct device *dev);
- unsigned int max_master_fs;
+ unsigned int max_clock_provider_fs;
unsigned int slot_width;
bool enabled;
- bool master;
+ bool clock_provider;
};
static const struct reg_default adau1977_reg_defaults[] = {
@@ -330,7 +330,7 @@ static int adau1977_hw_params(struct snd_pcm_substream *substream,
ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
}
- if (adau1977->master) {
+ if (adau1977->clock_provider) {
switch (params_width(params)) {
case 16:
ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
@@ -504,7 +504,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slots == 0) {
/* 0 = No fixed slot width */
adau1977->slot_width = 0;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
return regmap_update_bits(adau1977->regmap,
ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK,
ADAU1977_SAI_CTRL0_SAI_I2S);
@@ -533,7 +533,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 24:
/* We can only generate 16 bit or 32 bit wide slots */
- if (adau1977->master)
+ if (adau1977->clock_provider)
return -EINVAL;
ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
break;
@@ -593,8 +593,8 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
adau1977->slot_width = width;
- /* In master mode the maximum bitclock is 24.576 MHz */
- adau1977->max_master_fs = min(192000, 24576000 / width / slots);
+ /* In clock provider mode the maximum bitclock is 24.576 MHz */
+ adau1977->max_clock_provider_fs = min(192000, 24576000 / width / slots);
return 0;
}
@@ -620,13 +620,13 @@ static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
bool invert_lrclk;
int ret;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- adau1977->master = false;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1977->clock_provider = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
- adau1977->master = true;
+ adau1977->clock_provider = true;
break;
default:
return -EINVAL;
@@ -714,9 +714,10 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
- if (adau1977->master)
+ if (adau1977->clock_provider)
snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+ SNDRV_PCM_HW_PARAM_RATE, 8000,
+ adau1977->max_clock_provider_fs);
if (formats != 0)
snd_pcm_hw_constraint_mask64(substream->runtime,
@@ -913,7 +914,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->type = type;
adau1977->regmap = regmap;
adau1977->switch_mode = switch_mode;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
adau1977->constraints.list = adau1977_rates;
adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 75a649108106..90f3a5e9e31f 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -369,12 +369,12 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
unsigned int capture = 0x00;
unsigned int playback = 0x00;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
capture |= ADAV80X_CAPTURE_MODE_MASTER;
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 979cfb165eed..dc4747c77a7a 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -81,8 +81,8 @@ static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- /* This device can only be slave */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ /* This device can only be consumer */
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
index 5d46ae85566c..e0a6451851e8 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -151,8 +151,8 @@ static const struct snd_soc_dapm_route ak4118_dapm_routes[] = {
};
-static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118,
- unsigned int format)
+static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118,
+ unsigned int format)
{
int dif;
@@ -173,8 +173,8 @@ static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118,
return dif;
}
-static int ak4118_set_dai_fmt_slave(struct ak4118_priv *ak4118,
- unsigned int format)
+static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118,
+ unsigned int format)
{
int dif;
@@ -201,14 +201,12 @@ static int ak4118_set_dai_fmt(struct snd_soc_dai *dai,
int dif;
int ret = 0;
- switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- /* component is master */
- dif = ak4118_set_dai_fmt_master(ak4118, format);
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dif = ak4118_set_dai_fmt_provider(ak4118, format);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- /*component is slave */
- dif = ak4118_set_dai_fmt_slave(ak4118, format);
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dif = ak4118_set_dai_fmt_consumer(ak4118, format);
break;
default:
ret = -ENOTSUPP;
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
index 29eb78702bf3..baa9ff5d0ce5 100644
--- a/sound/soc/codecs/ak4458.c
+++ b/sound/soc/codecs/ak4458.c
@@ -464,14 +464,14 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
int ret;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS: /* Slave Mode */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */
break;
- case SND_SOC_DAIFMT_CBM_CFM: /* Master Mode is not supported */
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
default:
- dev_err(component->dev, "Master mode unsupported\n");
+ dev_err(component->dev, "Clock provider mode unsupported\n");
return -EINVAL;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index c49c58eeb476..c284dcc5af76 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -392,13 +392,13 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
data = MCKO | PMPLL; /* use MCKO */
bcko = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set clocking for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
data |= MS;
bcko = BCKO_64;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index eb435235b5a3..e9d1251c4265 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -520,11 +520,11 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
/* set master/slave audio interface */
mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode |= AK4671_M_S;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
mode &= ~(AK4671_M_S);
break;
default:
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
index 37d4600b6f2c..c94cfde3e4a8 100644
--- a/sound/soc/codecs/ak5558.c
+++ b/sound/soc/codecs/ak5558.c
@@ -198,13 +198,13 @@ static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct snd_soc_component *component = dai->component;
u8 format;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
default:
dev_err(dai->dev, "Clock mode unsupported");
return -EINVAL;
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 54f489837162..b10357a6d655 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -641,12 +641,12 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = ALC5623_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5623_DAI_SDP_SLAVE_MODE;
break;
default:
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 79813882a955..6d7af3736a91 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -815,12 +815,12 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = ALC5632_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5632_DAI_SDP_SLAVE_MODE;
break;
default:
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
index 05bbacd0d174..598e09024e23 100644
--- a/sound/soc/codecs/cpcap.c
+++ b/sound/soc/codecs/cpcap.c
@@ -1168,15 +1168,15 @@ static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
/*
* "HiFi Playback" should always be configured as
- * SND_SOC_DAIFMT_CBM_CFM - codec clk & frm master
+ * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider
* SND_SOC_DAIFMT_I2S - I2S mode
*/
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
val &= ~BIT(CPCAP_BIT_SMB_ST_DAC);
break;
default:
- dev_err(dev, "HiFi dai fmt failed: CPCAP should be master");
+ dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider");
return -EINVAL;
}
@@ -1318,15 +1318,15 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
/*
* "Voice Playback" and "Voice Capture" should always be
- * configured as SND_SOC_DAIFMT_CBM_CFM - codec clk & frm
- * master
+ * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm
+ * provider
*/
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
val &= ~BIT(CPCAP_BIT_SMB_CDC);
break;
default:
- dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the master");
+ dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider");
val &= ~BIT(CPCAP_BIT_SMB_CDC);
break;
}
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
index a201d652aca2..9b92e1a0d1a3 100644
--- a/sound/soc/codecs/cros_ec_codec.c
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -283,8 +283,8 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct ec_param_ec_codec_i2s_rx p;
enum ec_codec_i2s_rx_daifmt daifmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
new file mode 100644
index 000000000000..d5fa8d2c4a70
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-i2c.c -- CS35l41 I2C driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/cs35l41.h>
+#include "cs35l41.h"
+
+static struct regmap_config cs35l41_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+ { "cs35l40", 0 },
+ { "cs35l41", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
+
+static int cs35l41_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l41_private *cs35l41;
+ struct device *dev = &client->dev;
+ struct cs35l41_platform_data *pdata = dev_get_platdata(dev);
+ const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
+ int ret;
+
+ cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = client->irq;
+
+ i2c_set_clientdata(client, cs35l41);
+ cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l41->regmap)) {
+ ret = PTR_ERR(cs35l41->regmap);
+ dev_err(cs35l41->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ return cs35l41_probe(cs35l41, pdata);
+}
+
+static int cs35l41_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l41_private *cs35l41 = i2c_get_clientdata(client);
+
+ cs35l41_remove(cs35l41);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_i2c,
+ .probe = cs35l41_i2c_probe,
+ .remove = cs35l41_i2c_remove,
+};
+
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
new file mode 100644
index 000000000000..90a921f726c3
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-spi.c -- CS35l41 SPI driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include <sound/cs35l41.h>
+#include "cs35l41.h"
+
+static struct regmap_config cs35l41_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct spi_device_id cs35l41_id_spi[] = {
+ { "cs35l40", 0 },
+ { "cs35l41", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, cs35l41_id_spi);
+
+static void cs35l41_spi_otp_setup(struct cs35l41_private *cs35l41,
+ bool is_pre_setup, unsigned int *freq)
+{
+ struct spi_device *spi;
+ u32 orig_spi_freq;
+
+ spi = to_spi_device(cs35l41->dev);
+
+ if (!spi) {
+ dev_err(cs35l41->dev, "%s: No SPI device\n", __func__);
+ return;
+ }
+
+ if (is_pre_setup) {
+ orig_spi_freq = spi->max_speed_hz;
+ if (orig_spi_freq > CS35L41_SPI_MAX_FREQ_OTP) {
+ spi->max_speed_hz = CS35L41_SPI_MAX_FREQ_OTP;
+ spi_setup(spi);
+ }
+ *freq = orig_spi_freq;
+ } else {
+ if (spi->max_speed_hz != *freq) {
+ spi->max_speed_hz = *freq;
+ spi_setup(spi);
+ }
+ }
+}
+
+static int cs35l41_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
+ struct cs35l41_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct cs35l41_private *cs35l41;
+ int ret;
+
+ cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs35l41);
+ cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l41->regmap)) {
+ ret = PTR_ERR(cs35l41->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l41->dev = &spi->dev;
+ cs35l41->irq = spi->irq;
+ cs35l41->otp_setup = cs35l41_spi_otp_setup;
+
+ return cs35l41_probe(cs35l41, pdata);
+}
+
+static int cs35l41_spi_remove(struct spi_device *spi)
+{
+ struct cs35l41_private *cs35l41 = spi_get_drvdata(spi);
+
+ cs35l41_remove(cs35l41);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_spi,
+ .probe = cs35l41_spi_probe,
+ .remove = cs35l41_spi_remove,
+};
+
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c
new file mode 100644
index 000000000000..964e530afa27
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-tables.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-tables.c -- CS35L41 ALSA SoC audio driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include "cs35l41.h"
+
+const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = {
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { CS35L41_PWR_CTRL3, 0x01000010 },
+ { CS35L41_GPIO_PAD_CONTROL, 0x00000000 },
+ { CS35L41_SP_ENABLES, 0x00000000 },
+ { CS35L41_SP_RATE_CTRL, 0x00000028 },
+ { CS35L41_SP_FORMAT, 0x18180200 },
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 },
+ { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 },
+ { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 },
+ { CS35L41_SP_TX_WL, 0x00000018 },
+ { CS35L41_SP_RX_WL, 0x00000018 },
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 },
+ { CS35L41_ASP_TX1_SRC, 0x00000018 },
+ { CS35L41_ASP_TX2_SRC, 0x00000019 },
+ { CS35L41_ASP_TX3_SRC, 0x00000020 },
+ { CS35L41_ASP_TX4_SRC, 0x00000021 },
+ { CS35L41_DSP1_RX1_SRC, 0x00000008 },
+ { CS35L41_DSP1_RX2_SRC, 0x00000009 },
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 },
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 },
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 },
+ { CS35L41_DSP1_RX6_SRC, 0x00000021 },
+ { CS35L41_DSP1_RX7_SRC, 0x0000003A },
+ { CS35L41_DSP1_RX8_SRC, 0x00000001 },
+ { CS35L41_NGATE1_SRC, 0x00000008 },
+ { CS35L41_NGATE2_SRC, 0x00000009 },
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 },
+ { CS35L41_CLASSH_CFG, 0x000B0405 },
+ { CS35L41_WKFET_CFG, 0x00000111 },
+ { CS35L41_NG_CFG, 0x00000033 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000273 },
+ { CS35L41_GPIO1_CTRL1, 0xE1000001 },
+ { CS35L41_GPIO2_CTRL1, 0xE1000001 },
+ { CS35L41_MIXER_NGATE_CFG, 0x00000000 },
+ { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 },
+ { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 },
+};
+
+bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_REVID:
+ case CS35L41_FABID:
+ case CS35L41_RELID:
+ case CS35L41_OTPID:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_OTP_CTRL0:
+ case CS35L41_OTP_CTRL3:
+ case CS35L41_OTP_CTRL4:
+ case CS35L41_OTP_CTRL5:
+ case CS35L41_OTP_CTRL6:
+ case CS35L41_OTP_CTRL7:
+ case CS35L41_OTP_CTRL8:
+ case CS35L41_PWR_CTRL1:
+ case CS35L41_PWR_CTRL2:
+ case CS35L41_PWR_CTRL3:
+ case CS35L41_CTRL_OVRRIDE:
+ case CS35L41_AMP_OUT_MUTE:
+ case CS35L41_PROTECT_REL_ERR_IGN:
+ case CS35L41_GPIO_PAD_CONTROL:
+ case CS35L41_JTAG_CONTROL:
+ case CS35L41_PLL_CLK_CTRL:
+ case CS35L41_DSP_CLK_CTRL:
+ case CS35L41_GLOBAL_CLK_CTRL:
+ case CS35L41_DATA_FS_SEL:
+ case CS35L41_MDSYNC_EN:
+ case CS35L41_MDSYNC_TX_ID:
+ case CS35L41_MDSYNC_PWR_CTRL:
+ case CS35L41_MDSYNC_DATA_TX:
+ case CS35L41_MDSYNC_TX_STATUS:
+ case CS35L41_MDSYNC_DATA_RX:
+ case CS35L41_MDSYNC_RX_STATUS:
+ case CS35L41_MDSYNC_ERR_STATUS:
+ case CS35L41_MDSYNC_SYNC_PTE2:
+ case CS35L41_MDSYNC_SYNC_PTE3:
+ case CS35L41_MDSYNC_SYNC_MSM_STATUS:
+ case CS35L41_BSTCVRT_VCTRL1:
+ case CS35L41_BSTCVRT_VCTRL2:
+ case CS35L41_BSTCVRT_PEAK_CUR:
+ case CS35L41_BSTCVRT_SFT_RAMP:
+ case CS35L41_BSTCVRT_COEFF:
+ case CS35L41_BSTCVRT_SLOPE_LBST:
+ case CS35L41_BSTCVRT_SW_FREQ:
+ case CS35L41_BSTCVRT_DCM_CTRL:
+ case CS35L41_BSTCVRT_DCM_MODE_FORCE:
+ case CS35L41_BSTCVRT_OVERVOLT_CTRL:
+ case CS35L41_VI_VOL_POL:
+ case CS35L41_DTEMP_WARN_THLD:
+ case CS35L41_DTEMP_CFG:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_VPVBST_FS_SEL:
+ case CS35L41_SP_ENABLES:
+ case CS35L41_SP_RATE_CTRL:
+ case CS35L41_SP_FORMAT:
+ case CS35L41_SP_HIZ_CTRL:
+ case CS35L41_SP_FRAME_TX_SLOT:
+ case CS35L41_SP_FRAME_RX_SLOT:
+ case CS35L41_SP_TX_WL:
+ case CS35L41_SP_RX_WL:
+ case CS35L41_DAC_PCM1_SRC:
+ case CS35L41_ASP_TX1_SRC:
+ case CS35L41_ASP_TX2_SRC:
+ case CS35L41_ASP_TX3_SRC:
+ case CS35L41_ASP_TX4_SRC:
+ case CS35L41_DSP1_RX1_SRC:
+ case CS35L41_DSP1_RX2_SRC:
+ case CS35L41_DSP1_RX3_SRC:
+ case CS35L41_DSP1_RX4_SRC:
+ case CS35L41_DSP1_RX5_SRC:
+ case CS35L41_DSP1_RX6_SRC:
+ case CS35L41_DSP1_RX7_SRC:
+ case CS35L41_DSP1_RX8_SRC:
+ case CS35L41_NGATE1_SRC:
+ case CS35L41_NGATE2_SRC:
+ case CS35L41_AMP_DIG_VOL_CTRL:
+ case CS35L41_VPBR_CFG:
+ case CS35L41_VBBR_CFG:
+ case CS35L41_VPBR_STATUS:
+ case CS35L41_VBBR_STATUS:
+ case CS35L41_OVERTEMP_CFG:
+ case CS35L41_AMP_ERR_VOL:
+ case CS35L41_VOL_STATUS_TO_DSP:
+ case CS35L41_CLASSH_CFG:
+ case CS35L41_WKFET_CFG:
+ case CS35L41_NG_CFG:
+ case CS35L41_AMP_GAIN_CTRL:
+ case CS35L41_DAC_MSM_CFG:
+ case CS35L41_IRQ1_CFG:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ1_MASK1:
+ case CS35L41_IRQ1_MASK2:
+ case CS35L41_IRQ1_MASK3:
+ case CS35L41_IRQ1_MASK4:
+ case CS35L41_IRQ1_FRC1:
+ case CS35L41_IRQ1_FRC2:
+ case CS35L41_IRQ1_FRC3:
+ case CS35L41_IRQ1_FRC4:
+ case CS35L41_IRQ1_EDGE1:
+ case CS35L41_IRQ1_EDGE4:
+ case CS35L41_IRQ1_POL1:
+ case CS35L41_IRQ1_POL2:
+ case CS35L41_IRQ1_POL3:
+ case CS35L41_IRQ1_POL4:
+ case CS35L41_IRQ1_DB3:
+ case CS35L41_IRQ2_CFG:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_IRQ2_MASK1:
+ case CS35L41_IRQ2_MASK2:
+ case CS35L41_IRQ2_MASK3:
+ case CS35L41_IRQ2_MASK4:
+ case CS35L41_IRQ2_FRC1:
+ case CS35L41_IRQ2_FRC2:
+ case CS35L41_IRQ2_FRC3:
+ case CS35L41_IRQ2_FRC4:
+ case CS35L41_IRQ2_EDGE1:
+ case CS35L41_IRQ2_EDGE4:
+ case CS35L41_IRQ2_POL1:
+ case CS35L41_IRQ2_POL2:
+ case CS35L41_IRQ2_POL3:
+ case CS35L41_IRQ2_POL4:
+ case CS35L41_IRQ2_DB3:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_GPIO1_CTRL1:
+ case CS35L41_GPIO2_CTRL1:
+ case CS35L41_MIXER_NGATE_CFG:
+ case CS35L41_MIXER_NGATE_CH1_CFG:
+ case CS35L41_MIXER_NGATE_CH2_CFG:
+ case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_CLOCK_DETECT_1:
+ case CS35L41_DIE_STS1:
+ case CS35L41_DIE_STS2:
+ case CS35L41_TEMP_CAL1:
+ case CS35L41_TEMP_CAL2:
+ case CS35L41_OTP_TRIM_1:
+ case CS35L41_OTP_TRIM_2:
+ case CS35L41_OTP_TRIM_3:
+ case CS35L41_OTP_TRIM_4:
+ case CS35L41_OTP_TRIM_5:
+ case CS35L41_OTP_TRIM_6:
+ case CS35L41_OTP_TRIM_7:
+ case CS35L41_OTP_TRIM_8:
+ case CS35L41_OTP_TRIM_9:
+ case CS35L41_OTP_TRIM_10:
+ case CS35L41_OTP_TRIM_11:
+ case CS35L41_OTP_TRIM_12:
+ case CS35L41_OTP_TRIM_13:
+ case CS35L41_OTP_TRIM_14:
+ case CS35L41_OTP_TRIM_15:
+ case CS35L41_OTP_TRIM_16:
+ case CS35L41_OTP_TRIM_17:
+ case CS35L41_OTP_TRIM_18:
+ case CS35L41_OTP_TRIM_19:
+ case CS35L41_OTP_TRIM_20:
+ case CS35L41_OTP_TRIM_21:
+ case CS35L41_OTP_TRIM_22:
+ case CS35L41_OTP_TRIM_23:
+ case CS35L41_OTP_TRIM_24:
+ case CS35L41_OTP_TRIM_25:
+ case CS35L41_OTP_TRIM_26:
+ case CS35L41_OTP_TRIM_27:
+ case CS35L41_OTP_TRIM_28:
+ case CS35L41_OTP_TRIM_29:
+ case CS35L41_OTP_TRIM_30:
+ case CS35L41_OTP_TRIM_31:
+ case CS35L41_OTP_TRIM_32:
+ case CS35L41_OTP_TRIM_33:
+ case CS35L41_OTP_TRIM_34:
+ case CS35L41_OTP_TRIM_35:
+ case CS35L41_OTP_TRIM_36:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ /*test regs*/
+ case CS35L41_PLL_OVR:
+ case CS35L41_BST_TEST_DUTY:
+ case CS35L41_DIGPWM_IOCTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cs35l41_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_SFT_RESET:
+ case CS35L41_FABID:
+ case CS35L41_REVID:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ1_FRC1:
+ case CS35L41_IRQ1_FRC2:
+ case CS35L41_IRQ1_FRC3:
+ case CS35L41_IRQ1_FRC4:
+ case CS35L41_IRQ1_EDGE1:
+ case CS35L41_IRQ1_EDGE4:
+ case CS35L41_IRQ1_POL1:
+ case CS35L41_IRQ1_POL2:
+ case CS35L41_IRQ1_POL3:
+ case CS35L41_IRQ1_POL4:
+ case CS35L41_IRQ1_DB3:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_IRQ2_FRC1:
+ case CS35L41_IRQ2_FRC2:
+ case CS35L41_IRQ2_FRC3:
+ case CS35L41_IRQ2_FRC4:
+ case CS35L41_IRQ2_EDGE1:
+ case CS35L41_IRQ2_EDGE4:
+ case CS35L41_IRQ2_POL1:
+ case CS35L41_IRQ2_POL2:
+ case CS35L41_IRQ2_POL3:
+ case CS35L41_IRQ2_POL4:
+ case CS35L41_IRQ2_DB3:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_OTP_TRIM_1:
+ case CS35L41_OTP_TRIM_2:
+ case CS35L41_OTP_TRIM_3:
+ case CS35L41_OTP_TRIM_4:
+ case CS35L41_OTP_TRIM_5:
+ case CS35L41_OTP_TRIM_6:
+ case CS35L41_OTP_TRIM_7:
+ case CS35L41_OTP_TRIM_8:
+ case CS35L41_OTP_TRIM_9:
+ case CS35L41_OTP_TRIM_10:
+ case CS35L41_OTP_TRIM_11:
+ case CS35L41_OTP_TRIM_12:
+ case CS35L41_OTP_TRIM_13:
+ case CS35L41_OTP_TRIM_14:
+ case CS35L41_OTP_TRIM_15:
+ case CS35L41_OTP_TRIM_16:
+ case CS35L41_OTP_TRIM_17:
+ case CS35L41_OTP_TRIM_18:
+ case CS35L41_OTP_TRIM_19:
+ case CS35L41_OTP_TRIM_20:
+ case CS35L41_OTP_TRIM_21:
+ case CS35L41_OTP_TRIM_22:
+ case CS35L41_OTP_TRIM_23:
+ case CS35L41_OTP_TRIM_24:
+ case CS35L41_OTP_TRIM_25:
+ case CS35L41_OTP_TRIM_26:
+ case CS35L41_OTP_TRIM_27:
+ case CS35L41_OTP_TRIM_28:
+ case CS35L41_OTP_TRIM_29:
+ case CS35L41_OTP_TRIM_30:
+ case CS35L41_OTP_TRIM_31:
+ case CS35L41_OTP_TRIM_32:
+ case CS35L41_OTP_TRIM_33:
+ case CS35L41_OTP_TRIM_34:
+ case CS35L41_OTP_TRIM_35:
+ case CS35L41_OTP_TRIM_36:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00000000, 0, 1 }, /*extra bit*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00004000, 11, 1 }, /*VMON_POL*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = {
+ {
+ .id = 0x01,
+ .map = otp_map_1,
+ .num_elements = CS35L41_NUM_OTP_ELEM,
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x02,
+ .map = otp_map_2,
+ .num_elements = CS35L41_NUM_OTP_ELEM,
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x03,
+ .map = otp_map_2,
+ .num_elements = CS35L41_NUM_OTP_ELEM,
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x06,
+ .map = otp_map_2,
+ .num_elements = CS35L41_NUM_OTP_ELEM,
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x08,
+ .map = otp_map_1,
+ .num_elements = CS35L41_NUM_OTP_ELEM,
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+};
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
new file mode 100644
index 000000000000..94ed21d7676f
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.c
@@ -0,0 +1,1445 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 ALSA SoC audio driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs35l41.h"
+
+static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l41_pll_sysclk_config {
+ int freq;
+ int clk_cfg;
+};
+
+static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = {
+ { 32768, 0x00 },
+ { 8000, 0x01 },
+ { 11025, 0x02 },
+ { 12000, 0x03 },
+ { 16000, 0x04 },
+ { 22050, 0x05 },
+ { 24000, 0x06 },
+ { 32000, 0x07 },
+ { 44100, 0x08 },
+ { 48000, 0x09 },
+ { 88200, 0x0A },
+ { 96000, 0x0B },
+ { 128000, 0x0C },
+ { 176400, 0x0D },
+ { 192000, 0x0E },
+ { 256000, 0x0F },
+ { 352800, 0x10 },
+ { 384000, 0x11 },
+ { 512000, 0x12 },
+ { 705600, 0x13 },
+ { 750000, 0x14 },
+ { 768000, 0x15 },
+ { 1000000, 0x16 },
+ { 1024000, 0x17 },
+ { 1200000, 0x18 },
+ { 1411200, 0x19 },
+ { 1500000, 0x1A },
+ { 1536000, 0x1B },
+ { 2000000, 0x1C },
+ { 2048000, 0x1D },
+ { 2400000, 0x1E },
+ { 2822400, 0x1F },
+ { 3000000, 0x20 },
+ { 3072000, 0x21 },
+ { 3200000, 0x22 },
+ { 4000000, 0x23 },
+ { 4096000, 0x24 },
+ { 4800000, 0x25 },
+ { 5644800, 0x26 },
+ { 6000000, 0x27 },
+ { 6144000, 0x28 },
+ { 6250000, 0x29 },
+ { 6400000, 0x2A },
+ { 6500000, 0x2B },
+ { 6750000, 0x2C },
+ { 7526400, 0x2D },
+ { 8000000, 0x2E },
+ { 8192000, 0x2F },
+ { 9600000, 0x30 },
+ { 11289600, 0x31 },
+ { 12000000, 0x32 },
+ { 12288000, 0x33 },
+ { 12500000, 0x34 },
+ { 12800000, 0x35 },
+ { 13000000, 0x36 },
+ { 13500000, 0x37 },
+ { 19200000, 0x38 },
+ { 22579200, 0x39 },
+ { 24000000, 0x3A },
+ { 24576000, 0x3B },
+ { 25000000, 0x3C },
+ { 25600000, 0x3D },
+ { 26000000, 0x3E },
+ { 27000000, 0x3F },
+};
+
+struct cs35l41_fs_mon_config {
+ int freq;
+ unsigned int fs1;
+ unsigned int fs2;
+};
+
+static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
+ { 32768, 2254, 3754 },
+ { 8000, 9220, 15364 },
+ { 11025, 6148, 10244 },
+ { 12000, 6148, 10244 },
+ { 16000, 4612, 7684 },
+ { 22050, 3076, 5124 },
+ { 24000, 3076, 5124 },
+ { 32000, 2308, 3844 },
+ { 44100, 1540, 2564 },
+ { 48000, 1540, 2564 },
+ { 88200, 772, 1284 },
+ { 96000, 772, 1284 },
+ { 128000, 580, 964 },
+ { 176400, 388, 644 },
+ { 192000, 388, 644 },
+ { 256000, 292, 484 },
+ { 352800, 196, 324 },
+ { 384000, 196, 324 },
+ { 512000, 148, 244 },
+ { 705600, 100, 164 },
+ { 750000, 100, 164 },
+ { 768000, 100, 164 },
+ { 1000000, 76, 124 },
+ { 1024000, 76, 124 },
+ { 1200000, 64, 104 },
+ { 1411200, 52, 84 },
+ { 1500000, 52, 84 },
+ { 1536000, 52, 84 },
+ { 2000000, 40, 64 },
+ { 2048000, 40, 64 },
+ { 2400000, 34, 54 },
+ { 2822400, 28, 44 },
+ { 3000000, 28, 44 },
+ { 3072000, 28, 44 },
+ { 3200000, 27, 42 },
+ { 4000000, 22, 34 },
+ { 4096000, 22, 34 },
+ { 4800000, 19, 29 },
+ { 5644800, 16, 24 },
+ { 6000000, 16, 24 },
+ { 6144000, 16, 24 },
+};
+
+static const unsigned char cs35l41_bst_k1_table[4][5] = {
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 }
+};
+
+static const unsigned char cs35l41_bst_k2_table[4][5] = {
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA }
+};
+
+static const unsigned char cs35l41_bst_slope_table[4] = {
+ 0x75, 0x6B, 0x3B, 0x28
+};
+
+static int cs35l41_get_fs_mon_config_index(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) {
+ if (cs35l41_fs_mon[i].freq == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static const DECLARE_TLV_DB_RANGE(dig_vol_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200));
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+
+static const struct snd_kcontrol_new dre_ctrl =
+ SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
+
+static const char * const cs35l41_pcm_sftramp_text[] = {
+ "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
+ CS35L41_AMP_DIG_VOL_CTRL, 0,
+ cs35l41_pcm_sftramp_text);
+
+static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"};
+static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32};
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum,
+ CS35L41_DAC_PCM1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_pcm_source_texts,
+ cs35l41_pcm_source_values);
+
+static const struct snd_kcontrol_new pcm_source_mux =
+ SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum);
+
+static const char * const cs35l41_tx_input_texts[] = {
+ "Zero", "ASPRX1", "ASPRX2", "VMON", "IMON",
+ "VPMON", "VBSTMON", "DSPTX1", "DSPTX2"
+};
+
+static const unsigned int cs35l41_tx_input_values[] = {
+ 0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2,
+ CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, CS35L41_INPUT_SRC_VPMON,
+ CS35L41_INPUT_SRC_VBSTMON, CS35L41_INPUT_DSP_TX1, CS35L41_INPUT_DSP_TX2
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum,
+ CS35L41_ASP_TX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx1_mux =
+ SOC_DAPM_ENUM("ASPTX1 SRC", cs35l41_asptx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum,
+ CS35L41_ASP_TX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx2_mux =
+ SOC_DAPM_ENUM("ASPTX2 SRC", cs35l41_asptx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum,
+ CS35L41_ASP_TX3_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx3_mux =
+ SOC_DAPM_ENUM("ASPTX3 SRC", cs35l41_asptx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum,
+ CS35L41_ASP_TX4_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx4_mux =
+ SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum);
+
+static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL,
+ 3, 0x4CF, 0x391, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0,
+ amp_gain_tlv),
+ SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+ SOC_SINGLE("HW Noise Gate Enable", CS35L41_NG_CFG, 8, 63, 0),
+ SOC_SINGLE("HW Noise Gate Delay", CS35L41_NG_CFG, 4, 7, 0),
+ SOC_SINGLE("HW Noise Gate Threshold", CS35L41_NG_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Enable",
+ CS35L41_MIXER_NGATE_CH1_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Entry Delay",
+ CS35L41_MIXER_NGATE_CH1_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Threshold",
+ CS35L41_MIXER_NGATE_CH1_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Entry Delay",
+ CS35L41_MIXER_NGATE_CH2_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Enable",
+ CS35L41_MIXER_NGATE_CH2_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Threshold",
+ CS35L41_MIXER_NGATE_CH2_CFG, 0, 7, 0),
+ SOC_SINGLE("SCLK Force", CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("LRCLK Force", CS35L41_SP_FORMAT, CS35L41_LRCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("Invert Class D", CS35L41_AMP_DIG_VOL_CTRL,
+ CS35L41_AMP_INV_PCM_SHIFT, 1, 0),
+ SOC_SINGLE("Amp Gain ZC", CS35L41_AMP_GAIN_CTRL,
+ CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0),
+};
+
+static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
+ if (cs35l41_otp_map_map[i].id == otp_id)
+ return &cs35l41_otp_map_map[i];
+ }
+
+ return NULL;
+}
+
+static int cs35l41_otp_unpack(void *data)
+{
+ const struct cs35l41_otp_map_element_t *otp_map_match;
+ const struct cs35l41_otp_packed_element_t *otp_map;
+ struct cs35l41_private *cs35l41 = data;
+ int bit_offset, word_offset, ret, i;
+ unsigned int orig_spi_freq;
+ unsigned int bit_sum = 8;
+ u32 otp_val, otp_id_reg;
+ u32 *otp_mem;
+
+ otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL);
+ if (!otp_mem)
+ return -ENOMEM;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Read OTP ID failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map_match = cs35l41_find_otp_map(otp_id_reg);
+
+ if (!otp_map_match) {
+ dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n",
+ otp_id_reg);
+ ret = -EINVAL;
+ goto err_otp_unpack;
+ }
+
+ if (cs35l41->otp_setup)
+ cs35l41->otp_setup(cs35l41, true, &orig_spi_freq);
+
+ ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem,
+ CS35L41_OTP_SIZE_WORDS);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Read OTP Mem failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ if (cs35l41->otp_setup)
+ cs35l41->otp_setup(cs35l41, false, &orig_spi_freq);
+
+ otp_map = otp_map_match->map;
+
+ bit_offset = otp_map_match->bit_offset;
+ word_offset = otp_map_match->word_offset;
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write Unlock key failed 1/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write Unlock key failed 2/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ for (i = 0; i < otp_map_match->num_elements; i++) {
+ dev_dbg(cs35l41->dev,
+ "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n",
+ bit_offset, word_offset, bit_sum % 32);
+ if (bit_offset + otp_map[i].size - 1 >= 32) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(31, bit_offset)) >>
+ bit_offset;
+ otp_val |= (otp_mem[++word_offset] &
+ GENMASK(bit_offset +
+ otp_map[i].size - 33, 0)) <<
+ (32 - bit_offset);
+ bit_offset += otp_map[i].size - 32;
+ } else {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 1,
+ bit_offset)) >> bit_offset;
+ bit_offset += otp_map[i].size;
+ }
+ bit_sum += otp_map[i].size;
+
+ if (bit_offset == 32) {
+ bit_offset = 0;
+ word_offset++;
+ }
+
+ if (otp_map[i].reg != 0) {
+ ret = regmap_update_bits(cs35l41->regmap,
+ otp_map[i].reg,
+ GENMASK(otp_map[i].shift +
+ otp_map[i].size - 1,
+ otp_map[i].shift),
+ otp_val << otp_map[i].shift);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write OTP val failed: %d\n",
+ ret);
+ goto err_otp_unpack;
+ }
+ }
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write Lock key failed 1/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write Lock key failed 2/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = 0;
+
+err_otp_unpack:
+ kfree(otp_mem);
+ return ret;
+}
+
+static irqreturn_t cs35l41_irq(int irq, void *data)
+{
+ struct cs35l41_private *cs35l41 = data;
+ unsigned int status[4] = { 0, 0, 0, 0 };
+ unsigned int masks[4] = { 0, 0, 0, 0 };
+ int ret = IRQ_NONE;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(status); i++) {
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE),
+ &status[i]);
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_MASK1 + (i * CS35L41_REGSTRIDE),
+ &masks[i]);
+ }
+
+ /* Check to see if unmasked bits are active */
+ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+ !(status[2] & ~masks[2]) && !(status[3] & ~masks[3]))
+ return IRQ_NONE;
+
+ if (status[3] & CS35L41_OTP_BOOT_DONE) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
+ CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE);
+ }
+
+ /*
+ * The following interrupts require a
+ * protection release cycle to get the
+ * speaker out of Safe-Mode.
+ */
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static const struct reg_sequence cs35l41_pup_patch[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00002084, 0x002F1AA0 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_pdn_patch[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00002084, 0x002F1AA3 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static int cs35l41_main_amp_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 cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pup_patch,
+ ARRAY_SIZE(cs35l41_pup_patch));
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
+ CS35L41_GLOBAL_EN_MASK,
+ 1 << CS35L41_GLOBAL_EN_SHIFT);
+
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
+ CS35L41_GLOBAL_EN_MASK, 0);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+ val, val & CS35L41_PDN_DONE_MASK,
+ 1000, 100000);
+ if (ret)
+ dev_warn(cs35l41->dev, "PDN failed: %d\n", ret);
+
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+ CS35L41_PDN_DONE_MASK);
+
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pdn_patch,
+ ARRAY_SIZE(cs35l41_pdn_patch));
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L41_SP_ENABLES, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 0, CS35L41_SP_ENABLES, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L41_PWR_CTRL2, 9, 0),
+ SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, CS35L41_PWR_CTRL2, 10, 0),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0,
+ cs35l41_main_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+ SND_SOC_DAPM_INPUT("TEMP"),
+
+ SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux),
+ SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux),
+ SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl),
+};
+
+static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
+ {"ASP TX1 Source", "VMON", "VMON ADC"},
+ {"ASP TX1 Source", "IMON", "IMON ADC"},
+ {"ASP TX1 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX1 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX1 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX2 Source", "VMON", "VMON ADC"},
+ {"ASP TX2 Source", "IMON", "IMON ADC"},
+ {"ASP TX2 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX2 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX2 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX3 Source", "VMON", "VMON ADC"},
+ {"ASP TX3 Source", "IMON", "IMON ADC"},
+ {"ASP TX3 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX3 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX3 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX4 Source", "VMON", "VMON ADC"},
+ {"ASP TX4 Source", "IMON", "IMON ADC"},
+ {"ASP TX4 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX4 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX4 Source", "ASPRX2", "ASPRX2" },
+ {"ASPTX1", NULL, "ASP TX1 Source"},
+ {"ASPTX2", NULL, "ASP TX2 Source"},
+ {"ASPTX3", NULL, "ASP TX3 Source"},
+ {"ASPTX4", NULL, "ASP TX4 Source"},
+ {"AMP Capture", NULL, "ASPTX1"},
+ {"AMP Capture", NULL, "ASPTX2"},
+ {"AMP Capture", NULL, "ASPTX3"},
+ {"AMP Capture", NULL, "ASPTX4"},
+
+ {"VMON ADC", NULL, "VSENSE"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VPMON ADC", NULL, "VP"},
+ {"TEMPMON ADC", NULL, "TEMP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+
+ {"ASPRX1", NULL, "AMP Playback"},
+ {"ASPRX2", NULL, "AMP Playback"},
+ {"DRE", "Switch", "CLASS H"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"Main AMP", NULL, "DRE"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"PCM Source", "ASP", "ASPRX1"},
+ {"CLASS H", NULL, "PCM Source"},
+};
+
+static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num,
+ unsigned int *tx_slot, unsigned int rx_num,
+ unsigned int *rx_slot)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int val, mask;
+ int i;
+
+ if (tx_num > 4 || rx_num > 2)
+ return -EINVAL;
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < rx_num; i++) {
+ dev_dbg(cs35l41->dev, "rx slot %d position = %d\n", i, rx_slot[i]);
+ val |= rx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_RX_SLOT, mask, val);
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < tx_num; i++) {
+ dev_dbg(cs35l41->dev, "tx slot %d position = %d\n", i, tx_slot[i]);
+ val |= tx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_TX_SLOT, mask, val);
+
+ return 0;
+}
+
+static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int daifmt = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ daifmt |= CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Mixed provider/consumer mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ daifmt |= 2 << CS35L41_ASP_FMT_SHIFT;
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid or unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ daifmt |= CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK | CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid DAI clock INV\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK |
+ CS35L41_ASP_FMT_MASK | CS35L41_LRCLK_INV_MASK |
+ CS35L41_SCLK_INV_MASK, daifmt);
+}
+
+struct cs35l41_global_fs_config {
+ int rate;
+ int fs_cfg;
+};
+
+static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = {
+ { 12000, 0x01 },
+ { 24000, 0x02 },
+ { 48000, 0x03 },
+ { 96000, 0x04 },
+ { 192000, 0x05 },
+ { 11025, 0x09 },
+ { 22050, 0x0A },
+ { 44100, 0x0B },
+ { 88200, 0x0C },
+ { 176400, 0x0D },
+ { 8000, 0x11 },
+ { 16000, 0x12 },
+ { 32000, 0x13 },
+};
+
+static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_wl;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) {
+ if (rate == cs35l41_fs_rates[i].rate)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(cs35l41_fs_rates)) {
+ dev_err(cs35l41->dev, "Unsupported rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ asp_wl = params_width(params);
+
+ if (i < ARRAY_SIZE(cs35l41_fs_rates))
+ regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL,
+ CS35L41_GLOBAL_FS_MASK,
+ cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_RX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_RX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL,
+ CS35L41_ASP_RX_WL_MASK,
+ asp_wl << CS35L41_ASP_RX_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_TX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_TX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL,
+ CS35L41_ASP_TX_WL_MASK,
+ asp_wl << CS35L41_ASP_TX_WL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l41_get_clk_config(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) {
+ if (cs35l41_pll_sysclk[i].freq == freq)
+ return cs35l41_pll_sysclk[i].clk_cfg;
+ }
+
+ return -EINVAL;
+}
+
+static const unsigned int cs35l41_src_rates[] = {
+ 8000, 12000, 11025, 16000, 22050, 24000, 32000,
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
+ .count = ARRAY_SIZE(cs35l41_src_rates),
+ .list = cs35l41_src_rates,
+};
+
+static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (substream->runtime)
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l41_constraints);
+ return 0;
+}
+
+static int cs35l41_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int extclk_cfg, clksrc;
+
+ switch (clk_id) {
+ case CS35L41_CLKID_SCLK:
+ clksrc = CS35L41_PLLSRC_SCLK;
+ break;
+ case CS35L41_CLKID_LRCLK:
+ clksrc = CS35L41_PLLSRC_LRCLK;
+ break;
+ case CS35L41_CLKID_MCLK:
+ clksrc = CS35L41_PLLSRC_MCLK;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid CLK Config\n");
+ return -EINVAL;
+ }
+
+ extclk_cfg = cs35l41_get_clk_config(freq);
+
+ if (extclk_cfg < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config: %d, freq: %u\n",
+ extclk_cfg, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 1 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_REFCLK_FREQ_MASK,
+ extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 0 << CS35L41_PLL_CLK_EN_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_SEL_MASK, clksrc);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 0 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 1 << CS35L41_PLL_CLK_EN_SHIFT);
+
+ return 0;
+}
+
+static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int fs1_val;
+ unsigned int fs2_val;
+ unsigned int val;
+ int fsindex;
+
+ fsindex = cs35l41_get_fs_mon_config_index(freq);
+ if (fsindex < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(cs35l41->dev, "Set DAI sysclk %d\n", freq);
+
+ if (freq <= 6144000) {
+ /* Use the lookup table */
+ fs1_val = cs35l41_fs_mon[fsindex].fs1;
+ fs2_val = cs35l41_fs_mon[fsindex].fs2;
+ } else {
+ /* Use hard-coded values */
+ fs1_val = 0x10;
+ fs2_val = 0x24;
+ }
+
+ val = fs1_val;
+ val |= (fs2_val << CS35L41_FS2_WINDOW_SHIFT) & CS35L41_FS2_WINDOW_MASK;
+ regmap_write(cs35l41->regmap, CS35L41_TST_FS_MON0, val);
+
+ return 0;
+}
+
+static int cs35l41_boost_config(struct cs35l41_private *cs35l41,
+ int boost_ind, int boost_cap, int boost_ipk)
+{
+ unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
+ struct regmap *regmap = cs35l41->regmap;
+ struct device *dev = cs35l41->dev;
+ int ret;
+
+ switch (boost_ind) {
+ case 1000: /* 1.0 uH */
+ bst_lbst_val = 0;
+ break;
+ case 1200: /* 1.2 uH */
+ bst_lbst_val = 1;
+ break;
+ case 1500: /* 1.5 uH */
+ bst_lbst_val = 2;
+ break;
+ case 2200: /* 2.2 uH */
+ bst_lbst_val = 3;
+ break;
+ default:
+ dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind);
+ return -EINVAL;
+ }
+
+ switch (boost_cap) {
+ case 0 ... 19:
+ bst_cbst_range = 0;
+ break;
+ case 20 ... 50:
+ bst_cbst_range = 1;
+ break;
+ case 51 ... 100:
+ bst_cbst_range = 2;
+ break;
+ case 101 ... 200:
+ bst_cbst_range = 3;
+ break;
+ default: /* 201 uF and greater */
+ bst_cbst_range = 4;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
+ CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK,
+ cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K1_SHIFT |
+ cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K2_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost coefficients: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST,
+ CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK,
+ cs35l41_bst_slope_table[bst_lbst_val]
+ << CS35L41_BST_SLOPE_SHIFT |
+ bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret);
+ return ret;
+ }
+
+ if (boost_ipk < 1600 || boost_ipk > 4500) {
+ dev_err(dev, "Invalid boost inductor peak current: %d mA\n",
+ boost_ipk);
+ return -EINVAL;
+ }
+ bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10;
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR,
+ CS35L41_BST_IPK_MASK,
+ bst_ipk_scaled << CS35L41_BST_IPK_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
+{
+ int ret;
+
+ /* Set Platform Data */
+ /* Required */
+ if (cs35l41->pdata.bst_ipk &&
+ cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) {
+ ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind,
+ cs35l41->pdata.bst_cap,
+ cs35l41->pdata.bst_ipk);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error in Boost DT config: %d\n", ret);
+ return ret;
+ }
+ } else {
+ dev_err(cs35l41->dev, "Incomplete Boost component DT config\n");
+ return -EINVAL;
+ }
+
+ /* Optional */
+ if (cs35l41->pdata.dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK &&
+ cs35l41->pdata.dout_hiz >= 0)
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL,
+ CS35L41_ASP_DOUT_HIZ_MASK,
+ cs35l41->pdata.dout_hiz);
+
+ return 0;
+}
+
+static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
+{
+ struct cs35l41_irq_cfg *irq_gpio_cfg1 = &cs35l41->pdata.irq_config1;
+ struct cs35l41_irq_cfg *irq_gpio_cfg2 = &cs35l41->pdata.irq_config2;
+ int irq_pol = IRQF_TRIGGER_NONE;
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO1_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ irq_gpio_cfg1->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !irq_gpio_cfg1->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO2_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ irq_gpio_cfg1->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !irq_gpio_cfg1->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
+ CS35L41_GPIO1_CTRL_MASK | CS35L41_GPIO2_CTRL_MASK,
+ irq_gpio_cfg1->irq_src_sel << CS35L41_GPIO1_CTRL_SHIFT |
+ irq_gpio_cfg2->irq_src_sel << CS35L41_GPIO2_CTRL_SHIFT);
+
+ if ((irq_gpio_cfg2->irq_src_sel ==
+ (CS35L41_GPIO_CTRL_ACTV_LO | CS35L41_VALID_PDATA)) ||
+ (irq_gpio_cfg2->irq_src_sel ==
+ (CS35L41_GPIO_CTRL_OPEN_INT | CS35L41_VALID_PDATA)))
+ irq_pol = IRQF_TRIGGER_LOW;
+ else if (irq_gpio_cfg2->irq_src_sel ==
+ (CS35L41_GPIO_CTRL_ACTV_HI | CS35L41_VALID_PDATA))
+ irq_pol = IRQF_TRIGGER_HIGH;
+
+ return irq_pol;
+}
+
+static const struct snd_soc_dai_ops cs35l41_ops = {
+ .startup = cs35l41_pcm_startup,
+ .set_fmt = cs35l41_set_dai_fmt,
+ .hw_params = cs35l41_pcm_hw_params,
+ .set_sysclk = cs35l41_dai_set_sysclk,
+ .set_channel_map = cs35l41_set_channel_map,
+};
+
+static struct snd_soc_dai_driver cs35l41_dai[] = {
+ {
+ .name = "cs35l41-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L41_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L41_TX_FORMATS,
+ },
+ .ops = &cs35l41_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
+ .name = "cs35l41-codec",
+
+ .dapm_widgets = cs35l41_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets),
+ .dapm_routes = cs35l41_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map),
+
+ .controls = cs35l41_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l41_aud_controls),
+ .set_sysclk = cs35l41_component_set_sysclk,
+};
+
+static int cs35l41_handle_pdata(struct device *dev,
+ struct cs35l41_platform_data *pdata,
+ struct cs35l41_private *cs35l41)
+{
+ struct cs35l41_irq_cfg *irq_gpio1_config = &pdata->irq_config1;
+ struct cs35l41_irq_cfg *irq_gpio2_config = &pdata->irq_config2;
+ unsigned int val;
+ int ret;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
+ if (ret >= 0)
+ pdata->bst_ipk = val;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-ind-nanohenry", &val);
+ if (ret >= 0)
+ pdata->bst_ind = val;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-cap-microfarad", &val);
+ if (ret >= 0)
+ pdata->bst_cap = val;
+
+ ret = device_property_read_u32(dev, "cirrus,asp-sdout-hiz", &val);
+ if (ret >= 0)
+ pdata->dout_hiz = val;
+ else
+ pdata->dout_hiz = -1;
+
+ /* GPIO1 Pin Config */
+ irq_gpio1_config->irq_pol_inv = device_property_read_bool(dev,
+ "cirrus,gpio1-polarity-invert");
+ irq_gpio1_config->irq_out_en = device_property_read_bool(dev,
+ "cirrus,gpio1-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio1-src-select",
+ &val);
+ if (ret >= 0)
+ irq_gpio1_config->irq_src_sel = val | CS35L41_VALID_PDATA;
+
+ /* GPIO2 Pin Config */
+ irq_gpio2_config->irq_pol_inv = device_property_read_bool(dev,
+ "cirrus,gpio2-polarity-invert");
+ irq_gpio2_config->irq_out_en = device_property_read_bool(dev,
+ "cirrus,gpio2-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio2-src-select",
+ &val);
+ if (ret >= 0)
+ irq_gpio2_config->irq_src_sel = val | CS35L41_VALID_PDATA;
+
+ return 0;
+}
+
+static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
+ { 0x00000040, 0x00005555 },
+ { 0x00000040, 0x0000AAAA },
+ { 0x00003854, 0x05180240 },
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_OTP_TRIM_30, 0x9091A1C8 },
+ { 0x00003014, 0x0200EE0E },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { 0x00000054, 0x00000004 },
+ { CS35L41_IRQ1_DB3, 0x00000000 },
+ { CS35L41_IRQ2_DB3, 0x00000000 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { 0x00000040, 0x0000CCCC },
+ { 0x00000040, 0x00003333 },
+};
+
+static const struct reg_sequence cs35l41_revb0_errata_patch[] = {
+ { 0x00000040, 0x00005555 },
+ { 0x00000040, 0x0000AAAA },
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { 0x00000040, 0x0000CCCC },
+ { 0x00000040, 0x00003333 },
+};
+
+static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
+ { 0x00000040, 0x00005555 },
+ { 0x00000040, 0x0000AAAA },
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { 0x00000040, 0x0000CCCC },
+ { 0x00000040, 0x00003333 },
+};
+
+int cs35l41_probe(struct cs35l41_private *cs35l41,
+ struct cs35l41_platform_data *pdata)
+{
+ u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match;
+ int irq_pol = 0;
+ int ret;
+
+ if (pdata) {
+ cs35l41->pdata = *pdata;
+ } else {
+ ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->pdata, cs35l41);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < CS35L41_NUM_SUPPLIES; i++)
+ cs35l41->supplies[i].supply = cs35l41_supplies[i];
+
+ ret = devm_regulator_bulk_get(cs35l41->dev, CS35L41_NUM_SUPPLIES,
+ cs35l41->supplies);
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l41->reset_gpio)) {
+ ret = PTR_ERR(cs35l41->reset_gpio);
+ cs35l41->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(cs35l41->dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(cs35l41->dev,
+ "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ /* satisfy minimum reset pulse width spec */
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4,
+ int_status, int_status & CS35L41_OTP_BOOT_DONE,
+ 1000, 100000);
+ if (ret) {
+ dev_err(cs35l41->dev,
+ "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
+ goto err;
+ }
+
+ regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+ if (int_status & CS35L41_OTP_BOOT_ERR) {
+ dev_err(cs35l41->dev, "OTP Boot error\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+ /* CS35L41 will have even MTLREVID
+ * CS35L41R will have odd MTLREVID
+ */
+ chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (regid != chipid_match) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n",
+ regid, chipid_match);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ switch (reg_revid) {
+ case CS35L41_REVID_A0:
+ ret = regmap_register_patch(cs35l41->regmap,
+ cs35l41_reva0_errata_patch,
+ ARRAY_SIZE(cs35l41_reva0_errata_patch));
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to apply A0 errata patch: %d\n", ret);
+ goto err;
+ }
+ break;
+ case CS35L41_REVID_B0:
+ ret = regmap_register_patch(cs35l41->regmap,
+ cs35l41_revb0_errata_patch,
+ ARRAY_SIZE(cs35l41_revb0_errata_patch));
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to apply B0 errata patch: %d\n", ret);
+ goto err;
+ }
+ break;
+ case CS35L41_REVID_B2:
+ ret = regmap_register_patch(cs35l41->regmap,
+ cs35l41_revb2_errata_patch,
+ ARRAY_SIZE(cs35l41_revb2_errata_patch));
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to apply B2 errata patch: %d\n", ret);
+ goto err;
+ }
+ break;
+ }
+
+ irq_pol = cs35l41_irq_gpio_config(cs35l41);
+
+ /* Set interrupt masks for critical errors */
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
+ CS35L41_INT1_MASK_DEFAULT);
+
+ ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ "cs35l41", cs35l41);
+
+ /* CS35L41 needs INT for PDN_DONE */
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_otp_unpack(cs35l41);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_CCM_CORE_CTRL, 0);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CCM_CORE_CTRL failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 0);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CS35L41_PWR_CTRL2 failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_update_bits(cs35l41->regmap, CS35L41_AMP_GAIN_CTRL,
+ CS35L41_AMP_GAIN_PCM_MASK, 0);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CS35L41_AMP_GAIN_CTRL failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_set_pdata(cs35l41);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_component(cs35l41->dev,
+ &soc_component_dev_cs35l41,
+ cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Register codec failed: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
+ regid, reg_revid);
+
+ return 0;
+
+err:
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+
+ return ret;
+}
+
+void cs35l41_remove(struct cs35l41_private *cs35l41)
+{
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+}
+
+MODULE_DESCRIPTION("ASoC CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
new file mode 100644
index 000000000000..6cffe8a55beb
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.h
@@ -0,0 +1,775 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * cs35l41.h -- CS35L41 ALSA SoC audio driver
+ *
+ * Copyright 2017-2021 Cirrus Logic, Inc.
+ *
+ * Author: David Rhodes <david.rhodes@cirrus.com>
+ */
+
+#ifndef __CS35L41_H__
+#define __CS35L41_H__
+
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/cs35l41.h>
+
+#define CS35L41_FIRSTREG 0x00000000
+#define CS35L41_LASTREG 0x03804FE8
+#define CS35L41_DEVID 0x00000000
+#define CS35L41_REVID 0x00000004
+#define CS35L41_FABID 0x00000008
+#define CS35L41_RELID 0x0000000C
+#define CS35L41_OTPID 0x00000010
+#define CS35L41_SFT_RESET 0x00000020
+#define CS35L41_TEST_KEY_CTL 0x00000040
+#define CS35L41_USER_KEY_CTL 0x00000044
+#define CS35L41_OTP_MEM0 0x00000400
+#define CS35L41_OTP_MEM31 0x0000047C
+#define CS35L41_OTP_CTRL0 0x00000500
+#define CS35L41_OTP_CTRL1 0x00000504
+#define CS35L41_OTP_CTRL3 0x00000508
+#define CS35L41_OTP_CTRL4 0x0000050C
+#define CS35L41_OTP_CTRL5 0x00000510
+#define CS35L41_OTP_CTRL6 0x00000514
+#define CS35L41_OTP_CTRL7 0x00000518
+#define CS35L41_OTP_CTRL8 0x0000051C
+#define CS35L41_PWR_CTRL1 0x00002014
+#define CS35L41_PWR_CTRL2 0x00002018
+#define CS35L41_PWR_CTRL3 0x0000201C
+#define CS35L41_CTRL_OVRRIDE 0x00002020
+#define CS35L41_AMP_OUT_MUTE 0x00002024
+#define CS35L41_PROTECT_REL_ERR_IGN 0x00002034
+#define CS35L41_GPIO_PAD_CONTROL 0x0000242C
+#define CS35L41_JTAG_CONTROL 0x00002438
+#define CS35L41_PLL_CLK_CTRL 0x00002C04
+#define CS35L41_DSP_CLK_CTRL 0x00002C08
+#define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C
+#define CS35L41_DATA_FS_SEL 0x00002C10
+#define CS35L41_TST_FS_MON0 0x00002D10
+#define CS35L41_MDSYNC_EN 0x00003400
+#define CS35L41_MDSYNC_TX_ID 0x00003408
+#define CS35L41_MDSYNC_PWR_CTRL 0x0000340C
+#define CS35L41_MDSYNC_DATA_TX 0x00003410
+#define CS35L41_MDSYNC_TX_STATUS 0x00003414
+#define CS35L41_MDSYNC_DATA_RX 0x0000341C
+#define CS35L41_MDSYNC_RX_STATUS 0x00003420
+#define CS35L41_MDSYNC_ERR_STATUS 0x00003424
+#define CS35L41_MDSYNC_SYNC_PTE2 0x00003528
+#define CS35L41_MDSYNC_SYNC_PTE3 0x0000352C
+#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C
+#define CS35L41_BSTCVRT_VCTRL1 0x00003800
+#define CS35L41_BSTCVRT_VCTRL2 0x00003804
+#define CS35L41_BSTCVRT_PEAK_CUR 0x00003808
+#define CS35L41_BSTCVRT_SFT_RAMP 0x0000380C
+#define CS35L41_BSTCVRT_COEFF 0x00003810
+#define CS35L41_BSTCVRT_SLOPE_LBST 0x00003814
+#define CS35L41_BSTCVRT_SW_FREQ 0x00003818
+#define CS35L41_BSTCVRT_DCM_CTRL 0x0000381C
+#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820
+#define CS35L41_BSTCVRT_OVERVOLT_CTRL 0x00003830
+#define CS35L41_VI_VOL_POL 0x00004000
+#define CS35L41_VIMON_SPKMON_RESYNC 0x00004100
+#define CS35L41_DTEMP_WARN_THLD 0x00004220
+#define CS35L41_DTEMP_CFG 0x00004224
+#define CS35L41_DTEMP_EN 0x00004308
+#define CS35L41_VPVBST_FS_SEL 0x00004400
+#define CS35L41_SP_ENABLES 0x00004800
+#define CS35L41_SP_RATE_CTRL 0x00004804
+#define CS35L41_SP_FORMAT 0x00004808
+#define CS35L41_SP_HIZ_CTRL 0x0000480C
+#define CS35L41_SP_FRAME_TX_SLOT 0x00004810
+#define CS35L41_SP_FRAME_RX_SLOT 0x00004820
+#define CS35L41_SP_TX_WL 0x00004830
+#define CS35L41_SP_RX_WL 0x00004840
+#define CS35L41_ASP_CONTROL4 0x00004854
+#define CS35L41_DAC_PCM1_SRC 0x00004C00
+#define CS35L41_ASP_TX1_SRC 0x00004C20
+#define CS35L41_ASP_TX2_SRC 0x00004C24
+#define CS35L41_ASP_TX3_SRC 0x00004C28
+#define CS35L41_ASP_TX4_SRC 0x00004C2C
+#define CS35L41_DSP1_RX1_SRC 0x00004C40
+#define CS35L41_DSP1_RX2_SRC 0x00004C44
+#define CS35L41_DSP1_RX3_SRC 0x00004C48
+#define CS35L41_DSP1_RX4_SRC 0x00004C4C
+#define CS35L41_DSP1_RX5_SRC 0x00004C50
+#define CS35L41_DSP1_RX6_SRC 0x00004C54
+#define CS35L41_DSP1_RX7_SRC 0x00004C58
+#define CS35L41_DSP1_RX8_SRC 0x00004C5C
+#define CS35L41_NGATE1_SRC 0x00004C60
+#define CS35L41_NGATE2_SRC 0x00004C64
+#define CS35L41_AMP_DIG_VOL_CTRL 0x00006000
+#define CS35L41_VPBR_CFG 0x00006404
+#define CS35L41_VBBR_CFG 0x00006408
+#define CS35L41_VPBR_STATUS 0x0000640C
+#define CS35L41_VBBR_STATUS 0x00006410
+#define CS35L41_OVERTEMP_CFG 0x00006414
+#define CS35L41_AMP_ERR_VOL 0x00006418
+#define CS35L41_VOL_STATUS_TO_DSP 0x00006450
+#define CS35L41_CLASSH_CFG 0x00006800
+#define CS35L41_WKFET_CFG 0x00006804
+#define CS35L41_NG_CFG 0x00006808
+#define CS35L41_AMP_GAIN_CTRL 0x00006C04
+#define CS35L41_DAC_MSM_CFG 0x00007400
+#define CS35L41_IRQ1_CFG 0x00010000
+#define CS35L41_IRQ1_STATUS 0x00010004
+#define CS35L41_IRQ1_STATUS1 0x00010010
+#define CS35L41_IRQ1_STATUS2 0x00010014
+#define CS35L41_IRQ1_STATUS3 0x00010018
+#define CS35L41_IRQ1_STATUS4 0x0001001C
+#define CS35L41_IRQ1_RAW_STATUS1 0x00010090
+#define CS35L41_IRQ1_RAW_STATUS2 0x00010094
+#define CS35L41_IRQ1_RAW_STATUS3 0x00010098
+#define CS35L41_IRQ1_RAW_STATUS4 0x0001009C
+#define CS35L41_IRQ1_MASK1 0x00010110
+#define CS35L41_IRQ1_MASK2 0x00010114
+#define CS35L41_IRQ1_MASK3 0x00010118
+#define CS35L41_IRQ1_MASK4 0x0001011C
+#define CS35L41_IRQ1_FRC1 0x00010190
+#define CS35L41_IRQ1_FRC2 0x00010194
+#define CS35L41_IRQ1_FRC3 0x00010198
+#define CS35L41_IRQ1_FRC4 0x0001019C
+#define CS35L41_IRQ1_EDGE1 0x00010210
+#define CS35L41_IRQ1_EDGE4 0x0001021C
+#define CS35L41_IRQ1_POL1 0x00010290
+#define CS35L41_IRQ1_POL2 0x00010294
+#define CS35L41_IRQ1_POL3 0x00010298
+#define CS35L41_IRQ1_POL4 0x0001029C
+#define CS35L41_IRQ1_DB3 0x00010318
+#define CS35L41_IRQ2_CFG 0x00010800
+#define CS35L41_IRQ2_STATUS 0x00010804
+#define CS35L41_IRQ2_STATUS1 0x00010810
+#define CS35L41_IRQ2_STATUS2 0x00010814
+#define CS35L41_IRQ2_STATUS3 0x00010818
+#define CS35L41_IRQ2_STATUS4 0x0001081C
+#define CS35L41_IRQ2_RAW_STATUS1 0x00010890
+#define CS35L41_IRQ2_RAW_STATUS2 0x00010894
+#define CS35L41_IRQ2_RAW_STATUS3 0x00010898
+#define CS35L41_IRQ2_RAW_STATUS4 0x0001089C
+#define CS35L41_IRQ2_MASK1 0x00010910
+#define CS35L41_IRQ2_MASK2 0x00010914
+#define CS35L41_IRQ2_MASK3 0x00010918
+#define CS35L41_IRQ2_MASK4 0x0001091C
+#define CS35L41_IRQ2_FRC1 0x00010990
+#define CS35L41_IRQ2_FRC2 0x00010994
+#define CS35L41_IRQ2_FRC3 0x00010998
+#define CS35L41_IRQ2_FRC4 0x0001099C
+#define CS35L41_IRQ2_EDGE1 0x00010A10
+#define CS35L41_IRQ2_EDGE4 0x00010A1C
+#define CS35L41_IRQ2_POL1 0x00010A90
+#define CS35L41_IRQ2_POL2 0x00010A94
+#define CS35L41_IRQ2_POL3 0x00010A98
+#define CS35L41_IRQ2_POL4 0x00010A9C
+#define CS35L41_IRQ2_DB3 0x00010B18
+#define CS35L41_GPIO_STATUS1 0x00011000
+#define CS35L41_GPIO1_CTRL1 0x00011008
+#define CS35L41_GPIO2_CTRL1 0x0001100C
+#define CS35L41_MIXER_NGATE_CFG 0x00012000
+#define CS35L41_MIXER_NGATE_CH1_CFG 0x00012004
+#define CS35L41_MIXER_NGATE_CH2_CFG 0x00012008
+#define CS35L41_DSP_MBOX_1 0x00013000
+#define CS35L41_DSP_MBOX_2 0x00013004
+#define CS35L41_DSP_MBOX_3 0x00013008
+#define CS35L41_DSP_MBOX_4 0x0001300C
+#define CS35L41_DSP_MBOX_5 0x00013010
+#define CS35L41_DSP_MBOX_6 0x00013014
+#define CS35L41_DSP_MBOX_7 0x00013018
+#define CS35L41_DSP_MBOX_8 0x0001301C
+#define CS35L41_DSP_VIRT1_MBOX_1 0x00013020
+#define CS35L41_DSP_VIRT1_MBOX_2 0x00013024
+#define CS35L41_DSP_VIRT1_MBOX_3 0x00013028
+#define CS35L41_DSP_VIRT1_MBOX_4 0x0001302C
+#define CS35L41_DSP_VIRT1_MBOX_5 0x00013030
+#define CS35L41_DSP_VIRT1_MBOX_6 0x00013034
+#define CS35L41_DSP_VIRT1_MBOX_7 0x00013038
+#define CS35L41_DSP_VIRT1_MBOX_8 0x0001303C
+#define CS35L41_DSP_VIRT2_MBOX_1 0x00013040
+#define CS35L41_DSP_VIRT2_MBOX_2 0x00013044
+#define CS35L41_DSP_VIRT2_MBOX_3 0x00013048
+#define CS35L41_DSP_VIRT2_MBOX_4 0x0001304C
+#define CS35L41_DSP_VIRT2_MBOX_5 0x00013050
+#define CS35L41_DSP_VIRT2_MBOX_6 0x00013054
+#define CS35L41_DSP_VIRT2_MBOX_7 0x00013058
+#define CS35L41_DSP_VIRT2_MBOX_8 0x0001305C
+#define CS35L41_CLOCK_DETECT_1 0x00014000
+#define CS35L41_TIMER1_CONTROL 0x00015000
+#define CS35L41_TIMER1_COUNT_PRESET 0x00015004
+#define CS35L41_TIMER1_START_STOP 0x0001500C
+#define CS35L41_TIMER1_STATUS 0x00015010
+#define CS35L41_TIMER1_COUNT_READBACK 0x00015014
+#define CS35L41_TIMER1_DSP_CLK_CFG 0x00015018
+#define CS35L41_TIMER1_DSP_CLK_STATUS 0x0001501C
+#define CS35L41_TIMER2_CONTROL 0x00015100
+#define CS35L41_TIMER2_COUNT_PRESET 0x00015104
+#define CS35L41_TIMER2_START_STOP 0x0001510C
+#define CS35L41_TIMER2_STATUS 0x00015110
+#define CS35L41_TIMER2_COUNT_READBACK 0x00015114
+#define CS35L41_TIMER2_DSP_CLK_CFG 0x00015118
+#define CS35L41_TIMER2_DSP_CLK_STATUS 0x0001511C
+#define CS35L41_DFT_JTAG_CONTROL 0x00016000
+#define CS35L41_DIE_STS1 0x00017040
+#define CS35L41_DIE_STS2 0x00017044
+#define CS35L41_TEMP_CAL1 0x00017048
+#define CS35L41_TEMP_CAL2 0x0001704C
+#define CS35L41_DSP1_XMEM_PACK_0 0x02000000
+#define CS35L41_DSP1_XMEM_PACK_3068 0x02002FF0
+#define CS35L41_DSP1_XMEM_UNPACK32_0 0x02400000
+#define CS35L41_DSP1_XMEM_UNPACK32_2046 0x02401FF8
+#define CS35L41_DSP1_TIMESTAMP_COUNT 0x025C0800
+#define CS35L41_DSP1_SYS_ID 0x025E0000
+#define CS35L41_DSP1_SYS_VERSION 0x025E0004
+#define CS35L41_DSP1_SYS_CORE_ID 0x025E0008
+#define CS35L41_DSP1_SYS_AHB_ADDR 0x025E000C
+#define CS35L41_DSP1_SYS_XSRAM_SIZE 0x025E0010
+#define CS35L41_DSP1_SYS_YSRAM_SIZE 0x025E0018
+#define CS35L41_DSP1_SYS_PSRAM_SIZE 0x025E0020
+#define CS35L41_DSP1_SYS_PM_BOOT_SIZE 0x025E0028
+#define CS35L41_DSP1_SYS_FEATURES 0x025E002C
+#define CS35L41_DSP1_SYS_FIR_FILTERS 0x025E0030
+#define CS35L41_DSP1_SYS_LMS_FILTERS 0x025E0034
+#define CS35L41_DSP1_SYS_XM_BANK_SIZE 0x025E0038
+#define CS35L41_DSP1_SYS_YM_BANK_SIZE 0x025E003C
+#define CS35L41_DSP1_SYS_PM_BANK_SIZE 0x025E0040
+#define CS35L41_DSP1_AHBM_WIN0_CTRL0 0x025E2000
+#define CS35L41_DSP1_AHBM_WIN0_CTRL1 0x025E2004
+#define CS35L41_DSP1_AHBM_WIN1_CTRL0 0x025E2008
+#define CS35L41_DSP1_AHBM_WIN1_CTRL1 0x025E200C
+#define CS35L41_DSP1_AHBM_WIN2_CTRL0 0x025E2010
+#define CS35L41_DSP1_AHBM_WIN2_CTRL1 0x025E2014
+#define CS35L41_DSP1_AHBM_WIN3_CTRL0 0x025E2018
+#define CS35L41_DSP1_AHBM_WIN3_CTRL1 0x025E201C
+#define CS35L41_DSP1_AHBM_WIN4_CTRL0 0x025E2020
+#define CS35L41_DSP1_AHBM_WIN4_CTRL1 0x025E2024
+#define CS35L41_DSP1_AHBM_WIN5_CTRL0 0x025E2028
+#define CS35L41_DSP1_AHBM_WIN5_CTRL1 0x025E202C
+#define CS35L41_DSP1_AHBM_WIN6_CTRL0 0x025E2030
+#define CS35L41_DSP1_AHBM_WIN6_CTRL1 0x025E2034
+#define CS35L41_DSP1_AHBM_WIN7_CTRL0 0x025E2038
+#define CS35L41_DSP1_AHBM_WIN7_CTRL1 0x025E203C
+#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040
+#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044
+#define CS35L41_DSP1_XMEM_UNPACK24_0 0x02800000
+#define CS35L41_DSP1_XMEM_UNPACK24_4093 0x02803FF4
+#define CS35L41_DSP1_CTRL_BASE 0x02B80000
+#define CS35L41_DSP1_CORE_SOFT_RESET 0x02B80010
+#define CS35L41_DSP1_DEBUG 0x02B80040
+#define CS35L41_DSP1_TIMER_CTRL 0x02B80048
+#define CS35L41_DSP1_STREAM_ARB_CTRL 0x02B80050
+#define CS35L41_DSP1_RX1_RATE 0x02B80080
+#define CS35L41_DSP1_RX2_RATE 0x02B80088
+#define CS35L41_DSP1_RX3_RATE 0x02B80090
+#define CS35L41_DSP1_RX4_RATE 0x02B80098
+#define CS35L41_DSP1_RX5_RATE 0x02B800A0
+#define CS35L41_DSP1_RX6_RATE 0x02B800A8
+#define CS35L41_DSP1_RX7_RATE 0x02B800B0
+#define CS35L41_DSP1_RX8_RATE 0x02B800B8
+#define CS35L41_DSP1_TX1_RATE 0x02B80280
+#define CS35L41_DSP1_TX2_RATE 0x02B80288
+#define CS35L41_DSP1_TX3_RATE 0x02B80290
+#define CS35L41_DSP1_TX4_RATE 0x02B80298
+#define CS35L41_DSP1_TX5_RATE 0x02B802A0
+#define CS35L41_DSP1_TX6_RATE 0x02B802A8
+#define CS35L41_DSP1_TX7_RATE 0x02B802B0
+#define CS35L41_DSP1_TX8_RATE 0x02B802B8
+#define CS35L41_DSP1_NMI_CTRL1 0x02B80480
+#define CS35L41_DSP1_NMI_CTRL2 0x02B80488
+#define CS35L41_DSP1_NMI_CTRL3 0x02B80490
+#define CS35L41_DSP1_NMI_CTRL4 0x02B80498
+#define CS35L41_DSP1_NMI_CTRL5 0x02B804A0
+#define CS35L41_DSP1_NMI_CTRL6 0x02B804A8
+#define CS35L41_DSP1_NMI_CTRL7 0x02B804B0
+#define CS35L41_DSP1_NMI_CTRL8 0x02B804B8
+#define CS35L41_DSP1_RESUME_CTRL 0x02B80500
+#define CS35L41_DSP1_IRQ1_CTRL 0x02B80508
+#define CS35L41_DSP1_IRQ2_CTRL 0x02B80510
+#define CS35L41_DSP1_IRQ3_CTRL 0x02B80518
+#define CS35L41_DSP1_IRQ4_CTRL 0x02B80520
+#define CS35L41_DSP1_IRQ5_CTRL 0x02B80528
+#define CS35L41_DSP1_IRQ6_CTRL 0x02B80530
+#define CS35L41_DSP1_IRQ7_CTRL 0x02B80538
+#define CS35L41_DSP1_IRQ8_CTRL 0x02B80540
+#define CS35L41_DSP1_IRQ9_CTRL 0x02B80548
+#define CS35L41_DSP1_IRQ10_CTRL 0x02B80550
+#define CS35L41_DSP1_IRQ11_CTRL 0x02B80558
+#define CS35L41_DSP1_IRQ12_CTRL 0x02B80560
+#define CS35L41_DSP1_IRQ13_CTRL 0x02B80568
+#define CS35L41_DSP1_IRQ14_CTRL 0x02B80570
+#define CS35L41_DSP1_IRQ15_CTRL 0x02B80578
+#define CS35L41_DSP1_IRQ16_CTRL 0x02B80580
+#define CS35L41_DSP1_IRQ17_CTRL 0x02B80588
+#define CS35L41_DSP1_IRQ18_CTRL 0x02B80590
+#define CS35L41_DSP1_IRQ19_CTRL 0x02B80598
+#define CS35L41_DSP1_IRQ20_CTRL 0x02B805A0
+#define CS35L41_DSP1_IRQ21_CTRL 0x02B805A8
+#define CS35L41_DSP1_IRQ22_CTRL 0x02B805B0
+#define CS35L41_DSP1_IRQ23_CTRL 0x02B805B8
+#define CS35L41_DSP1_SCRATCH1 0x02B805C0
+#define CS35L41_DSP1_SCRATCH2 0x02B805C8
+#define CS35L41_DSP1_SCRATCH3 0x02B805D0
+#define CS35L41_DSP1_SCRATCH4 0x02B805D8
+#define CS35L41_DSP1_CCM_CORE_CTRL 0x02BC1000
+#define CS35L41_DSP1_CCM_CLK_OVERRIDE 0x02BC1008
+#define CS35L41_DSP1_XM_MSTR_EN 0x02BC2000
+#define CS35L41_DSP1_XM_CORE_PRI 0x02BC2008
+#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010
+#define CS35L41_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018
+#define CS35L41_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020
+#define CS35L41_DSP1_XM_NPL0_PRI 0x02BC2078
+#define CS35L41_DSP1_YM_MSTR_EN 0x02BC20C0
+#define CS35L41_DSP1_YM_CORE_PRI 0x02BC20C8
+#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0
+#define CS35L41_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8
+#define CS35L41_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0
+#define CS35L41_DSP1_YM_NPL0_PRI 0x02BC2138
+#define CS35L41_DSP1_PM_MSTR_EN 0x02BC2180
+#define CS35L41_DSP1_PM_PATCH0_ADDR 0x02BC2188
+#define CS35L41_DSP1_PM_PATCH0_EN 0x02BC218C
+#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190
+#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194
+#define CS35L41_DSP1_PM_PATCH1_ADDR 0x02BC2198
+#define CS35L41_DSP1_PM_PATCH1_EN 0x02BC219C
+#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0
+#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4
+#define CS35L41_DSP1_PM_PATCH2_ADDR 0x02BC21A8
+#define CS35L41_DSP1_PM_PATCH2_EN 0x02BC21AC
+#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0
+#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4
+#define CS35L41_DSP1_PM_PATCH3_ADDR 0x02BC21B8
+#define CS35L41_DSP1_PM_PATCH3_EN 0x02BC21BC
+#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0
+#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4
+#define CS35L41_DSP1_PM_PATCH4_ADDR 0x02BC21C8
+#define CS35L41_DSP1_PM_PATCH4_EN 0x02BC21CC
+#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0
+#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4
+#define CS35L41_DSP1_PM_PATCH5_ADDR 0x02BC21D8
+#define CS35L41_DSP1_PM_PATCH5_EN 0x02BC21DC
+#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0
+#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4
+#define CS35L41_DSP1_PM_PATCH6_ADDR 0x02BC21E8
+#define CS35L41_DSP1_PM_PATCH6_EN 0x02BC21EC
+#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0
+#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4
+#define CS35L41_DSP1_PM_PATCH7_ADDR 0x02BC21F8
+#define CS35L41_DSP1_PM_PATCH7_EN 0x02BC21FC
+#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200
+#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204
+#define CS35L41_DSP1_MPU_XM_ACCESS0 0x02BC3000
+#define CS35L41_DSP1_MPU_YM_ACCESS0 0x02BC3004
+#define CS35L41_DSP1_MPU_WNDW_ACCESS0 0x02BC3008
+#define CS35L41_DSP1_MPU_XREG_ACCESS0 0x02BC300C
+#define CS35L41_DSP1_MPU_YREG_ACCESS0 0x02BC3014
+#define CS35L41_DSP1_MPU_XM_ACCESS1 0x02BC3018
+#define CS35L41_DSP1_MPU_YM_ACCESS1 0x02BC301C
+#define CS35L41_DSP1_MPU_WNDW_ACCESS1 0x02BC3020
+#define CS35L41_DSP1_MPU_XREG_ACCESS1 0x02BC3024
+#define CS35L41_DSP1_MPU_YREG_ACCESS1 0x02BC302C
+#define CS35L41_DSP1_MPU_XM_ACCESS2 0x02BC3030
+#define CS35L41_DSP1_MPU_YM_ACCESS2 0x02BC3034
+#define CS35L41_DSP1_MPU_WNDW_ACCESS2 0x02BC3038
+#define CS35L41_DSP1_MPU_XREG_ACCESS2 0x02BC303C
+#define CS35L41_DSP1_MPU_YREG_ACCESS2 0x02BC3044
+#define CS35L41_DSP1_MPU_XM_ACCESS3 0x02BC3048
+#define CS35L41_DSP1_MPU_YM_ACCESS3 0x02BC304C
+#define CS35L41_DSP1_MPU_WNDW_ACCESS3 0x02BC3050
+#define CS35L41_DSP1_MPU_XREG_ACCESS3 0x02BC3054
+#define CS35L41_DSP1_MPU_YREG_ACCESS3 0x02BC305C
+#define CS35L41_DSP1_MPU_XM_VIO_ADDR 0x02BC3100
+#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104
+#define CS35L41_DSP1_MPU_YM_VIO_ADDR 0x02BC3108
+#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C
+#define CS35L41_DSP1_MPU_PM_VIO_ADDR 0x02BC3110
+#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114
+#define CS35L41_DSP1_MPU_LOCK_CONFIG 0x02BC3140
+#define CS35L41_DSP1_MPU_WDT_RST_CTRL 0x02BC3180
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078
+#define CS35L41_DSP1_STRMARB_TX0_CFG0 0x02BC5200
+#define CS35L41_DSP1_STRMARB_TX0_CFG1 0x02BC5204
+#define CS35L41_DSP1_STRMARB_TX1_CFG0 0x02BC5208
+#define CS35L41_DSP1_STRMARB_TX1_CFG1 0x02BC520C
+#define CS35L41_DSP1_STRMARB_TX2_CFG0 0x02BC5210
+#define CS35L41_DSP1_STRMARB_TX2_CFG1 0x02BC5214
+#define CS35L41_DSP1_STRMARB_TX3_CFG0 0x02BC5218
+#define CS35L41_DSP1_STRMARB_TX3_CFG1 0x02BC521C
+#define CS35L41_DSP1_STRMARB_TX4_CFG0 0x02BC5220
+#define CS35L41_DSP1_STRMARB_TX4_CFG1 0x02BC5224
+#define CS35L41_DSP1_STRMARB_TX5_CFG0 0x02BC5228
+#define CS35L41_DSP1_STRMARB_TX5_CFG1 0x02BC522C
+#define CS35L41_DSP1_STRMARB_TX6_CFG0 0x02BC5230
+#define CS35L41_DSP1_STRMARB_TX6_CFG1 0x02BC5234
+#define CS35L41_DSP1_STRMARB_TX7_CFG0 0x02BC5238
+#define CS35L41_DSP1_STRMARB_TX7_CFG1 0x02BC523C
+#define CS35L41_DSP1_STRMARB_RX0_CFG0 0x02BC5400
+#define CS35L41_DSP1_STRMARB_RX0_CFG1 0x02BC5404
+#define CS35L41_DSP1_STRMARB_RX1_CFG0 0x02BC5408
+#define CS35L41_DSP1_STRMARB_RX1_CFG1 0x02BC540C
+#define CS35L41_DSP1_STRMARB_RX2_CFG0 0x02BC5410
+#define CS35L41_DSP1_STRMARB_RX2_CFG1 0x02BC5414
+#define CS35L41_DSP1_STRMARB_RX3_CFG0 0x02BC5418
+#define CS35L41_DSP1_STRMARB_RX3_CFG1 0x02BC541C
+#define CS35L41_DSP1_STRMARB_RX4_CFG0 0x02BC5420
+#define CS35L41_DSP1_STRMARB_RX4_CFG1 0x02BC5424
+#define CS35L41_DSP1_STRMARB_RX5_CFG0 0x02BC5428
+#define CS35L41_DSP1_STRMARB_RX5_CFG1 0x02BC542C
+#define CS35L41_DSP1_STRMARB_RX6_CFG0 0x02BC5430
+#define CS35L41_DSP1_STRMARB_RX6_CFG1 0x02BC5434
+#define CS35L41_DSP1_STRMARB_RX7_CFG0 0x02BC5438
+#define CS35L41_DSP1_STRMARB_RX7_CFG1 0x02BC543C
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678
+#define CS35L41_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00
+#define CS35L41_DSP1_STRMARB_ERR_STATUS 0x02BC5A08
+#define CS35L41_DSP1_INTPCTL_RES_STATIC 0x02BC6000
+#define CS35L41_DSP1_INTPCTL_RES_DYN 0x02BC6004
+#define CS35L41_DSP1_INTPCTL_NMI_CTRL 0x02BC6008
+#define CS35L41_DSP1_INTPCTL_IRQ_INV 0x02BC6010
+#define CS35L41_DSP1_INTPCTL_IRQ_MODE 0x02BC6014
+#define CS35L41_DSP1_INTPCTL_IRQ_EN 0x02BC6018
+#define CS35L41_DSP1_INTPCTL_IRQ_MSK 0x02BC601C
+#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020
+#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024
+#define CS35L41_DSP1_INTPCTL_IRQ_FRC 0x02BC6028
+#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C
+#define CS35L41_DSP1_INTPCTL_IRQ_ERR 0x02BC6030
+#define CS35L41_DSP1_INTPCTL_IRQ_PEND 0x02BC6034
+#define CS35L41_DSP1_INTPCTL_IRQ_GEN 0x02BC6038
+#define CS35L41_DSP1_INTPCTL_TESTBITS 0x02BC6040
+#define CS35L41_DSP1_WDT_CONTROL 0x02BC7000
+#define CS35L41_DSP1_WDT_STATUS 0x02BC7008
+#define CS35L41_DSP1_YMEM_PACK_0 0x02C00000
+#define CS35L41_DSP1_YMEM_PACK_1532 0x02C017F0
+#define CS35L41_DSP1_YMEM_UNPACK32_0 0x03000000
+#define CS35L41_DSP1_YMEM_UNPACK32_1022 0x03000FF8
+#define CS35L41_DSP1_YMEM_UNPACK24_0 0x03400000
+#define CS35L41_DSP1_YMEM_UNPACK24_2045 0x03401FF4
+#define CS35L41_DSP1_PMEM_0 0x03800000
+#define CS35L41_DSP1_PMEM_5114 0x03804FE8
+
+/*test regs for emulation bringup*/
+#define CS35L41_PLL_OVR 0x00003018
+#define CS35L41_BST_TEST_DUTY 0x00003900
+#define CS35L41_DIGPWM_IOCTRL 0x0000706C
+
+/*registers populated by OTP*/
+#define CS35L41_OTP_TRIM_1 0x0000208c
+#define CS35L41_OTP_TRIM_2 0x00002090
+#define CS35L41_OTP_TRIM_3 0x00003010
+#define CS35L41_OTP_TRIM_4 0x0000300C
+#define CS35L41_OTP_TRIM_5 0x0000394C
+#define CS35L41_OTP_TRIM_6 0x00003950
+#define CS35L41_OTP_TRIM_7 0x00003954
+#define CS35L41_OTP_TRIM_8 0x00003958
+#define CS35L41_OTP_TRIM_9 0x0000395C
+#define CS35L41_OTP_TRIM_10 0x0000416C
+#define CS35L41_OTP_TRIM_11 0x00004160
+#define CS35L41_OTP_TRIM_12 0x00004170
+#define CS35L41_OTP_TRIM_13 0x00004360
+#define CS35L41_OTP_TRIM_14 0x00004448
+#define CS35L41_OTP_TRIM_15 0x0000444C
+#define CS35L41_OTP_TRIM_16 0x00006E30
+#define CS35L41_OTP_TRIM_17 0x00006E34
+#define CS35L41_OTP_TRIM_18 0x00006E38
+#define CS35L41_OTP_TRIM_19 0x00006E3C
+#define CS35L41_OTP_TRIM_20 0x00006E40
+#define CS35L41_OTP_TRIM_21 0x00006E44
+#define CS35L41_OTP_TRIM_22 0x00006E48
+#define CS35L41_OTP_TRIM_23 0x00006E4C
+#define CS35L41_OTP_TRIM_24 0x00006E50
+#define CS35L41_OTP_TRIM_25 0x00006E54
+#define CS35L41_OTP_TRIM_26 0x00006E58
+#define CS35L41_OTP_TRIM_27 0x00006E5C
+#define CS35L41_OTP_TRIM_28 0x00006E60
+#define CS35L41_OTP_TRIM_29 0x00006E64
+#define CS35L41_OTP_TRIM_30 0x00007418
+#define CS35L41_OTP_TRIM_31 0x0000741C
+#define CS35L41_OTP_TRIM_32 0x00007434
+#define CS35L41_OTP_TRIM_33 0x00007068
+#define CS35L41_OTP_TRIM_34 0x0000410C
+#define CS35L41_OTP_TRIM_35 0x0000400C
+#define CS35L41_OTP_TRIM_36 0x00002030
+
+#define CS35L41_MAX_CACHE_REG 36
+#define CS35L41_OTP_SIZE_WORDS 32
+#define CS35L41_NUM_OTP_ELEM 100
+#define CS35L41_NUM_OTP_MAPS 5
+
+#define CS35L41_VALID_PDATA 0x80000000
+#define CS35L41_NUM_SUPPLIES 2
+
+#define CS35L41_SCLK_MSTR_MASK 0x10
+#define CS35L41_SCLK_MSTR_SHIFT 4
+#define CS35L41_LRCLK_MSTR_MASK 0x01
+#define CS35L41_LRCLK_MSTR_SHIFT 0
+#define CS35L41_SCLK_INV_MASK 0x40
+#define CS35L41_SCLK_INV_SHIFT 6
+#define CS35L41_LRCLK_INV_MASK 0x04
+#define CS35L41_LRCLK_INV_SHIFT 2
+#define CS35L41_SCLK_FRC_MASK 0x20
+#define CS35L41_SCLK_FRC_SHIFT 5
+#define CS35L41_LRCLK_FRC_MASK 0x02
+#define CS35L41_LRCLK_FRC_SHIFT 1
+
+#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0
+#define CS35L41_AMP_GAIN_ZC_MASK 0x0400
+#define CS35L41_AMP_GAIN_ZC_SHIFT 10
+
+#define CS35L41_BST_CTL_MASK 0xFF
+#define CS35L41_BST_CTL_SEL_MASK 0x03
+#define CS35L41_BST_CTL_SEL_REG 0x00
+#define CS35L41_BST_CTL_SEL_CLASSH 0x01
+#define CS35L41_BST_IPK_MASK 0x7F
+#define CS35L41_BST_IPK_SHIFT 0
+#define CS35L41_BST_LIM_MASK 0x4
+#define CS35L41_BST_LIM_SHIFT 2
+#define CS35L41_BST_K1_MASK 0x000000FF
+#define CS35L41_BST_K1_SHIFT 0
+#define CS35L41_BST_K2_MASK 0x0000FF00
+#define CS35L41_BST_K2_SHIFT 8
+#define CS35L41_BST_SLOPE_MASK 0x0000FF00
+#define CS35L41_BST_SLOPE_SHIFT 8
+#define CS35L41_BST_LBST_VAL_MASK 0x00000003
+#define CS35L41_BST_LBST_VAL_SHIFT 0
+
+#define CS35L41_TEMP_THLD_MASK 0x03
+#define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF
+#define CS35L41_PDM_MODE_MASK 0x01
+#define CS35L41_PDM_MODE_SHIFT 0
+
+#define CS35L41_CH_MEM_DEPTH_MASK 0x07
+#define CS35L41_CH_MEM_DEPTH_SHIFT 0
+#define CS35L41_CH_HDRM_CTL_MASK 0x007F0000
+#define CS35L41_CH_HDRM_CTL_SHIFT 16
+#define CS35L41_CH_REL_RATE_MASK 0xFF00
+#define CS35L41_CH_REL_RATE_SHIFT 8
+#define CS35L41_CH_WKFET_DLY_MASK 0x001C
+#define CS35L41_CH_WKFET_DLY_SHIFT 2
+#define CS35L41_CH_WKFET_THLD_MASK 0x0F00
+#define CS35L41_CH_WKFET_THLD_SHIFT 8
+
+#define CS35L41_HW_NG_SEL_MASK 0x3F00
+#define CS35L41_HW_NG_SEL_SHIFT 8
+#define CS35L41_HW_NG_DLY_MASK 0x0070
+#define CS35L41_HW_NG_DLY_SHIFT 4
+#define CS35L41_HW_NG_THLD_MASK 0x0007
+#define CS35L41_HW_NG_THLD_SHIFT 0
+
+#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000
+#define CS35L41_DSP_NG_ENABLE_SHIFT 16
+#define CS35L41_DSP_NG_THLD_MASK 0x7
+#define CS35L41_DSP_NG_THLD_SHIFT 0
+#define CS35L41_DSP_NG_DELAY_MASK 0x0F00
+#define CS35L41_DSP_NG_DELAY_SHIFT 8
+
+#define CS35L41_ASP_FMT_MASK 0x0700
+#define CS35L41_ASP_FMT_SHIFT 8
+#define CS35L41_ASP_DOUT_HIZ_MASK 0x03
+#define CS35L41_ASP_DOUT_HIZ_SHIFT 0
+#define CS35L41_ASP_WIDTH_16 0x10
+#define CS35L41_ASP_WIDTH_24 0x18
+#define CS35L41_ASP_WIDTH_32 0x20
+#define CS35L41_ASP_WIDTH_TX_MASK 0xFF0000
+#define CS35L41_ASP_WIDTH_TX_SHIFT 16
+#define CS35L41_ASP_WIDTH_RX_MASK 0xFF000000
+#define CS35L41_ASP_WIDTH_RX_SHIFT 24
+#define CS35L41_ASP_RX1_SLOT_MASK 0x3F
+#define CS35L41_ASP_RX1_SLOT_SHIFT 0
+#define CS35L41_ASP_RX2_SLOT_MASK 0x3F00
+#define CS35L41_ASP_RX2_SLOT_SHIFT 8
+#define CS35L41_ASP_RX_WL_MASK 0x3F
+#define CS35L41_ASP_TX_WL_MASK 0x3F
+#define CS35L41_ASP_RX_WL_SHIFT 0
+#define CS35L41_ASP_TX_WL_SHIFT 0
+#define CS35L41_ASP_SOURCE_MASK 0x7F
+
+#define CS35L41_INPUT_SRC_ASPRX1 0x08
+#define CS35L41_INPUT_SRC_ASPRX2 0x09
+#define CS35L41_INPUT_SRC_VMON 0x18
+#define CS35L41_INPUT_SRC_IMON 0x19
+#define CS35L41_INPUT_SRC_CLASSH 0x21
+#define CS35L41_INPUT_SRC_VPMON 0x28
+#define CS35L41_INPUT_SRC_VBSTMON 0x29
+#define CS35L41_INPUT_SRC_TEMPMON 0x3A
+#define CS35L41_INPUT_SRC_RSVD 0x3B
+#define CS35L41_INPUT_DSP_TX1 0x32
+#define CS35L41_INPUT_DSP_TX2 0x33
+
+#define CS35L41_PLL_CLK_SEL_MASK 0x07
+#define CS35L41_PLL_CLK_SEL_SHIFT 0
+#define CS35L41_PLL_CLK_EN_MASK 0x10
+#define CS35L41_PLL_CLK_EN_SHIFT 4
+#define CS35L41_PLL_OPENLOOP_MASK 0x0800
+#define CS35L41_PLL_OPENLOOP_SHIFT 11
+#define CS35L41_PLLSRC_SCLK 0
+#define CS35L41_PLLSRC_LRCLK 1
+#define CS35L41_PLLSRC_SELF 3
+#define CS35L41_PLLSRC_PDMCLK 4
+#define CS35L41_PLLSRC_MCLK 5
+#define CS35L41_PLLSRC_SWIRE 7
+#define CS35L41_REFCLK_FREQ_MASK 0x7E0
+#define CS35L41_REFCLK_FREQ_SHIFT 5
+
+#define CS35L41_GLOBAL_FS_MASK 0x1F
+#define CS35L41_GLOBAL_FS_SHIFT 0
+
+#define CS35L41_GLOBAL_EN_MASK 0x01
+#define CS35L41_GLOBAL_EN_SHIFT 0
+#define CS35L41_BST_EN_MASK 0x0030
+#define CS35L41_BST_EN_SHIFT 4
+#define CS35L41_BST_EN_DEFAULT 0x2
+#define CS35L41_AMP_EN_SHIFT 0
+#define CS35L41_AMP_EN_MASK 1
+
+#define CS35L41_PDN_DONE_MASK 0x00800000
+#define CS35L41_PDN_DONE_SHIFT 23
+#define CS35L41_PUP_DONE_MASK 0x01000000
+#define CS35L41_PUP_DONE_SHIFT 24
+
+#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F
+#define CS35L36_PUP_DONE_IRQ_MASK 0xBF
+
+#define CS35L41_AMP_SHORT_ERR 0x80000000
+#define CS35L41_BST_SHORT_ERR 0x0100
+#define CS35L41_TEMP_WARN 0x8000
+#define CS35L41_TEMP_ERR 0x00020000
+#define CS35L41_BST_OVP_ERR 0x40
+#define CS35L41_BST_DCM_UVP_ERR 0x80
+#define CS35L41_OTP_BOOT_DONE 0x02
+#define CS35L41_PLL_UNLOCK 0x10
+#define CS35L41_OTP_BOOT_ERR 0x80000000
+
+#define CS35L41_AMP_SHORT_ERR_RLS 0x02
+#define CS35L41_BST_SHORT_ERR_RLS 0x04
+#define CS35L41_BST_OVP_ERR_RLS 0x08
+#define CS35L41_BST_UVP_ERR_RLS 0x10
+#define CS35L41_TEMP_WARN_ERR_RLS 0x20
+#define CS35L41_TEMP_ERR_RLS 0x40
+
+#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F
+#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF
+#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF
+
+#define CS35L41_GPIO_DIR_MASK 0x80000000
+#define CS35L41_GPIO_DIR_SHIFT 31
+#define CS35L41_GPIO1_CTRL_MASK 0x00030000
+#define CS35L41_GPIO1_CTRL_SHIFT 16
+#define CS35L41_GPIO2_CTRL_MASK 0x07000000
+#define CS35L41_GPIO2_CTRL_SHIFT 24
+#define CS35L41_GPIO_CTRL_OPEN_INT 2
+#define CS35L41_GPIO_CTRL_ACTV_LO 4
+#define CS35L41_GPIO_CTRL_ACTV_HI 5
+#define CS35L41_GPIO_POL_MASK 0x1000
+#define CS35L41_GPIO_POL_SHIFT 12
+
+#define CS35L41_AMP_INV_PCM_SHIFT 14
+#define CS35L41_AMP_INV_PCM_MASK BIT(CS35L41_AMP_INV_PCM_SHIFT)
+#define CS35L41_AMP_PCM_VOL_SHIFT 3
+#define CS35L41_AMP_PCM_VOL_MASK (0x7FF << 3)
+#define CS35L41_AMP_PCM_VOL_MUTE 0x4CF
+
+#define CS35L41_CHIP_ID 0x35a40
+#define CS35L41R_CHIP_ID 0x35b40
+#define CS35L41_MTLREVID_MASK 0x0F
+#define CS35L41_REVID_A0 0xA0
+#define CS35L41_REVID_B0 0xB0
+#define CS35L41_REVID_B2 0xB2
+
+#define CS35L41_HALO_CORE_RESET 0x00000200
+
+#define CS35L41_FS1_WINDOW_MASK 0x000007FF
+#define CS35L41_FS2_WINDOW_MASK 0x00FFF800
+#define CS35L41_FS2_WINDOW_SHIFT 12
+
+#define CS35L41_SPI_MAX_FREQ_OTP 4000000
+
+#define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+bool cs35l41_readable_reg(struct device *dev, unsigned int reg);
+bool cs35l41_precious_reg(struct device *dev, unsigned int reg);
+bool cs35l41_volatile_reg(struct device *dev, unsigned int reg);
+
+struct cs35l41_otp_packed_element_t {
+ u32 reg;
+ u8 shift;
+ u8 size;
+};
+
+struct cs35l41_otp_map_element_t {
+ u32 id;
+ u32 num_elements;
+ const struct cs35l41_otp_packed_element_t *map;
+ u32 bit_offset;
+ u32 word_offset;
+};
+
+extern const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG];
+extern const struct cs35l41_otp_map_element_t
+ cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS];
+
+#define CS35L41_REGSTRIDE 4
+
+struct cs35l41_private {
+ struct snd_soc_codec *codec;
+ struct cs35l41_platform_data pdata;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES];
+ int irq;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ void (*otp_setup)(struct cs35l41_private *cs35l41, bool is_pre_setup,
+ unsigned int *freq);
+};
+
+int cs35l41_probe(struct cs35l41_private *cs35l41,
+ struct cs35l41_platform_data *pdata);
+void cs35l41_remove(struct cs35l41_private *cs35l41);
+
+#endif /*__CS35L41_H__*/
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 9a463ab54bdd..27a1c4c73074 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -25,7 +25,6 @@
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -41,7 +40,6 @@
static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_FRZ_CTL, 0x00 },
{ CS42L42_SRC_CTL, 0x10 },
- { CS42L42_MCLK_STATUS, 0x02 },
{ CS42L42_MCLK_CTL, 0x02 },
{ CS42L42_SFTRAMP_RATE, 0xA4 },
{ CS42L42_I2C_DEBOUNCE, 0x88 },
@@ -53,15 +51,12 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_RSENSE_CTL1, 0x40 },
{ CS42L42_RSENSE_CTL2, 0x00 },
{ CS42L42_OSC_SWITCH, 0x00 },
- { CS42L42_OSC_SWITCH_STATUS, 0x05 },
{ CS42L42_RSENSE_CTL3, 0x1B },
{ CS42L42_TSENSE_CTL, 0x1B },
{ CS42L42_TSRS_INT_DISABLE, 0x00 },
- { CS42L42_TRSENSE_STATUS, 0x00 },
{ CS42L42_HSDET_CTL1, 0x77 },
{ CS42L42_HSDET_CTL2, 0x00 },
{ CS42L42_HS_SWITCH_CTL, 0xF3 },
- { CS42L42_HS_DET_STATUS, 0x00 },
{ CS42L42_HS_CLAMP_DISABLE, 0x00 },
{ CS42L42_MCLK_SRC_SEL, 0x00 },
{ CS42L42_SPDIF_CLK_CFG, 0x00 },
@@ -75,25 +70,13 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_IN_ASRC_CLK, 0x00 },
{ CS42L42_OUT_ASRC_CLK, 0x00 },
{ CS42L42_PLL_DIV_CFG1, 0x00 },
- { CS42L42_ADC_OVFL_STATUS, 0x00 },
- { CS42L42_MIXER_STATUS, 0x00 },
- { CS42L42_SRC_STATUS, 0x00 },
- { CS42L42_ASP_RX_STATUS, 0x00 },
- { CS42L42_ASP_TX_STATUS, 0x00 },
- { CS42L42_CODEC_STATUS, 0x00 },
- { CS42L42_DET_INT_STATUS1, 0x00 },
- { CS42L42_DET_INT_STATUS2, 0x00 },
- { CS42L42_SRCPL_INT_STATUS, 0x00 },
- { CS42L42_VPMON_STATUS, 0x00 },
- { CS42L42_PLL_LOCK_STATUS, 0x00 },
- { CS42L42_TSRS_PLUG_STATUS, 0x00 },
{ CS42L42_ADC_OVFL_INT_MASK, 0x01 },
{ CS42L42_MIXER_INT_MASK, 0x0F },
{ CS42L42_SRC_INT_MASK, 0x0F },
{ CS42L42_ASP_RX_INT_MASK, 0x1F },
{ CS42L42_ASP_TX_INT_MASK, 0x0F },
{ CS42L42_CODEC_INT_MASK, 0x03 },
- { CS42L42_SRCPL_INT_MASK, 0xFF },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
{ CS42L42_VPMON_INT_MASK, 0x01 },
{ CS42L42_PLL_LOCK_INT_MASK, 0x01 },
{ CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
@@ -105,8 +88,6 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_PLL_CTL3, 0x10 },
{ CS42L42_PLL_CAL_RATIO, 0x80 },
{ CS42L42_PLL_CTL4, 0x03 },
- { CS42L42_LOAD_DET_RCSTAT, 0x00 },
- { CS42L42_LOAD_DET_DONE, 0x00 },
{ CS42L42_LOAD_DET_EN, 0x00 },
{ CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
{ CS42L42_WAKE_CTL, 0xC0 },
@@ -115,8 +96,6 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_MISC_DET_CTL, 0x03 },
{ CS42L42_MIC_DET_CTL1, 0x1F },
{ CS42L42_MIC_DET_CTL2, 0x2F },
- { CS42L42_DET_STATUS1, 0x00 },
- { CS42L42_DET_STATUS2, 0x00 },
{ CS42L42_DET_INT1_MASK, 0xE0 },
{ CS42L42_DET_INT2_MASK, 0xFF },
{ CS42L42_HS_BIAS_CTL, 0xC2 },
@@ -130,7 +109,7 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_MIXER_CHA_VOL, 0x3F },
{ CS42L42_MIXER_ADC_VOL, 0x3F },
{ CS42L42_MIXER_CHB_VOL, 0x3F },
- { CS42L42_EQ_COEF_IN0, 0x22 },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
{ CS42L42_EQ_COEF_IN1, 0x00 },
{ CS42L42_EQ_COEF_IN2, 0x00 },
{ CS42L42_EQ_COEF_IN3, 0x00 },
@@ -182,7 +161,6 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
{ CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
{ CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
- { CS42L42_SUB_REVID, 0x03 },
};
static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
@@ -351,6 +329,7 @@ static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
case CS42L42_DEVID_CD:
case CS42L42_DEVID_E:
case CS42L42_MCLK_STATUS:
+ case CS42L42_OSC_SWITCH_STATUS:
case CS42L42_TRSENSE_STATUS:
case CS42L42_HS_DET_STATUS:
case CS42L42_ADC_OVFL_STATUS:
@@ -455,10 +434,36 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
0x3f, 1, mixer_tlv)
};
+static int cs42l42_hp_adc_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 cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs42l42->hp_adc_up_pending = true;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Only need one delay if HP and ADC are both powering-up */
+ if (cs42l42->hp_adc_up_pending) {
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US,
+ CS42L42_HP_ADC_EN_TIME_US + 1000);
+ cs42l42->hp_adc_up_pending = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Playback Path */
SND_SOC_DAPM_OUTPUT("HP"),
- SND_SOC_DAPM_DAC("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, SND_SOC_NOPM, 0, 0),
@@ -468,7 +473,8 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Capture Path */
SND_SOC_DAPM_INPUT("HS"),
- SND_SOC_DAPM_ADC("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0),
@@ -517,26 +523,10 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_
cs42l42->jack = jk;
- regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK,
- CS42L42_RS_PLUG_MASK | CS42L42_RS_UNPLUG_MASK |
- CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK,
- (1 << CS42L42_RS_PLUG_SHIFT) | (1 << CS42L42_RS_UNPLUG_SHIFT) |
- (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT));
-
- return 0;
-}
-
-static int cs42l42_component_probe(struct snd_soc_component *component)
-{
- struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
-
- cs42l42->component = component;
-
return 0;
}
static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
- .probe = cs42l42_component_probe,
.set_jack = cs42l42_set_jack,
.dapm_widgets = cs42l42_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
@@ -569,7 +559,6 @@ static const struct reg_sequence cs42l42_to_osc_seq[] = {
struct cs42l42_pll_params {
u32 sclk;
- u8 mclk_div;
u8 mclk_src_sel;
u8 sclk_prediv;
u8 pll_div_int;
@@ -586,24 +575,24 @@ struct cs42l42_pll_params {
* Table 4-5 from the Datasheet
*/
static const struct cs42l42_pll_params pll_ratio_table[] = {
- { 1411200, 0, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2},
- { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2},
- { 2304000, 0, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2},
- { 2400000, 0, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
- { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
- { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
- { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
- { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1},
- { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1},
- { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
- { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
- { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
- { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0, 1},
- { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0, 1},
- { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0, 1},
- { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0, 1},
- { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0, 1},
- { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0, 1}
+ { 1411200, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2},
+ { 1536000, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2},
+ { 2304000, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2},
+ { 2400000, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 2822400, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 3000000, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1},
+ { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1},
+ { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1},
+ { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1},
+ { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
};
static int cs42l42_pll_config(struct snd_soc_component *component)
@@ -618,6 +607,14 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
else
clk = cs42l42->sclk;
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use) {
+ if (pll_ratio_table[cs42l42->pll_config].sclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
if (pll_ratio_table[i].sclk == clk) {
cs42l42->pll_config = i;
@@ -631,10 +628,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
24000000)) <<
CS42L42_INTERNAL_FS_SHIFT);
- snd_soc_component_update_bits(component, CS42L42_MCLK_SRC_SEL,
- CS42L42_MCLKDIV_MASK,
- (pll_ratio_table[i].mclk_div <<
- CS42L42_MCLKDIV_SHIFT));
/* Set up the LRCLK */
fsync = clk / cs42l42->srate;
if (((fsync * cs42l42->srate) != clk)
@@ -668,22 +661,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
CS42L42_FSYNC_PULSE_WIDTH_MASK,
CS42L42_FRAC1_VAL(fsync - 1) <<
CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
- /* Set the sample rates (96k or lower) */
- snd_soc_component_update_bits(component, CS42L42_FS_RATE_EN,
- CS42L42_FS_EN_MASK,
- (CS42L42_FS_EN_IASRC_96K |
- CS42L42_FS_EN_OASRC_96K) <<
- CS42L42_FS_EN_SHIFT);
- /* Set the input/output internal MCLK clock ~12 MHz */
- snd_soc_component_update_bits(component, CS42L42_IN_ASRC_CLK,
- CS42L42_CLK_IASRC_SEL_MASK,
- CS42L42_CLK_IASRC_SEL_12 <<
- CS42L42_CLK_IASRC_SEL_SHIFT);
- snd_soc_component_update_bits(component,
- CS42L42_OUT_ASRC_CLK,
- CS42L42_CLK_OASRC_SEL_MASK,
- CS42L42_CLK_OASRC_SEL_12 <<
- CS42L42_CLK_OASRC_SEL_SHIFT);
if (pll_ratio_table[i].mclk_src_sel == 0) {
/* Pass the clock straight through */
snd_soc_component_update_bits(component,
@@ -746,6 +723,39 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
return -EINVAL;
}
+static void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int fs;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use)
+ return;
+
+ /* SRC MCLK must be as close as possible to 125 * sample rate */
+ if (sample_rate <= 48000)
+ fs = CS42L42_CLK_IASRC_SEL_6;
+ else
+ fs = CS42L42_CLK_IASRC_SEL_12;
+
+ /* Set the sample rates (96k or lower) */
+ snd_soc_component_update_bits(component,
+ CS42L42_FS_RATE_EN,
+ CS42L42_FS_EN_MASK,
+ (CS42L42_FS_EN_IASRC_96K |
+ CS42L42_FS_EN_OASRC_96K) <<
+ CS42L42_FS_EN_SHIFT);
+
+ snd_soc_component_update_bits(component,
+ CS42L42_IN_ASRC_CLK,
+ CS42L42_CLK_IASRC_SEL_MASK,
+ fs << CS42L42_CLK_IASRC_SEL_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_OUT_ASRC_CLK,
+ CS42L42_CLK_OASRC_SEL_MASK,
+ fs << CS42L42_CLK_OASRC_SEL_SHIFT);
+}
+
static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
@@ -824,7 +834,7 @@ static int cs42l42_dai_startup(struct snd_pcm_substream *substream, struct snd_s
/* Machine driver has not set a SCLK, limit bottom end to 44.1 kHz */
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- 44100, 192000);
+ 44100, 96000);
}
static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -836,6 +846,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int channels = params_channels(params);
unsigned int width = (params_width(params) / 8) - 1;
unsigned int val = 0;
+ int ret;
cs42l42->srate = params_rate(params);
cs42l42->bclk = snd_soc_params_to_bclk(params);
@@ -851,13 +862,12 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
if (params_width(params) == 24)
cs42l42->bclk = (cs42l42->bclk / 3) * 4;
- switch(substream->stream) {
+ switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
- if (channels == 2) {
- val |= CS42L42_ASP_TX_CH2_AP_MASK;
- val |= width << CS42L42_ASP_TX_CH2_RES_SHIFT;
- }
- val |= width << CS42L42_ASP_TX_CH1_RES_SHIFT;
+ /* channel 2 on high LRCLK */
+ val = CS42L42_ASP_TX_CH2_AP_MASK |
+ (width << CS42L42_ASP_TX_CH2_RES_SHIFT) |
+ (width << CS42L42_ASP_TX_CH1_RES_SHIFT);
snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES,
CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK |
@@ -890,7 +900,13 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- return cs42l42_pll_config(component);
+ ret = cs42l42_pll_config(component);
+ if (ret)
+ return ret;
+
+ cs42l42_src_config(component, params_rate(params));
+
+ return 0;
}
static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
@@ -934,7 +950,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
CS42L42_HP_ANA_BMUTE_MASK);
cs42l42->stream_use &= ~(1 << stream);
- if(!cs42l42->stream_use) {
+ if (!cs42l42->stream_use) {
/*
* Switch to the internal oscillator.
* SCLK must remain running until after this clock switch.
@@ -1005,7 +1021,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE )
+ SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops cs42l42_ops = {
.startup = cs42l42_dai_startup,
@@ -1021,14 +1037,14 @@ static struct snd_soc_dai_driver cs42l42_dai = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L42_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L42_FORMATS,
},
.symmetric_rate = 1,
@@ -1036,11 +1052,121 @@ static struct snd_soc_dai_driver cs42l42_dai = {
.ops = &cs42l42_ops,
};
+static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+
+ /* Set hs detect to manual, active mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ case CS42L42_HSDET_COMP_TYPE3:
+ cs42l42->hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ default:
+ cs42l42->hs_type = CS42L42_PLUG_INVALID;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE4;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+}
+
static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
{
unsigned int hs_det_status;
unsigned int int_status;
+ /* Read and save the hs detection result */
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
/* Mask the auto detect interrupt */
regmap_update_bits(cs42l42->regmap,
CS42L42_CODEC_INT_MASK,
@@ -1049,6 +1175,10 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
(1 << CS42L42_PDN_DONE_SHIFT) |
(1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
+ CS42L42_HSDET_TYPE_SHIFT;
+
/* Set hs detect to automatic, disabled mode */
regmap_update_bits(cs42l42->regmap,
CS42L42_HSDET_CTL2,
@@ -1061,11 +1191,15 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
(0 << CS42L42_HSBIAS_REF_SHIFT) |
(3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- /* Read and save the hs detection result */
- regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
-
- cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
- CS42L42_HSDET_TYPE_SHIFT;
+ /* Run Manual detection if auto detect has not found a headset.
+ * We Re-Run with Manual Detection if the original detection was invalid or headphones,
+ * to ensure that a headset mic is detected in all cases.
+ */
+ if (cs42l42->hs_type == CS42L42_PLUG_INVALID ||
+ cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) {
+ dev_dbg(cs42l42->dev, "Running Manual Detection Fallback\n");
+ cs42l42_manual_hs_type_detect(cs42l42);
+ }
/* Set up button detection */
if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
@@ -1362,19 +1496,19 @@ static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
switch (bias_level) {
case 1: /* Function C button press */
bias_level = SND_JACK_BTN_2;
- dev_dbg(cs42l42->component->dev, "Function C button press\n");
+ dev_dbg(cs42l42->dev, "Function C button press\n");
break;
case 2: /* Function B button press */
bias_level = SND_JACK_BTN_1;
- dev_dbg(cs42l42->component->dev, "Function B button press\n");
+ dev_dbg(cs42l42->dev, "Function B button press\n");
break;
case 3: /* Function D button press */
bias_level = SND_JACK_BTN_3;
- dev_dbg(cs42l42->component->dev, "Function D button press\n");
+ dev_dbg(cs42l42->dev, "Function D button press\n");
break;
case 4: /* Function A button press */
bias_level = SND_JACK_BTN_0;
- dev_dbg(cs42l42->component->dev, "Function A button press\n");
+ dev_dbg(cs42l42->dev, "Function A button press\n");
break;
default:
bias_level = 0;
@@ -1448,7 +1582,6 @@ static const struct cs42l42_irq_params irq_params_table[] = {
static irqreturn_t cs42l42_irq_thread(int irq, void *data)
{
struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
- struct snd_soc_component *component = cs42l42->component;
unsigned int stickies[12];
unsigned int masks[12];
unsigned int current_plug_status;
@@ -1482,7 +1615,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
if ((~masks[5]) & irq_params_table[5].mask) {
if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
cs42l42_process_hs_type_detect(cs42l42);
- switch(cs42l42->hs_type){
+ switch (cs42l42->hs_type) {
case CS42L42_PLUG_CTIA:
case CS42L42_PLUG_OMTP:
snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET,
@@ -1495,7 +1628,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
default:
break;
}
- dev_dbg(component->dev, "Auto detect done (%d)\n", cs42l42->hs_type);
+ dev_dbg(cs42l42->dev, "Auto detect done (%d)\n", cs42l42->hs_type);
}
}
@@ -1514,7 +1647,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
cs42l42->plug_state = CS42L42_TS_UNPLUG;
cs42l42_cancel_hs_type_detect(cs42l42);
- switch(cs42l42->hs_type){
+ switch (cs42l42->hs_type) {
case CS42L42_PLUG_CTIA:
case CS42L42_PLUG_OMTP:
snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET);
@@ -1529,7 +1662,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3);
- dev_dbg(component->dev, "Unplug event\n");
+ dev_dbg(cs42l42->dev, "Unplug event\n");
}
break;
@@ -1545,7 +1678,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
CS42L42_M_HSBIAS_HIZ_MASK)) {
if (current_button_status & CS42L42_M_DETECT_TF_MASK) {
- dev_dbg(component->dev, "Button released\n");
+ dev_dbg(cs42l42->dev, "Button released\n");
report = 0;
} else if (current_button_status & CS42L42_M_DETECT_FT_MASK) {
report = cs42l42_handle_button_press(cs42l42);
@@ -1658,8 +1791,8 @@ static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
CS42L42_TS_UNPLUG_MASK,
(1 << CS42L42_RS_PLUG_SHIFT) |
(1 << CS42L42_RS_UNPLUG_SHIFT) |
- (1 << CS42L42_TS_PLUG_SHIFT) |
- (1 << CS42L42_TS_UNPLUG_SHIFT));
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
}
static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
@@ -1685,12 +1818,15 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
(1 << CS42L42_HS_CLAMP_DISABLE_SHIFT));
/* Enable the tip sense circuit */
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_INV_MASK, CS42L42_TS_INV_MASK);
+
regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL,
CS42L42_TIP_SENSE_CTRL_MASK |
CS42L42_TIP_SENSE_INV_MASK |
CS42L42_TIP_SENSE_DEBOUNCE_MASK,
(3 << CS42L42_TIP_SENSE_CTRL_SHIFT) |
- (0 << CS42L42_TIP_SENSE_INV_SHIFT) |
+ (!cs42l42->ts_inv << CS42L42_TIP_SENSE_INV_SHIFT) |
(2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT));
/* Save the initial status of the tip sense */
@@ -1734,10 +1870,6 @@ static int cs42l42_handle_device_data(struct device *dev,
cs42l42->ts_inv = CS42L42_TS_INV_DIS;
}
- regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
- CS42L42_TS_INV_MASK,
- (cs42l42->ts_inv << CS42L42_TS_INV_SHIFT));
-
ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val);
if (!ret) {
switch (val) {
@@ -1899,6 +2031,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
if (!cs42l42)
return -ENOMEM;
+ cs42l42->dev = &i2c_client->dev;
i2c_set_clientdata(i2c_client, cs42l42);
cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
@@ -1933,7 +2066,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(cs42l42->reset_gpio)) {
ret = PTR_ERR(cs42l42->reset_gpio);
- goto err_disable;
+ goto err_disable_noreset;
}
if (cs42l42->reset_gpio) {
@@ -1942,16 +2075,20 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
}
usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
- /* Request IRQ */
- ret = devm_request_threaded_irq(&i2c_client->dev,
- i2c_client->irq,
- NULL, cs42l42_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs42l42", cs42l42);
-
- if (ret != 0)
- dev_err(&i2c_client->dev,
- "Failed to request IRQ: %d\n", ret);
+ /* Request IRQ if one was specified */
+ if (i2c_client->irq) {
+ ret = request_threaded_irq(i2c_client->irq,
+ NULL, cs42l42_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs42l42", cs42l42);
+ if (ret == -EPROBE_DEFER) {
+ goto err_disable_noirq;
+ } else if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request IRQ: %d\n", ret);
+ goto err_disable_noirq;
+ }
+ }
/* initialize codec */
devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB);
@@ -1972,7 +2109,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- goto err_disable;
+ goto err_shutdown;
}
dev_info(&i2c_client->dev,
@@ -1997,7 +2134,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
ret = cs42l42_handle_device_data(&i2c_client->dev, cs42l42);
if (ret != 0)
- goto err_disable;
+ goto err_shutdown;
/* Setup headset detection */
cs42l42_setup_hs_type_detect(cs42l42);
@@ -2009,10 +2146,22 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
ret = devm_snd_soc_register_component(&i2c_client->dev,
&soc_component_dev_cs42l42, &cs42l42_dai, 1);
if (ret < 0)
- goto err_disable;
+ goto err_shutdown;
+
return 0;
+err_shutdown:
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+
err_disable:
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l42);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+err_disable_noreset:
regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
return ret;
@@ -2022,59 +2171,22 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
{
struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
- devm_free_irq(&i2c_client->dev, i2c_client->irq, cs42l42);
- pm_runtime_suspend(&i2c_client->dev);
- pm_runtime_disable(&i2c_client->dev);
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l42);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int cs42l42_runtime_suspend(struct device *dev)
-{
- struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
-
- regcache_cache_only(cs42l42->regmap, true);
- regcache_mark_dirty(cs42l42->regmap);
+ /*
+ * The driver might not have control of reset and power supplies,
+ * so ensure that the chip internals are powered down.
+ */
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
- /* Hold down reset */
gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
-
- /* remove power */
- regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
- cs42l42->supplies);
-
- return 0;
-}
-
-static int cs42l42_runtime_resume(struct device *dev)
-{
- struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
- int ret;
-
- /* Enable power */
- ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
- cs42l42->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to enable supplies: %d\n",
- ret);
- return ret;
- }
-
- gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
- usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
-
- regcache_cache_only(cs42l42->regmap, false);
- regcache_sync(cs42l42->regmap);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
return 0;
}
-#endif
-
-static const struct dev_pm_ops cs42l42_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs42l42_runtime_suspend, cs42l42_runtime_resume,
- NULL)
-};
#ifdef CONFIG_OF
static const struct of_device_id cs42l42_of_match[] = {
@@ -2102,7 +2214,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l42_id);
static struct i2c_driver cs42l42_i2c_driver = {
.driver = {
.name = "cs42l42",
- .pm = &cs42l42_runtime_pm,
.of_match_table = of_match_ptr(cs42l42_of_match),
.acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
},
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 8734f6828f3e..f45bcc9a3a62 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -188,6 +188,11 @@
#define CS42L42_HSDET_COMP2_LVL_SHIFT 4
#define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT)
+#define CS42L42_HSDET_COMP1_LVL_VAL 12 /* 1.25V Comparator */
+#define CS42L42_HSDET_COMP2_LVL_VAL 2 /* 1.75V Comparator */
+#define CS42L42_HSDET_COMP1_LVL_DEFAULT 7 /* 1V Comparator */
+#define CS42L42_HSDET_COMP2_LVL_DEFAULT 7 /* 2V Comparator */
+
#define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20)
#define CS42L42_HSDET_AUTO_TIME_SHIFT 0
#define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)
@@ -228,6 +233,60 @@
#define CS42L42_PLUG_HEADPHONE 2
#define CS42L42_PLUG_INVALID 3
+#define CS42L42_HSDET_SW_COMP1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (0 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS3_SHIFT))
+#define CS42L42_HSDET_SW_COMP2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (0 << CS42L42_SW_REF_HS3_SHIFT))
+#define CS42L42_HSDET_SW_TYPE1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (0 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS3_SHIFT))
+#define CS42L42_HSDET_SW_TYPE2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (0 << CS42L42_SW_REF_HS3_SHIFT))
+#define CS42L42_HSDET_SW_TYPE3 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS3_SHIFT))
+#define CS42L42_HSDET_SW_TYPE4 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
+ (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
+ (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
+ (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
+ (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
+ (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
+ (0 << CS42L42_SW_REF_HS4_SHIFT) | \
+ (1 << CS42L42_SW_REF_HS3_SHIFT))
+
+#define CS42L42_HSDET_COMP_TYPE1 1
+#define CS42L42_HSDET_COMP_TYPE2 2
+#define CS42L42_HSDET_COMP_TYPE3 0
+#define CS42L42_HSDET_COMP_TYPE4 3
+
#define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29)
#define CS42L42_HS_CLAMP_DISABLE_SHIFT 0
#define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)
@@ -288,6 +347,7 @@
#define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A)
#define CS42L42_CLK_IASRC_SEL_SHIFT 0
#define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT)
+#define CS42L42_CLK_IASRC_SEL_6 0
#define CS42L42_CLK_IASRC_SEL_12 1
#define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B)
@@ -761,6 +821,7 @@
#define CS42L42_CLOCK_SWITCH_DELAY_US 150
#define CS42L42_PLL_LOCK_POLL_US 250
#define CS42L42_PLL_LOCK_TIMEOUT_US 1250
+#define CS42L42_HP_ADC_EN_TIME_US 20000
static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
"VA",
@@ -772,7 +833,7 @@ static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
struct cs42l42_private {
struct regmap *regmap;
- struct snd_soc_component *component;
+ struct device *dev;
struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
struct gpio_desc *reset_gpio;
struct completion pdn_done;
@@ -794,6 +855,7 @@ struct cs42l42_private {
u8 hs_bias_ramp_time;
u8 hs_bias_sense_en;
u8 stream_use;
+ bool hp_adc_up_pending;
};
#endif /* __CS42L42_H__ */
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
index 1ee83160b83f..391fd7da331f 100644
--- a/sound/soc/codecs/cs47l15.c
+++ b/sound/soc/codecs/cs47l15.c
@@ -45,7 +45,7 @@ struct cs47l15 {
bool in1_lp_mode;
};
-static const struct wm_adsp_region cs47l15_dsp1_regions[] = {
+static const struct cs_dsp_region cs47l15_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
@@ -1402,18 +1402,18 @@ static int cs47l15_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
cs47l15->core.adsp[0].part = "cs47l15";
- cs47l15->core.adsp[0].num = 1;
- cs47l15->core.adsp[0].type = WMFW_ADSP2;
- cs47l15->core.adsp[0].rev = 2;
- cs47l15->core.adsp[0].dev = madera->dev;
- cs47l15->core.adsp[0].regmap = madera->regmap_32bit;
-
- cs47l15->core.adsp[0].base = MADERA_DSP1_CONFIG_1;
- cs47l15->core.adsp[0].mem = cs47l15_dsp1_regions;
- cs47l15->core.adsp[0].num_mems = ARRAY_SIZE(cs47l15_dsp1_regions);
-
- cs47l15->core.adsp[0].lock_regions =
- WM_ADSP2_REGION_1 | WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3;
+ cs47l15->core.adsp[0].cs_dsp.num = 1;
+ cs47l15->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l15->core.adsp[0].cs_dsp.rev = 2;
+ cs47l15->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l15->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l15->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l15->core.adsp[0].cs_dsp.mem = cs47l15_dsp1_regions;
+ cs47l15->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l15_dsp1_regions);
+
+ cs47l15->core.adsp[0].cs_dsp.lock_regions =
+ CS_ADSP2_REGION_1 | CS_ADSP2_REGION_2 | CS_ADSP2_REGION_3;
ret = wm_adsp2_init(&cs47l15->core.adsp[0]);
if (ret != 0)
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 6b6d08816024..6356f81aafc5 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -37,21 +37,21 @@ struct cs47l24_priv {
struct arizona_fll fll[2];
};
-static const struct wm_adsp_region cs47l24_dsp2_regions[] = {
+static const struct cs_dsp_region cs47l24_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x280000 },
{ .type = WMFW_ADSP2_XM, .base = 0x290000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
};
-static const struct wm_adsp_region cs47l24_dsp3_regions[] = {
+static const struct cs_dsp_region cs47l24_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x380000 },
{ .type = WMFW_ADSP2_XM, .base = 0x390000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
};
-static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l24_dsp_regions[] = {
cs47l24_dsp2_regions,
cs47l24_dsp3_regions,
};
@@ -1234,15 +1234,15 @@ static int cs47l24_probe(struct platform_device *pdev)
for (i = 1; i <= 2; i++) {
cs47l24->core.adsp[i].part = "cs47l24";
- cs47l24->core.adsp[i].num = i + 1;
- cs47l24->core.adsp[i].type = WMFW_ADSP2;
- cs47l24->core.adsp[i].dev = arizona->dev;
- cs47l24->core.adsp[i].regmap = arizona->regmap;
+ cs47l24->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l24->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l24->core.adsp[i].cs_dsp.dev = arizona->dev;
+ cs47l24->core.adsp[i].cs_dsp.regmap = arizona->regmap;
- cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 +
+ cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 +
(0x100 * i);
- cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1];
- cs47l24->core.adsp[i].num_mems =
+ cs47l24->core.adsp[i].cs_dsp.mem = cs47l24_dsp_regions[i - 1];
+ cs47l24->core.adsp[i].cs_dsp.num_mems =
ARRAY_SIZE(cs47l24_dsp2_regions);
ret = wm_adsp2_init(&cs47l24->core.adsp[i]);
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
index 3f04a2a74521..db2f844b8b17 100644
--- a/sound/soc/codecs/cs47l35.c
+++ b/sound/soc/codecs/cs47l35.c
@@ -37,28 +37,28 @@ struct cs47l35 {
struct madera_fll fll;
};
-static const struct wm_adsp_region cs47l35_dsp1_regions[] = {
+static const struct cs_dsp_region cs47l35_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
};
-static const struct wm_adsp_region cs47l35_dsp2_regions[] = {
+static const struct cs_dsp_region cs47l35_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x160000 },
{ .type = WMFW_ADSP2_XM, .base = 0x120000 },
{ .type = WMFW_ADSP2_YM, .base = 0x140000 },
};
-static const struct wm_adsp_region cs47l35_dsp3_regions[] = {
+static const struct cs_dsp_region cs47l35_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x180000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
};
-static const struct wm_adsp_region *cs47l35_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l35_dsp_regions[] = {
cs47l35_dsp1_regions,
cs47l35_dsp2_regions,
cs47l35_dsp3_regions,
@@ -1686,15 +1686,15 @@ static int cs47l35_probe(struct platform_device *pdev)
for (i = 0; i < CS47L35_NUM_ADSP; i++) {
cs47l35->core.adsp[i].part = "cs47l35";
- cs47l35->core.adsp[i].num = i + 1;
- cs47l35->core.adsp[i].type = WMFW_ADSP2;
- cs47l35->core.adsp[i].rev = 1;
- cs47l35->core.adsp[i].dev = madera->dev;
- cs47l35->core.adsp[i].regmap = madera->regmap_32bit;
-
- cs47l35->core.adsp[i].base = wm_adsp2_control_bases[i];
- cs47l35->core.adsp[i].mem = cs47l35_dsp_regions[i];
- cs47l35->core.adsp[i].num_mems =
+ cs47l35->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l35->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l35->core.adsp[i].cs_dsp.rev = 1;
+ cs47l35->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l35->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l35->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l35->core.adsp[i].cs_dsp.mem = cs47l35_dsp_regions[i];
+ cs47l35->core.adsp[i].cs_dsp.num_mems =
ARRAY_SIZE(cs47l35_dsp1_regions);
ret = wm_adsp2_init(&cs47l35->core.adsp[i]);
diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c
index 748a180870bc..d4fedc5ad516 100644
--- a/sound/soc/codecs/cs47l85.c
+++ b/sound/soc/codecs/cs47l85.c
@@ -37,56 +37,56 @@ struct cs47l85 {
struct madera_fll fll[3];
};
-static const struct wm_adsp_region cs47l85_dsp1_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
};
-static const struct wm_adsp_region cs47l85_dsp2_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x160000 },
{ .type = WMFW_ADSP2_XM, .base = 0x120000 },
{ .type = WMFW_ADSP2_YM, .base = 0x140000 },
};
-static const struct wm_adsp_region cs47l85_dsp3_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x180000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
};
-static const struct wm_adsp_region cs47l85_dsp4_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp4_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x260000 },
{ .type = WMFW_ADSP2_XM, .base = 0x220000 },
{ .type = WMFW_ADSP2_YM, .base = 0x240000 },
};
-static const struct wm_adsp_region cs47l85_dsp5_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp5_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x280000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
};
-static const struct wm_adsp_region cs47l85_dsp6_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp6_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x360000 },
{ .type = WMFW_ADSP2_XM, .base = 0x320000 },
{ .type = WMFW_ADSP2_YM, .base = 0x340000 },
};
-static const struct wm_adsp_region cs47l85_dsp7_regions[] = {
+static const struct cs_dsp_region cs47l85_dsp7_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x380000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
};
-static const struct wm_adsp_region *cs47l85_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l85_dsp_regions[] = {
cs47l85_dsp1_regions,
cs47l85_dsp2_regions,
cs47l85_dsp3_regions,
@@ -2632,15 +2632,15 @@ static int cs47l85_probe(struct platform_device *pdev)
for (i = 0; i < CS47L85_NUM_ADSP; i++) {
cs47l85->core.adsp[i].part = "cs47l85";
- cs47l85->core.adsp[i].num = i + 1;
- cs47l85->core.adsp[i].type = WMFW_ADSP2;
- cs47l85->core.adsp[i].rev = 1;
- cs47l85->core.adsp[i].dev = madera->dev;
- cs47l85->core.adsp[i].regmap = madera->regmap_32bit;
-
- cs47l85->core.adsp[i].base = wm_adsp2_control_bases[i];
- cs47l85->core.adsp[i].mem = cs47l85_dsp_regions[i];
- cs47l85->core.adsp[i].num_mems =
+ cs47l85->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l85->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l85->core.adsp[i].cs_dsp.rev = 1;
+ cs47l85->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l85->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l85->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l85->core.adsp[i].cs_dsp.mem = cs47l85_dsp_regions[i];
+ cs47l85->core.adsp[i].cs_dsp.num_mems =
ARRAY_SIZE(cs47l85_dsp1_regions);
ret = wm_adsp2_init(&cs47l85->core.adsp[i]);
diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c
index d2911c014b86..5aec937a2462 100644
--- a/sound/soc/codecs/cs47l90.c
+++ b/sound/soc/codecs/cs47l90.c
@@ -37,56 +37,56 @@ struct cs47l90 {
struct madera_fll fll[3];
};
-static const struct wm_adsp_region cs47l90_dsp1_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
};
-static const struct wm_adsp_region cs47l90_dsp2_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x160000 },
{ .type = WMFW_ADSP2_XM, .base = 0x120000 },
{ .type = WMFW_ADSP2_YM, .base = 0x140000 },
};
-static const struct wm_adsp_region cs47l90_dsp3_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x180000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
};
-static const struct wm_adsp_region cs47l90_dsp4_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp4_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x260000 },
{ .type = WMFW_ADSP2_XM, .base = 0x220000 },
{ .type = WMFW_ADSP2_YM, .base = 0x240000 },
};
-static const struct wm_adsp_region cs47l90_dsp5_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp5_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x280000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
};
-static const struct wm_adsp_region cs47l90_dsp6_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp6_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x360000 },
{ .type = WMFW_ADSP2_XM, .base = 0x320000 },
{ .type = WMFW_ADSP2_YM, .base = 0x340000 },
};
-static const struct wm_adsp_region cs47l90_dsp7_regions[] = {
+static const struct cs_dsp_region cs47l90_dsp7_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x380000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
};
-static const struct wm_adsp_region *cs47l90_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l90_dsp_regions[] = {
cs47l90_dsp1_regions,
cs47l90_dsp2_regions,
cs47l90_dsp3_regions,
@@ -2543,18 +2543,18 @@ static int cs47l90_probe(struct platform_device *pdev)
for (i = 0; i < CS47L90_NUM_ADSP; i++) {
cs47l90->core.adsp[i].part = "cs47l90";
- cs47l90->core.adsp[i].num = i + 1;
- cs47l90->core.adsp[i].type = WMFW_ADSP2;
- cs47l90->core.adsp[i].rev = 2;
- cs47l90->core.adsp[i].dev = madera->dev;
- cs47l90->core.adsp[i].regmap = madera->regmap_32bit;
-
- cs47l90->core.adsp[i].base = cs47l90_dsp_control_bases[i];
- cs47l90->core.adsp[i].mem = cs47l90_dsp_regions[i];
- cs47l90->core.adsp[i].num_mems =
+ cs47l90->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l90->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l90->core.adsp[i].cs_dsp.rev = 2;
+ cs47l90->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l90->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l90->core.adsp[i].cs_dsp.base = cs47l90_dsp_control_bases[i];
+ cs47l90->core.adsp[i].cs_dsp.mem = cs47l90_dsp_regions[i];
+ cs47l90->core.adsp[i].cs_dsp.num_mems =
ARRAY_SIZE(cs47l90_dsp1_regions);
- cs47l90->core.adsp[i].lock_regions = WM_ADSP2_REGION_1_9;
+ cs47l90->core.adsp[i].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
ret = wm_adsp2_init(&cs47l90->core.adsp[i]);
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
index 1a0280416d92..a1b8dcdb9f7b 100644
--- a/sound/soc/codecs/cs47l92.c
+++ b/sound/soc/codecs/cs47l92.c
@@ -37,7 +37,7 @@ struct cs47l92 {
struct madera_fll fll[2];
};
-static const struct wm_adsp_region cs47l92_dsp1_regions[] = {
+static const struct cs_dsp_region cs47l92_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
@@ -2002,17 +2002,17 @@ static int cs47l92_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
cs47l92->core.adsp[0].part = "cs47l92";
- cs47l92->core.adsp[0].num = 1;
- cs47l92->core.adsp[0].type = WMFW_ADSP2;
- cs47l92->core.adsp[0].rev = 2;
- cs47l92->core.adsp[0].dev = madera->dev;
- cs47l92->core.adsp[0].regmap = madera->regmap_32bit;
+ cs47l92->core.adsp[0].cs_dsp.num = 1;
+ cs47l92->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l92->core.adsp[0].cs_dsp.rev = 2;
+ cs47l92->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l92->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
- cs47l92->core.adsp[0].base = MADERA_DSP1_CONFIG_1;
- cs47l92->core.adsp[0].mem = cs47l92_dsp1_regions;
- cs47l92->core.adsp[0].num_mems = ARRAY_SIZE(cs47l92_dsp1_regions);
+ cs47l92->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l92->core.adsp[0].cs_dsp.mem = cs47l92_dsp1_regions;
+ cs47l92->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l92_dsp1_regions);
- cs47l92->core.adsp[0].lock_regions = WM_ADSP2_REGION_1_9;
+ cs47l92->core.adsp[0].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
ret = wm_adsp2_init(&cs47l92->core.adsp[0]);
if (ret != 0)
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 067757d1d70a..8f30a3ea8bfe 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -811,12 +811,9 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
mutex_init(&es8316->lock);
ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
"es8316", es8316);
- if (ret == 0) {
- /* Gets re-enabled by es8316_set_jack() */
- disable_irq(es8316->irq);
- } else {
+ if (ret) {
dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
es8316->irq = -ENXIO;
}
@@ -843,6 +840,7 @@ MODULE_DEVICE_TABLE(of, es8316_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id es8316_acpi_match[] = {
{"ESSX8316", 0},
+ {"ESSX8336", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, es8316_acpi_match);
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index 196b06898eeb..2bed5cf229be 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -3531,7 +3531,7 @@ static int rx_macro_probe(struct platform_device *pdev)
rx->clks[3].id = "npl";
rx->clks[4].id = "fsgen";
- ret = devm_clk_bulk_get(dev, RX_NUM_CLKS_MAX, rx->clks);
+ ret = devm_clk_bulk_get_optional(dev, RX_NUM_CLKS_MAX, rx->clks);
if (ret) {
dev_err(dev, "Error getting RX Clocks (%d)\n", ret);
return ret;
@@ -3577,6 +3577,7 @@ static int rx_macro_remove(struct platform_device *pdev)
}
static const struct of_device_id rx_macro_dt_match[] = {
+ { .compatible = "qcom,sc7280-lpass-rx-macro" },
{ .compatible = "qcom,sm8250-lpass-rx-macro" },
{ }
};
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index 27a0d5defd27..a4c0a155af56 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -272,7 +272,7 @@ struct tx_macro {
static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
-static const struct reg_default tx_defaults[] = {
+static struct reg_default tx_defaults[] = {
/* TX Macro */
{ CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
{ CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
@@ -1674,6 +1674,9 @@ static int tx_macro_component_probe(struct snd_soc_component *comp)
snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F,
0x0A);
+ /* Enable swr mic0 and mic1 clock */
+ snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00);
+ snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00);
return 0;
}
@@ -1778,9 +1781,10 @@ static const struct snd_soc_component_driver tx_macro_component_drv = {
static int tx_macro_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct tx_macro *tx;
void __iomem *base;
- int ret;
+ int ret, reg;
tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
if (!tx)
@@ -1792,7 +1796,7 @@ static int tx_macro_probe(struct platform_device *pdev)
tx->clks[3].id = "npl";
tx->clks[4].id = "fsgen";
- ret = devm_clk_bulk_get(dev, TX_NUM_CLKS_MAX, tx->clks);
+ ret = devm_clk_bulk_get_optional(dev, TX_NUM_CLKS_MAX, tx->clks);
if (ret) {
dev_err(dev, "Error getting RX Clocks (%d)\n", ret);
return ret;
@@ -1802,6 +1806,20 @@ static int tx_macro_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
+ /* Update defaults for lpass sc7280 */
+ if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) {
+ for (reg = 0; reg < ARRAY_SIZE(tx_defaults); reg++) {
+ switch (tx_defaults[reg].reg) {
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ tx_defaults[reg].def = 0x0E;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config);
dev_set_drvdata(dev, tx);
@@ -1843,6 +1861,7 @@ static int tx_macro_remove(struct platform_device *pdev)
}
static const struct of_device_id tx_macro_dt_match[] = {
+ { .compatible = "qcom,sc7280-lpass-tx-macro" },
{ .compatible = "qcom,sm8250-lpass-tx-macro" },
{ }
};
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 56c93f4465c9..11147e35689b 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1408,7 +1408,7 @@ static int va_macro_probe(struct platform_device *pdev)
va->clks[1].id = "dcodec";
va->clks[2].id = "mclk";
- ret = devm_clk_bulk_get(dev, VA_NUM_CLKS_MAX, va->clks);
+ ret = devm_clk_bulk_get_optional(dev, VA_NUM_CLKS_MAX, va->clks);
if (ret) {
dev_err(dev, "Error getting VA Clocks (%d)\n", ret);
return ret;
@@ -1472,6 +1472,7 @@ static int va_macro_remove(struct platform_device *pdev)
}
static const struct of_device_id va_macro_dt_match[] = {
+ { .compatible = "qcom,sc7280-lpass-va-macro" },
{ .compatible = "qcom,sm8250-lpass-va-macro" },
{}
};
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index d3ac318fd6b6..75baf8eb7029 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -2445,6 +2445,7 @@ static int wsa_macro_remove(struct platform_device *pdev)
}
static const struct of_device_id wsa_macro_dt_match[] = {
+ {.compatible = "qcom,sc7280-lpass-wsa-macro"},
{.compatible = "qcom,sm8250-lpass-wsa-macro"},
{}
};
diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c
index f4ed7e04673f..272041c6236a 100644
--- a/sound/soc/codecs/madera.c
+++ b/sound/soc/codecs/madera.c
@@ -905,7 +905,7 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol,
*/
mutex_lock(&priv->rate_lock);
- if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].base)) {
+ if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) {
dev_warn(priv->madera->dev,
"Cannot change '%s' while in use by active audio paths\n",
kcontrol->id.name);
@@ -964,7 +964,7 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv,
unsigned int mask = MADERA_DSP_RATE_MASK;
int ret;
- val = priv->adsp_rate_cache[dsp->num - 1] << MADERA_DSP_RATE_SHIFT;
+ val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
switch (priv->madera->type) {
case CS47L35:
@@ -978,15 +978,15 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv,
/* Configure exact dsp frequency */
dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq);
- ret = regmap_write(dsp->regmap,
- dsp->base + MADERA_DSP_CONFIG_2_OFFS, freq);
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq);
if (ret)
goto err;
break;
}
- ret = regmap_update_bits(dsp->regmap,
- dsp->base + MADERA_DSP_CONFIG_1_OFFS,
+ ret = regmap_update_bits(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS,
mask, val);
if (ret)
goto err;
@@ -996,7 +996,7 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv,
return 0;
err:
- dev_err(dsp->dev, "Failed to set DSP%d clock: %d\n", dsp->num, ret);
+ dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret);
return ret;
}
@@ -1018,7 +1018,7 @@ int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
* changes are locked out by the domain_group_ref reference count.
*/
- ret = regmap_read(dsp->regmap, dsp->base, &cur);
+ ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur);
if (ret) {
dev_err(madera->dev,
"Failed to read current DSP rate: %d\n", ret);
@@ -1027,7 +1027,7 @@ int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
cur &= MADERA_DSP_RATE_MASK;
- new = priv->adsp_rate_cache[dsp->num - 1] << MADERA_DSP_RATE_SHIFT;
+ new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
if (new == cur) {
dev_dbg(madera->dev, "DSP rate not changed\n");
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index b392567c2b3e..d1882cbc9381 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1021,7 +1021,7 @@ static int max98390_i2c_probe(struct i2c_client *i2c,
int reg = 0;
struct max98390_priv *max98390 = NULL;
- struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
+ struct i2c_adapter *adapter = i2c->adapter;
ret = i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
new file mode 100644
index 000000000000..bb8649cd421c
--- /dev/null
+++ b/sound/soc/codecs/max98520.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98520.h"
+
+static struct reg_default max98520_reg[] = {
+ {MAX98520_R2000_SW_RESET, 0x00},
+ {MAX98520_R2001_STATUS_1, 0x00},
+ {MAX98520_R2002_STATUS_2, 0x00},
+ {MAX98520_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98520_R2021_THERM_SHDN_THRESH, 0x64},
+ {MAX98520_R2022_THERM_HYSTERESIS, 0x02},
+ {MAX98520_R2023_THERM_FOLDBACK_SET, 0x31},
+ {MAX98520_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98520_R2030_CLK_MON_CTRL, 0x00},
+ {MAX98520_R2037_ERR_MON_CTRL, 0x01},
+ {MAX98520_R2040_PCM_MODE_CFG, 0xC0},
+ {MAX98520_R2041_PCM_CLK_SETUP, 0x04},
+ {MAX98520_R2042_PCM_SR_SETUP, 0x08},
+ {MAX98520_R2043_PCM_RX_SRC1, 0x00},
+ {MAX98520_R2044_PCM_RX_SRC2, 0x00},
+ {MAX98520_R204F_PCM_RX_EN, 0x00},
+ {MAX98520_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98520_R2091_AMP_PATH_GAIN, 0x03},
+ {MAX98520_R2092_AMP_DSP_CFG, 0x02},
+ {MAX98520_R2094_SSM_CFG, 0x01},
+ {MAX98520_R2095_AMP_CFG, 0xF0},
+ {MAX98520_R209F_AMP_EN, 0x00},
+ {MAX98520_R20B0_ADC_SR, 0x00},
+ {MAX98520_R20B1_ADC_RESOLUTION, 0x00},
+ {MAX98520_R20B2_ADC_PVDD0_CFG, 0x02},
+ {MAX98520_R20B3_ADC_THERMAL_CFG, 0x02},
+ {MAX98520_R20B4_ADC_READBACK_CTRL, 0x00},
+ {MAX98520_R20B5_ADC_READBACK_UPDATE, 0x00},
+ {MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB, 0xFF},
+ {MAX98520_R20BB_ADC_LOW_READBACK_LSB, 0x01},
+ {MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20CF_MEAS_ADC_CFG, 0x00},
+ {MAX98520_R20D0_DHT_CFG1, 0x00},
+ {MAX98520_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98520_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98520_R20D3_DHT_CFG2, 0x14},
+ {MAX98520_R20D4_DHT_CFG3, 0x02},
+ {MAX98520_R20D5_DHT_CFG4, 0x04},
+ {MAX98520_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98520_R20D8_DHT_EN, 0x00},
+ {MAX98520_R210E_AUTO_RESTART_BEHAVIOR, 0x00},
+ {MAX98520_R210F_GLOBAL_EN, 0x00},
+ {MAX98520_R21FF_REVISION_ID, 0x00},
+};
+
+static int max98520_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98520_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98520_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98520_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98520_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98520_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98520_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98520_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98520->ch_size;
+ int value;
+
+ if (!max98520->tdm_mode) {
+ /* BCLK configuration */
+ value = max98520_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ dev_dbg(component->dev, "%s tdm_mode:%d out\n", __func__, max98520->tdm_mode);
+ return 0;
+}
+
+static int max98520_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 max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98520->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98520_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98520_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98520_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98520_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98520_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98520_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98520_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98520_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98520_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98520_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98520_PCM_SR_96000;
+ break;
+ case 176400:
+ sampling_rate = MAX98520_PCM_SR_176400;
+ break;
+ case 192000:
+ sampling_rate = MAX98520_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ dev_dbg(component->dev, " %s ch_size: %d, sampling rate : %d out\n", __func__,
+ snd_pcm_format_width(params_format(params)), params_rate(params));
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2042_PCM_SR_SETUP,
+ MAX98520_PCM_SR_MASK,
+ sampling_rate);
+
+ return max98520_set_clock(component, params);
+err:
+ dev_dbg(component->dev, "%s out error", __func__);
+ return -EINVAL;
+}
+
+static int max98520_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98520->tdm_mode = false;
+ else
+ max98520->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98520_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98520_PCM_DMIX_CH1_SHIFT);
+
+ return 0;
+}
+
+#define MAX98520_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98520_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98520_dai_ops = {
+ .set_fmt = max98520_dai_set_fmt,
+ .hw_params = max98520_dai_hw_params,
+ .set_tdm_slot = max98520_dai_tdm_slot,
+};
+
+static int max98520_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(component->dev, " AMP ON\n");
+
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 1);
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev, " AMP OFF\n");
+
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 0);
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 0);
+ usleep_range(30000, 31000);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98520_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98520_R2043_PCM_RX_SRC1,
+ 0, 3, max98520_switch_text);
+
+static const struct snd_kcontrol_new max98520_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98520_left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 0, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 0, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 0, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 0, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 0, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 0, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 0, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 0, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 0, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 0, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 0, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 0, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 0, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 0, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 0, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 0, 0xf, 0),
+};
+
+static const struct snd_kcontrol_new max98520_right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 4, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 4, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 4, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 4, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 4, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 4, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 4, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 4, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 4, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 4, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 4, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 4, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 4, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 4, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 4, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 4, 0xf, 0),
+};
+
+static const struct snd_soc_dapm_widget max98520_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ SND_SOC_NOPM, 0, 0, max98520_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, &max98520_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ /* Left Input Selection */
+ SND_SOC_DAPM_MIXER("Left Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_left_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_left_input_mixer_controls)),
+ /* Right Input Selection */
+ SND_SOC_DAPM_MIXER("Right Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_right_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_right_input_mixer_controls)),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98520_digital_tlv, -6300, 50, 1);
+static const DECLARE_TLV_DB_SCALE(max98520_spk_tlv, -600, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_lim_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_hysteresis_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(600, 200, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+ 5, 10, TLV_DB_SCALE_ITEM(-500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_supply_hr_tlv,
+ 0, 16, TLV_DB_SCALE_ITEM(-2000, 250, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_max_atten_tlv,
+ 1, 20, TLV_DB_SCALE_ITEM(-2000, 100, 0),
+);
+
+static const char * const max98520_dht_attack_rate_text[] = {
+ "20us", "40us", "80us", "160us", "320us", "640us",
+ "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms",
+ "81.92ms", "163.84ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_attack_rate_enum,
+ MAX98520_R20D4_DHT_CFG3, 0,
+ max98520_dht_attack_rate_text);
+
+static const char * const max98520_dht_release_rate_text[] = {
+ "2ms", "4ms", "8ms", "16ms", "32ms", "64ms", "128ms", "256ms", "512ms",
+ "1.024s", "2.048s", "4.096s", "8.192s", "16.384s"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_release_rate_enum,
+ MAX98520_R20D5_DHT_CFG4, 0,
+ max98520_dht_release_rate_text);
+
+static bool max98520_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2027_THERM_FOLDBACK_EN:
+ case MAX98520_R2030_CLK_MON_CTRL:
+ case MAX98520_R2037_ERR_MON_CTRL:
+ case MAX98520_R204F_PCM_RX_EN:
+ case MAX98520_R209F_AMP_EN:
+ case MAX98520_R20CF_MEAS_ADC_CFG:
+ case MAX98520_R20D8_DHT_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2001_STATUS_1... MAX98520_R2002_STATUS_2:
+ case MAX98520_R2020_THERM_WARN_THRESH... MAX98520_R2023_THERM_FOLDBACK_SET:
+ case MAX98520_R2040_PCM_MODE_CFG... MAX98520_R2044_PCM_RX_SRC2:
+ case MAX98520_R2090_AMP_VOL_CTRL... MAX98520_R2092_AMP_DSP_CFG:
+ case MAX98520_R2094_SSM_CFG... MAX98520_R2095_AMP_CFG:
+ case MAX98520_R20B0_ADC_SR... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ case MAX98520_R20D0_DHT_CFG1... MAX98520_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98520_R210E_AUTO_RESTART_BEHAVIOR... MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R2161_BOOST_TM1... MAX98520_R2163_BOOST_TM3:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98520_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2001_STATUS_1 ... MAX98520_R2002_STATUS_2:
+ case MAX98520_R20B4_ADC_READBACK_CTRL
+ ... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_kcontrol_new max98520_snd_controls[] = {
+/* Volume */
+SOC_SINGLE_TLV("Digital Volume", MAX98520_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98520_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98520_R2091_AMP_PATH_GAIN,
+ 0, 0x5, 0, max98520_spk_tlv),
+/* Volume Ramp Up/Down Enable*/
+SOC_SINGLE("Ramp Up Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+/* Clock Monitor Enable */
+SOC_SINGLE("CLK Monitor Switch", MAX98520_R2037_ERR_MON_CTRL,
+ MAX98520_CTRL_CMON_EN_SHIFT, 1, 0),
+/* Clock Monitor Config */
+SOC_SINGLE("CLKMON Autorestart Switch", MAX98520_R2030_CLK_MON_CTRL,
+ MAX98520_CMON_AUTORESTART_SHIFT, 1, 0),
+/* Dither Enable */
+SOC_SINGLE("Dither Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+/* DC Blocker Enable */
+SOC_SINGLE("DC Blocker Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+/* Speaker Safe Mode Enable */
+SOC_SINGLE("Speaker Safemode Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+/* AMP SSM Enable */
+SOC_SINGLE("CP Bypass Switch", MAX98520_R2094_SSM_CFG,
+ MAX98520_SSM_RCVR_MODE_SHIFT, 1, 0),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98520_R20D8_DHT_EN, 0, 1, 0),
+SOC_SINGLE("DHT Limiter Mode", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_MODE_SHIFT, 1, 0),
+SOC_SINGLE("DHT Hysteresis Switch", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Rot Pnt", MAX98520_R20D0_DHT_CFG1,
+ MAX98520_DHT_VROT_PNT_SHIFT, 10, 1, max98520_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Supply Headroom", MAX98520_R20D1_LIMITER_CFG1,
+ MAX98520_DHT_SUPPLY_HR_SHIFT, 16, 0, max98520_dht_supply_hr_tlv),
+SOC_SINGLE_TLV("DHT Limiter Threshold", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_THRESHOLD_SHIFT, 0xF, 1, max98520_dht_lim_thresh_tlv),
+SOC_SINGLE_TLV("DHT Max Attenuation", MAX98520_R20D3_DHT_CFG2,
+ MAX98520_DHT_MAX_ATTEN_SHIFT, 20, 1, max98520_dht_max_atten_tlv),
+SOC_SINGLE_TLV("DHT Hysteresis", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SHIFT, 0x7, 0, max98520_dht_hysteresis_tlv),
+SOC_ENUM("DHT Attack Rate", max98520_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98520_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98520_R20CF_MEAS_ADC_CFG, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98520_R20B3_ADC_THERMAL_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC PVDD MSB", MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC PVDD LSB", MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0, 0x01, 0),
+SOC_SINGLE("ADC TEMP MSB", MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC TEMP LSB", MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0, 0x01, 0),
+};
+
+static const struct snd_soc_dapm_route max98520_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static struct snd_soc_dai_driver max98520_dai[] = {
+ {
+ .name = "max98520-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98520_RATES,
+ .formats = MAX98520_FORMATS,
+ },
+ .ops = &max98520_dai_ops,
+ }
+
+};
+
+static int max98520_probe(struct snd_soc_component *component)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+
+ /* L/R mono mix configuration : "DAI Sel" for 0x2043 */
+ regmap_write(max98520->regmap, MAX98520_R2043_PCM_RX_SRC1, 0x2);
+
+ /* PCM input channles configuration : "Left Input Selection" for 0x2044 */
+ /* PCM input channles configuration : "Right Input Selection" for 0x2044 */
+ regmap_write(max98520->regmap, MAX98520_R2044_PCM_RX_SRC2, 0x10);
+
+ /* Enable DC blocker */
+ regmap_update_bits(max98520->regmap, MAX98520_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable Clock Monitor Auto-restart */
+ regmap_write(max98520->regmap, MAX98520_R2030_CLK_MON_CTRL, 0x1);
+
+ /* set Rx Enable */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R204F_PCM_RX_EN,
+ MAX98520_PCM_RX_EN_MASK,
+ 1);
+
+ return 0;
+}
+
+static int __maybe_unused max98520_suspend(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, true);
+ regcache_mark_dirty(max98520->regmap);
+ return 0;
+}
+
+static int __maybe_unused max98520_resume(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, false);
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+ regcache_sync(max98520->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98520_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98520 = {
+ .probe = max98520_probe,
+ .controls = max98520_snd_controls,
+ .num_controls = ARRAY_SIZE(max98520_snd_controls),
+ .dapm_widgets = max98520_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98520_dapm_widgets),
+ .dapm_routes = max98520_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98520_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct regmap_config max98520_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98520_R21FF_REVISION_ID,
+ .reg_defaults = max98520_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98520_reg),
+ .readable_reg = max98520_readable_register,
+ .volatile_reg = max98520_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98520_power_on(struct max98520_priv *max98520, bool poweron)
+{
+ if (max98520->reset_gpio)
+ gpiod_set_value_cansleep(max98520->reset_gpio, !poweron);
+}
+
+static int max98520_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+ int ret;
+ int reg = 0;
+ struct max98520_priv *max98520;
+ struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
+
+ ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!ret) {
+ dev_err(&i2c->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ max98520 = devm_kzalloc(&i2c->dev, sizeof(*max98520), GFP_KERNEL);
+
+ if (!max98520)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98520);
+
+ /* regmap initialization */
+ max98520->regmap = devm_regmap_init_i2c(i2c, &max98520_regmap);
+ if (IS_ERR(max98520->regmap)) {
+ ret = PTR_ERR(max98520->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Power on device */
+ max98520->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (max98520->reset_gpio) {
+ if (IS_ERR(max98520->reset_gpio)) {
+ ret = PTR_ERR(max98520->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ max98520_power_on(max98520, 1);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98520->regmap, MAX98520_R21FF_REVISION_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98520_R21FF_REVISION_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98520 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98520,
+ max98520_dai, ARRAY_SIZE(max98520_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98520_i2c_id[] = {
+ { "max98520", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98520_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98520_of_match[] = {
+ { .compatible = "maxim,max98520", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98520_of_match);
+#endif
+
+static struct i2c_driver max98520_i2c_driver = {
+ .driver = {
+ .name = "max98520",
+ .of_match_table = of_match_ptr(max98520_of_match),
+ .pm = &max98520_pm,
+ },
+ .probe = max98520_i2c_probe,
+ .id_table = max98520_i2c_id,
+};
+
+module_i2c_driver(max98520_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98520 driver");
+MODULE_AUTHOR("George Song <george.song@maximintegrated.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/max98520.h b/sound/soc/codecs/max98520.h
new file mode 100644
index 000000000000..89a95c25afcf
--- /dev/null
+++ b/sound/soc/codecs/max98520.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, Maxim Integrated.
+ */
+
+#ifndef _MAX98520_H
+#define _MAX98520_H
+
+#define MAX98520_R2000_SW_RESET 0x2000
+#define MAX98520_R2001_STATUS_1 0x2001
+#define MAX98520_R2002_STATUS_2 0x2002
+#define MAX98520_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98520_R2021_THERM_SHDN_THRESH 0x2021
+#define MAX98520_R2022_THERM_HYSTERESIS 0x2022
+#define MAX98520_R2023_THERM_FOLDBACK_SET 0x2023
+#define MAX98520_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98520_R2030_CLK_MON_CTRL 0x2030
+#define MAX98520_R2037_ERR_MON_CTRL 0x2037
+#define MAX98520_R2040_PCM_MODE_CFG 0x2040
+#define MAX98520_R2041_PCM_CLK_SETUP 0x2041
+#define MAX98520_R2042_PCM_SR_SETUP 0x2042
+#define MAX98520_R2043_PCM_RX_SRC1 0x2043
+#define MAX98520_R2044_PCM_RX_SRC2 0x2044
+#define MAX98520_R204F_PCM_RX_EN 0x204F
+#define MAX98520_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98520_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98520_R2092_AMP_DSP_CFG 0x2092
+#define MAX98520_R2094_SSM_CFG 0x2094
+#define MAX98520_R2095_AMP_CFG 0x2095
+#define MAX98520_R209F_AMP_EN 0x209F
+#define MAX98520_R20B0_ADC_SR 0x20B0
+#define MAX98520_R20B1_ADC_RESOLUTION 0x20B1
+#define MAX98520_R20B2_ADC_PVDD0_CFG 0x20B2
+#define MAX98520_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98520_R20B4_ADC_READBACK_CTRL 0x20B4
+#define MAX98520_R20B5_ADC_READBACK_UPDATE 0x20B5
+#define MAX98520_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98520_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98520_R20B8_ADC_TEMP_READBACK_MSB 0x20B8
+#define MAX98520_R20B9_ADC_TEMP_READBACK_LSB 0x20B9
+#define MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB 0x20BA
+#define MAX98520_R20BB_ADC_LOW_READBACK_LSB 0x20BB
+#define MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB 0x20BC
+#define MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB 0x20BD
+#define MAX98520_R20CF_MEAS_ADC_CFG 0x20CF
+#define MAX98520_R20D0_DHT_CFG1 0x20D0
+#define MAX98520_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98520_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98520_R20D3_DHT_CFG2 0x20D3
+#define MAX98520_R20D4_DHT_CFG3 0x20D4
+#define MAX98520_R20D5_DHT_CFG4 0x20D5
+#define MAX98520_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98520_R20D8_DHT_EN 0x20D8
+#define MAX98520_R210E_AUTO_RESTART_BEHAVIOR 0x210E
+#define MAX98520_R210F_GLOBAL_EN 0x210F
+#define MAX98520_R2161_BOOST_TM1 0x2161
+#define MAX98520_R2162_BOOST_TM2 0x2162
+#define MAX98520_R2163_BOOST_TM3 0x2163
+#define MAX98520_R21FF_REVISION_ID 0x21FF
+
+/* MAX98520_R2030_CLK_MON_CTRL */
+#define MAX98520_CMON_AUTORESTART_SHIFT (0)
+
+/* MAX98520_R2037_ERR_MON_CTRL */
+#define MAX98520_CTRL_CMON_EN_SHIFT (0)
+
+/* MAX98520_R2040_PCM_MODE_CFG */
+#define MAX98520_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98520_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98520_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98520_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98520_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98520_R2041_PCM_CLK_SETUP */
+#define MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98520_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98520_R2042_PCM_SR_SETUP */
+#define MAX98520_PCM_SR_SHIFT (0)
+#define MAX98520_IVADC_SR_SHIFT (4)
+#define MAX98520_PCM_SR_MASK (0xF << MAX98520_PCM_SR_SHIFT)
+#define MAX98520_IVADC_SR_MASK (0xF << MAX98520_IVADC_SR_SHIFT)
+#define MAX98520_PCM_SR_8000 (0x0)
+#define MAX98520_PCM_SR_11025 (0x1)
+#define MAX98520_PCM_SR_12000 (0x2)
+#define MAX98520_PCM_SR_16000 (0x3)
+#define MAX98520_PCM_SR_22050 (0x4)
+#define MAX98520_PCM_SR_24000 (0x5)
+#define MAX98520_PCM_SR_32000 (0x6)
+#define MAX98520_PCM_SR_44100 (0x7)
+#define MAX98520_PCM_SR_48000 (0x8)
+#define MAX98520_PCM_SR_88200 (0x9)
+#define MAX98520_PCM_SR_96000 (0xA)
+#define MAX98520_PCM_SR_176400 (0xB)
+#define MAX98520_PCM_SR_192000 (0xC)
+
+/* MAX98520_R2044_PCM_RX_SRC2 */
+#define MAX98520_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98520_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98520_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98520_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98520_R204F_PCM_RX_EN */
+#define MAX98520_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98520_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98520_R2092_AMP_DSP_CFG */
+#define MAX98520_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98520_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98520_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98520_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98520_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98520_DSP_SPK_SAFE_EN_SHIFT (5)
+
+#define MAX98520_SPK_SAFE_EN_MASK (0x1 << MAX98520_DSP_SPK_SAFE_EN_SHIFT)
+
+/* MAX98520_R2094_SSM_CFG */
+#define MAX98520_SSM_EN_SHIFT (0)
+#define MAX98520_SSM_MOD_SHIFT (1)
+#define MAX98520_SSM_RCVR_MODE_SHIFT (3)
+
+/* MAX98520_R2095_AMP_CFG */
+#define MAX98520_CFG_DYN_MODE_SHIFT (4)
+#define MAX98520_CFG_SPK_MODE_SHIFT (3)
+
+/* MAX98520_R20D0_DHT_CFG1 */
+#define MAX98520_DHT_VROT_PNT_SHIFT (0)
+
+/* MAX98520_R20D1_LIMITER_CFG1 */
+#define MAX98520_DHT_SUPPLY_HR_SHIFT (0)
+
+/* MAX98520_R20D2_DHT_CFG2 */
+#define MAX98520_DHT_LIMITER_MODE_SHIFT (0)
+#define MAX98520_DHT_LIMITER_THRESHOLD_SHIFT (1)
+
+/* MAX98520_R20D3_DHT_CFG2 */
+#define MAX98520_DHT_MAX_ATTEN_SHIFT (0)
+
+/* MAX98520_R20D6_DHT_HYSTERESIS_CFG */
+#define MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT (0)
+#define MAX98520_DHT_HYSTERESIS_SHIFT (1)
+
+/* MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_R20B3_ADC_THERMAL_CFG */
+#define MAX98520_FLT_EN_SHIFT (4)
+
+struct max98520_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+#endif
+
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 8b206ee77709..5ba5f876eab8 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -897,6 +897,19 @@ static int max98927_i2c_probe(struct i2c_client *i2c,
"Failed to allocate regmap: %d\n", ret);
return ret;
}
+
+ max98927->reset_gpio
+ = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98927->reset_gpio)) {
+ ret = PTR_ERR(max98927->reset_gpio);
+ return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin");
+ }
+
+ if (max98927->reset_gpio) {
+ gpiod_set_value_cansleep(max98927->reset_gpio, 0);
+ /* Wait for i2c port to be ready */
+ usleep_range(5000, 6000);
+ }
/* Check Revision ID */
ret = regmap_read(max98927->regmap,
@@ -921,6 +934,17 @@ static int max98927_i2c_probe(struct i2c_client *i2c,
return ret;
}
+static int max98927_i2c_remove(struct i2c_client *i2c)
+{
+ struct max98927_priv *max98927 = i2c_get_clientdata(i2c);
+
+ if (max98927->reset_gpio) {
+ gpiod_set_value_cansleep(max98927->reset_gpio, 1);
+ }
+
+ return 0;
+}
+
static const struct i2c_device_id max98927_i2c_id[] = {
{ "max98927", 0},
{ },
@@ -952,6 +976,7 @@ static struct i2c_driver max98927_i2c_driver = {
.pm = &max98927_pm,
},
.probe = max98927_i2c_probe,
+ .remove = max98927_i2c_remove,
.id_table = max98927_i2c_id,
};
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
index 05f495db914d..13f5066d7419 100644
--- a/sound/soc/codecs/max98927.h
+++ b/sound/soc/codecs/max98927.h
@@ -255,6 +255,7 @@ struct max98927_priv {
struct regmap *regmap;
struct snd_soc_component *component;
struct max98927_pdata *pdata;
+ struct gpio_desc *reset_gpio;
unsigned int spk_gain;
unsigned int sysclk;
unsigned int v_l_slot;
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index 2d6a4a29b850..f8532aa7e4aa 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -2697,7 +2697,7 @@ static int mt6359_codec_probe(struct snd_soc_component *cmpnt)
static void mt6359_codec_remove(struct snd_soc_component *cmpnt)
{
- snd_soc_component_exit_regmap(cmpnt);
+ cmpnt->regmap = NULL;
}
static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0);
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
new file mode 100644
index 000000000000..2de818377484
--- /dev/null
+++ b/sound/soc/codecs/nau8821.c
@@ -0,0 +1,1714 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8821.c -- Nuvoton NAU88L21 audio codec driver
+//
+// Copyright 2021 Nuvoton Technology Corp.
+// Author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Lee <wtli@nuvoton.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "nau8821.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq);
+
+struct nau8821_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8821_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8821_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8821_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8821_fll_attr fll_pre_scalar[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+};
+
+/* over sampling rate */
+struct nau8821_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+static const struct nau8821_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8821_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+struct nau8821_dmic_speed {
+ unsigned int param;
+ unsigned int val;
+};
+
+static const struct nau8821_dmic_speed dmic_speed_sel[] = {
+ { 0, 0x0 }, /*SPEED 1, SRC 1 */
+ { 1, 0x1 }, /*SPEED 2, SRC 1/2 */
+ { 2, 0x2 }, /*SPEED 4, SRC 1/4 */
+ { 3, 0x3 }, /*SPEED 8, SRC 1/8 */
+};
+
+static const struct reg_default nau8821_reg_defaults[] = {
+ { NAU8821_R01_ENA_CTRL, 0x00ff },
+ { NAU8821_R03_CLK_DIVIDER, 0x0050 },
+ { NAU8821_R04_FLL1, 0x0 },
+ { NAU8821_R05_FLL2, 0x00bc },
+ { NAU8821_R06_FLL3, 0x0008 },
+ { NAU8821_R07_FLL4, 0x0010 },
+ { NAU8821_R08_FLL5, 0x4000 },
+ { NAU8821_R09_FLL6, 0x6900 },
+ { NAU8821_R0A_FLL7, 0x0031 },
+ { NAU8821_R0B_FLL8, 0x26e9 },
+ { NAU8821_R0D_JACK_DET_CTRL, 0x0 },
+ { NAU8821_R0F_INTERRUPT_MASK, 0x0 },
+ { NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8821_R13_DMIC_CTRL, 0x0 },
+ { NAU8821_R1A_GPIO12_CTRL, 0x0 },
+ { NAU8821_R1B_TDM_CTRL, 0x0 },
+ { NAU8821_R1C_I2S_PCM_CTRL1, 0x000a },
+ { NAU8821_R1D_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8821_R1E_LEFT_TIME_SLOT, 0x0 },
+ { NAU8821_R1F_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8821_R21_BIQ0_COF1, 0x0 },
+ { NAU8821_R22_BIQ0_COF2, 0x0 },
+ { NAU8821_R23_BIQ0_COF3, 0x0 },
+ { NAU8821_R24_BIQ0_COF4, 0x0 },
+ { NAU8821_R25_BIQ0_COF5, 0x0 },
+ { NAU8821_R26_BIQ0_COF6, 0x0 },
+ { NAU8821_R27_BIQ0_COF7, 0x0 },
+ { NAU8821_R28_BIQ0_COF8, 0x0 },
+ { NAU8821_R29_BIQ0_COF9, 0x0 },
+ { NAU8821_R2A_BIQ0_COF10, 0x0 },
+ { NAU8821_R2B_ADC_RATE, 0x0002 },
+ { NAU8821_R2C_DAC_CTRL1, 0x0082 },
+ { NAU8821_R2D_DAC_CTRL2, 0x0 },
+ { NAU8821_R2F_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R30_ADC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R31_MUTE_CTRL, 0x0 },
+ { NAU8821_R32_HSVOL_CTRL, 0x0 },
+ { NAU8821_R34_DACR_CTRL, 0xcfcf },
+ { NAU8821_R35_ADC_DGAIN_CTRL1, 0xcfcf },
+ { NAU8821_R36_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R37_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R38_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8821_R39_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R3A_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R3B_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R3C_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8821_R3D_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R41_BIQ1_COF1, 0x0 },
+ { NAU8821_R42_BIQ1_COF2, 0x0 },
+ { NAU8821_R43_BIQ1_COF3, 0x0 },
+ { NAU8821_R44_BIQ1_COF4, 0x0 },
+ { NAU8821_R45_BIQ1_COF5, 0x0 },
+ { NAU8821_R46_BIQ1_COF6, 0x0 },
+ { NAU8821_R47_BIQ1_COF7, 0x0 },
+ { NAU8821_R48_BIQ1_COF8, 0x0 },
+ { NAU8821_R49_BIQ1_COF9, 0x0 },
+ { NAU8821_R4A_BIQ1_COF10, 0x0 },
+ { NAU8821_R4B_CLASSG_CTRL, 0x0 },
+ { NAU8821_R4C_IMM_MODE_CTRL, 0x0 },
+ { NAU8821_R4D_IMM_RMS_L, 0x0 },
+ { NAU8821_R53_OTPDOUT_1, 0xaad8 },
+ { NAU8821_R54_OTPDOUT_2, 0x0002 },
+ { NAU8821_R55_MISC_CTRL, 0x0 },
+ { NAU8821_R66_BIAS_ADJ, 0x0 },
+ { NAU8821_R68_TRIM_SETTINGS, 0x0 },
+ { NAU8821_R69_ANALOG_CONTROL_1, 0x0 },
+ { NAU8821_R6A_ANALOG_CONTROL_2, 0x0 },
+ { NAU8821_R6B_PGA_MUTE, 0x0 },
+ { NAU8821_R71_ANALOG_ADC_1, 0x0011 },
+ { NAU8821_R72_ANALOG_ADC_2, 0x0020 },
+ { NAU8821_R73_RDAC, 0x0008 },
+ { NAU8821_R74_MIC_BIAS, 0x0006 },
+ { NAU8821_R76_BOOST, 0x0 },
+ { NAU8821_R77_FEPGA, 0x0 },
+ { NAU8821_R7E_PGA_GAIN, 0x0 },
+ { NAU8821_R7F_POWER_UP_CONTROL, 0x0 },
+ { NAU8821_R80_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8821_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R55_MISC_CTRL:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK:
+ case NAU8821_R11_INT_CLR_KEY_STATUS ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4C_IMM_MODE_CTRL:
+ case NAU8821_R4E_FUSE_CTRL2 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R55_MISC_CTRL:
+ case NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R80_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET:
+ case NAU8821_R10_IRQ_STATUS ... NAU8821_R11_INT_CLR_KEY_STATUS:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2A_BIQ0_COF10:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4A_BIQ1_COF10:
+ case NAU8821_R4D_IMM_RMS_L:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R54_OTPDOUT_2:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R81_CHARGE_PUMP_INPUT_READ ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8821_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1,
+ ucontrol->value.bytes.data, params->max);
+
+ return 0;
+}
+
+static int nau8821_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1,
+ data, params->max);
+
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8821_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8821_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8821_R2B_ADC_RATE, NAU8821_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8821_adc_decimation), nau8821_adc_decimation);
+
+static const char * const nau8821_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+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 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 struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1,
+ NAU8821_ADCL_CH_VOL_SFT, NAU8821_ADCR_CH_VOL_SFT,
+ 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8821_R30_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8821_R32_HSVOL_CTRL,
+ NAU8821_HPL_VOL_SFT, NAU8821_HPR_VOL_SFT, 0x3, 1, hp_vol_tlv),
+ SOC_DOUBLE_TLV("Digital Playback Volume", NAU8821_R34_DACR_CTRL,
+ NAU8821_DACL_CH_VOL_SFT, NAU8821_DACR_CH_VOL_SFT,
+ 0xcf, 0, playback_vol_tlv),
+ SOC_DOUBLE_TLV("Frontend PGA Volume", NAU8821_R7E_PGA_GAIN,
+ NAU8821_PGA_GAIN_L_SFT, NAU8821_PGA_GAIN_R_SFT,
+ 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume",
+ NAU8821_R2F_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+ SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8821_biq_coeff_get, nau8821_biq_coeff_put),
+ SOC_SINGLE("ADC Phase Switch", NAU8821_R1B_TDM_CTRL,
+ NAU8821_ADCPHS_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8821_dmic_mode_switch =
+ SOC_DAPM_SINGLE("Switch", NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_EN_SFT, 1, 0);
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int i, speed_selection = -1, clk_adc_src, clk_adc;
+ unsigned int clk_divider_r03;
+
+ /* The DMIC clock is gotten from adc clock divided by
+ * CLK_DMIC_SRC (1, 2, 4, 8). The clock has to be equal or
+ * less than nau8821->dmic_clk_threshold.
+ */
+ regmap_read(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ &clk_divider_r03);
+ clk_adc_src = (clk_divider_r03 & NAU8821_CLK_ADC_SRC_MASK)
+ >> NAU8821_CLK_ADC_SRC_SFT;
+ clk_adc = (nau8821->fs * 256) >> clk_adc_src;
+
+ for (i = 0 ; i < 4 ; i++)
+ if ((clk_adc >> dmic_speed_sel[i].param) <=
+ nau8821->dmic_clk_threshold) {
+ speed_selection = dmic_speed_sel[i].val;
+ break;
+ }
+ if (i == 4)
+ return -EINVAL;
+
+ dev_dbg(nau8821->dev,
+ "clk_adc=%d, dmic_clk_threshold = %d, param=%d, val = %d\n",
+ clk_adc, nau8821->dmic_clk_threshold,
+ dmic_speed_sel[i].param, dmic_speed_sel[i].val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SRC_MASK,
+ (speed_selection << NAU8821_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static int nau8821_left_adc_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 nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
+ regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCL, NAU8821_EN_ADCL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap,
+ NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCL, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_right_adc_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 nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
+ regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCR, NAU8821_EN_ADCR);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap,
+ NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCR, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_pump_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 nau8821 *nau8821 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(20);
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, NAU8821_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, NAU8821_BIAS_TESTDAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("ADCL Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCL_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCR_SFT, 0),
+ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCL_SFT, 0, nau8821_left_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCR_SFT, 0, nau8821_right_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("DMIC Enable", SND_SOC_NOPM,
+ 0, 0, &nau8821_dmic_mode_switch),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_TRISTATE_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8821_R73_RDAC,
+ NAU8821_DACL_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8821_R73_RDAC,
+ NAU8821_DACR_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACL_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACR_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACL_SFT, 0),
+ SND_SOC_DAPM_PGA_S("HP amp L", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_LDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_RDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_CHANRGE_PUMP_EN_SFT, 0, nau8821_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACL_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACR_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R_SFT, 0, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8821_R76_BOOST, NAU8821_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8821_dapm_routes[] = {
+ {"DMIC Enable", "Switch", "DMIC"},
+ {"DMIC Enable", NULL, "DMIC Clock"},
+
+ {"Frontend PGA L", NULL, "MICL"},
+ {"Frontend PGA R", NULL, "MICR"},
+ {"Frontend PGA L", NULL, "MICBIAS"},
+ {"Frontend PGA R", NULL, "MICBIAS"},
+
+ {"ADCL Power", NULL, "Frontend PGA L"},
+ {"ADCR Power", NULL, "Frontend PGA R"},
+
+ {"ADCL Digital path", NULL, "ADCL Power"},
+ {"ADCR Digital path", NULL, "ADCR Power"},
+ {"ADCL Digital path", NULL, "DMIC Enable"},
+ {"ADCR Digital path", NULL, "DMIC Enable"},
+
+ {"AIFTX", NULL, "ADCL Digital path"},
+ {"AIFTX", NULL, "ADCR Digital path"},
+
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
+
+ {"HP amp L", NULL, "DDACL"},
+ {"HP amp R", NULL, "DDACR"},
+
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
+};
+
+static int nau8821_clock_check(struct nau8821 *nau8821,
+ int stream, int rate, int osr)
+{
+ int osrate = 0;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return -EINVAL;
+ osrate = osr_dac_sel[osr].osr;
+ } else {
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return -EINVAL;
+ osrate = osr_adc_sel[osr].osr;
+ }
+
+ if (!osrate || rate * osrate > CLK_DA_AD_MAX) {
+ dev_err(nau8821->dev,
+ "exceed the maximum frequency of CLK_ADC or CLK_DAC");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_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 nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, osr, ctrl_val, bclk_fs, clk_div;
+
+ nau8821->fs = params_rate(params);
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
+ osr &= NAU8821_DAC_OVERSAMPLE_MASK;
+ if (nau8821_clock_check(nau8821, substream->stream,
+ nau8821->fs, osr)) {
+ return -EINVAL;
+ }
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_DAC_SRC_MASK,
+ osr_dac_sel[osr].clk_src << NAU8821_CLK_DAC_SRC_SFT);
+ } else {
+ regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
+ osr &= NAU8821_ADC_SYNC_DOWN_MASK;
+ if (nau8821_clock_check(nau8821, substream->stream,
+ nau8821->fs, osr)) {
+ return -EINVAL;
+ }
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_ADC_SRC_MASK,
+ osr_adc_sel[osr].clk_src << NAU8821_CLK_ADC_SRC_SFT);
+ }
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, &ctrl_val);
+ if (ctrl_val & NAU8821_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs;
+ if (bclk_fs <= 32)
+ clk_div = 3;
+ else if (bclk_fs <= 64)
+ clk_div = 2;
+ else if (bclk_fs <= 128)
+ clk_div = 1;
+ else {
+ return -EINVAL;
+ }
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK,
+ (clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8821_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8821_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8821_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8821_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8821_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8821_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8821_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8821_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8821_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8821_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8821_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK | NAU8821_I2S_DF_MASK |
+ NAU8821_I2S_BP_MASK | NAU8821_I2S_PCMB_MASK, ctrl1_val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ if (mute)
+ val = NAU8821_DAC_SOFT_MUTE;
+
+ return regmap_update_bits(nau8821->regmap,
+ NAU8821_R31_MUTE_CTRL, NAU8821_DAC_SOFT_MUTE, val);
+}
+
+static const struct snd_soc_dai_ops nau8821_dai_ops = {
+ .hw_params = nau8821_hw_params,
+ .set_fmt = nau8821_set_dai_fmt,
+ .mute_stream = nau8821_digital_mute,
+};
+
+#define NAU8821_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8821_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8821_dai = {
+ .name = NUVOTON_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .ops = &nau8821_dai_ops,
+};
+
+
+static bool nau8821_is_jack_inserted(struct regmap *regmap)
+{
+ bool active_high, is_high;
+ int status, jkdet;
+
+ regmap_read(regmap, NAU8821_R0D_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8821_JACK_POLARITY;
+ regmap_read(regmap, NAU8821_R82_GENERAL_STATUS, &status);
+ is_high = status & NAU8821_GPIO2_IN;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
+}
+
+static void nau8821_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8821_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
+static void nau8821_eject_jack(struct nau8821 *nau8821)
+{
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct regmap *regmap = nau8821->regmap;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Clear all interruption status */
+ nau8821_int_status_clear_all(regmap);
+
+ /* Enable the insertion interruption, disable the ejection inter-
+ * ruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_EJECT_DIS | NAU8821_IRQ_INSERT_DIS,
+ NAU8821_IRQ_EJECT_DIS);
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_EJECT_EN);
+
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, NAU8821_JACK_DET_DB_BYPASS);
+
+ /* Close clock for jack type detection at manual mode */
+ if (dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+
+ /* Recover to normal channel input */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, 0);
+}
+
+static void nau8821_jdet_work(struct work_struct *work)
+{
+ struct nau8821 *nau8821 =
+ container_of(work, struct nau8821, jdet_work);
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct regmap *regmap = nau8821->regmap;
+ int jack_status_reg, mic_detected, event = 0, event_mask = 0;
+
+ snd_soc_component_force_enable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ msleep(20);
+
+ regmap_read(regmap, NAU8821_R58_I2C_DEVICE_ID, &jack_status_reg);
+ mic_detected = !(jack_status_reg & NAU8821_KEYDET);
+ if (mic_detected) {
+ dev_dbg(nau8821->dev, "Headset connected\n");
+ event |= SND_JACK_HEADSET;
+
+ /* 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, NAU8821_MICBIAS_JKR2);
+ /* Latch Right Channel Analog data
+ * input into the Right Channel Filter
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, NAU8821_ADC_R_SRC_EN);
+ } else {
+ dev_dbg(nau8821->dev, "Headphone connected\n");
+ event |= SND_JACK_HEADPHONE;
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ }
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+}
+
+/* Enable interruptions with internal clock. */
+static void nau8821_setup_inserted_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Enable internal VCO needed for interruptions */
+ if (nau8821->dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_INTERNAL, 0);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE);
+
+ /* Not bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, 0);
+
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_EJECT_DIS, 0);
+}
+
+static irqreturn_t nau8821_interrupt(int irq, void *data)
+{
+ struct nau8821 *nau8821 = (struct nau8821 *)data;
+ struct regmap *regmap = nau8821->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq)) {
+ dev_err(nau8821->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+
+ dev_dbg(nau8821->dev, "IRQ %d\n", active_irq);
+
+ if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) ==
+ NAU8821_JACK_EJECT_DETECTED) {
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_DIS);
+ nau8821_eject_jack(nau8821);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8821_JACK_EJECT_IRQ_MASK;
+ } else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) ==
+ NAU8821_JACK_INSERT_DETECTED) {
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_EN);
+ if (nau8821_is_jack_inserted(regmap)) {
+ /* detect microphone and jack type */
+ cancel_work_sync(&nau8821->jdet_work);
+ schedule_work(&nau8821->jdet_work);
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS,
+ NAU8821_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap,
+ NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_INSERT_EN);
+ nau8821_setup_inserted_irq(nau8821);
+ } else {
+ dev_warn(nau8821->dev,
+ "Inserted IRQ fired but not connected\n");
+ nau8821_eject_jack(nau8821);
+ }
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config nau8821_regmap_config = {
+ .val_bits = NAU8821_REG_DATA_LEN,
+ .reg_bits = NAU8821_REG_ADDR_LEN,
+
+ .max_register = NAU8821_REG_MAX,
+ .readable_reg = nau8821_readable_reg,
+ .writeable_reg = nau8821_writeable_reg,
+ .volatile_reg = nau8821_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8821_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8821_reg_defaults),
+};
+
+static int nau8821_component_probe(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ nau8821->dapm = dapm;
+
+ return 0;
+}
+
+/**
+ * nau8821_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8821_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8821_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by
+ * dividing freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8821_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in >> fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 24-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 24, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 24) & 0x3ff;
+ fll_param->fll_frac = fvco & 0xffffff;
+
+ return 0;
+}
+
+static void nau8821_fll_apply(struct nau8821 *nau8821,
+ struct nau8821_fll *fll_param)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK | NAU8821_CLK_MCLK_SRC_MASK,
+ NAU8821_CLK_SRC_MCLK | fll_param->mclk_src);
+ /* Make DSP operate at high speed for better performance. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_FLL_RATIO_MASK | NAU8821_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8821_ICTRL_LATCH_SFT));
+ /* FLL 24-bit fractional input */
+ regmap_write(regmap, NAU8821_R0A_FLL7,
+ (fll_param->fll_frac >> 16) & 0xff);
+ regmap_write(regmap, NAU8821_R0B_FLL8, fll_param->fll_frac & 0xffff);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8821_R07_FLL4,
+ NAU8821_HIGHBW_EN | NAU8821_FLL_REF_DIV_MASK,
+ NAU8821_HIGHBW_EN |
+ (fll_param->clk_ref_div << NAU8821_FLL_REF_DIV_SFT));
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_CLK_SW_MASK, NAU8821_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8821_R09_FLL6, NAU8821_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500);
+ } else {
+ /* disable FLL loop filter and cutoff frequency */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK, NAU8821_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500, 0);
+ }
+}
+
+/**
+ * nau8821_set_fll - FLL configuration of nau8821
+ * @component: codec component
+ * @pll_id: PLL requested
+ * @source: clock source
+ * @freq_in: frequency of input clock source
+ * @freq_out: must be 256*Fs in order to achieve the best performance
+ *
+ * The FLL function can select BCLK or MCLK as the input clock source.
+ *
+ * Returns 0 if the parameters have been applied successfully
+ * or negative error code.
+ */
+static int nau8821_set_fll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct nau8821_fll fll_set_param, *fll_param = &fll_set_param;
+ int ret, fs;
+
+ fs = freq_out >> 8;
+ ret = nau8821_calc_fll_param(freq_in, fs, fll_param);
+ if (ret) {
+ dev_err(nau8821->dev,
+ "Unsupported input clock %d to output clock %d\n",
+ freq_in, freq_out);
+ return ret;
+ }
+ dev_dbg(nau8821->dev,
+ "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param->mclk_src, fll_param->ratio, fll_param->fll_frac,
+ fll_param->fll_int, fll_param->clk_ref_div);
+
+ nau8821_fll_apply(nau8821, fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static void nau8821_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, 0);
+ /* Make DSP operate as default setting for power saving. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK, 0);
+}
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (clk_id) {
+ case NAU8821_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8821_configure_mclk_as_sysclk(regmap);
+ break;
+ case NAU8821_CLK_MCLK:
+ nau8821_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0);
+ break;
+ case NAU8821_CLK_INTERNAL:
+ if (nau8821_is_jack_inserted(regmap)) {
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, NAU8821_DCO_EN);
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+ /* Decrease the VCO frequency and make DSP operate
+ * as default setting for power saving.
+ */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK |
+ NAU8821_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN, NAU8821_SDM_EN);
+ }
+ break;
+ case NAU8821_CLK_FLL_MCLK:
+ /* Higher FLL reference input frequency can only set lower
+ * gain error, such as 0000 for input reference from MCLK
+ * 12.288Mhz.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_MCLK | 0);
+ break;
+ case NAU8821_CLK_FLL_BLK:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_BLK |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ case NAU8821_CLK_FLL_FS:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_FS |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ default:
+ dev_err(nau8821->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ nau8821->clk_id = clk_id;
+ dev_dbg(nau8821->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ nau8821->clk_id);
+
+ return 0;
+}
+
+static int nau8821_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ return nau8821_configure_sysclk(nau8821, clk_id, freq);
+}
+
+static int nau8821_resume_setup(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+ if (nau8821->irq) {
+ /* Clear all interruption status */
+ nau8821_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS,
+ NAU8821_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS, 0);
+ }
+
+ return 0;
+}
+
+static int nau8821_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Setup codec configuration after resume */
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_OFF)
+ nau8821_resume_setup(nau8821);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ if (nau8821->irq) {
+ /* Reset the configuration of jack type for detection.
+ * Detach 2kOhm Resistors from MICBIAS to MICGND1/2.
+ */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* Turn off all interruptions before system shutdown.
+ * Keep theinterruption quiet before resume
+ * setup completes.
+ */
+ regmap_write(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_suspend(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (nau8821->irq)
+ disable_irq(nau8821->irq);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+ /* Power down codec power; don't support button wakeup */
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ regcache_cache_only(nau8821->regmap, true);
+ regcache_mark_dirty(nau8821->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_resume(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8821->regmap, false);
+ regcache_sync(nau8821->regmap);
+ if (nau8821->irq)
+ enable_irq(nau8821->irq);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8821_component_driver = {
+ .probe = nau8821_component_probe,
+ .set_sysclk = nau8821_set_sysclk,
+ .set_pll = nau8821_set_fll,
+ .set_bias_level = nau8821_set_bias_level,
+ .suspend = nau8821_suspend,
+ .resume = nau8821_resume,
+ .controls = nau8821_controls,
+ .num_controls = ARRAY_SIZE(nau8821_controls),
+ .dapm_widgets = nau8821_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8821_dapm_widgets),
+ .dapm_routes = nau8821_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8821_dapm_routes),
+ .suspend_bias_off = 1,
+ .non_legacy_dai_naming = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/**
+ * nau8821_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ nau8821->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_WORK(&nau8821->jdet_work, nau8821_jdet_work);
+ ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
+ nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8821", nau8821);
+ if (ret) {
+ dev_err(nau8821->dev, "Cannot request irq %d (%d)\n",
+ nau8821->irq, ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8821_enable_jack_detect);
+
+static void nau8821_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+}
+
+static void nau8821_print_device_properties(struct nau8821 *nau8821)
+{
+ struct device *dev = nau8821->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8821->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8821->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8821->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8821->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8821->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8821->vref_impedance);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8821->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8821->jack_eject_debounce);
+ dev_dbg(dev, "dmic-clk-threshold: %d\n",
+ nau8821->dmic_clk_threshold);
+}
+
+static int nau8821_read_device_properties(struct device *dev,
+ struct nau8821 *nau8821)
+{
+ int ret;
+
+ nau8821->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8821->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8821->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8821->jkdet_polarity);
+ if (ret)
+ nau8821->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8821->micbias_voltage);
+ if (ret)
+ nau8821->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8821->vref_impedance);
+ if (ret)
+ nau8821->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8821->jack_insert_debounce);
+ if (ret)
+ nau8821->jack_insert_debounce = 7;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8821->jack_eject_debounce);
+ if (ret)
+ nau8821->jack_eject_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-clk-threshold",
+ &nau8821->dmic_clk_threshold);
+ if (ret)
+ nau8821->dmic_clk_threshold = 3072000;
+
+ return 0;
+}
+
+static void nau8821_init_regs(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID, NAU8821_BIAS_VMID);
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_GLOBAL_BIAS_EN, NAU8821_GLOBAL_BIAS_EN);
+ /* VMID Tieoff setting and enable TESTDAC.
+ * This sets the analog DAC inputs to a '0' input signal to avoid
+ * any glitches due to power up transients in both the analog and
+ * digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID_SEL_MASK | NAU8821_BIAS_TESTDAC_EN,
+ (nau8821->vref_impedance << NAU8821_BIAS_VMID_SEL_SFT) |
+ NAU8821_BIAS_TESTDAC_EN);
+ /* Disable short Frame Sync detection logic */
+ regmap_update_bits(regmap, NAU8821_R1E_LEFT_TIME_SLOT,
+ NAU8821_DIS_FS_SHORT_DET, NAU8821_DIS_FS_SHORT_DET);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_TIMER_MASK,
+ 0x20 << NAU8821_CLASSG_TIMER_SFT);
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8821_R6A_ANALOG_CONTROL_2,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_POWER_DOWN_DACR | NAU8821_POWER_DOWN_DACL, 0);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8821_R73_RDAC,
+ NAU8821_DAC_CLK_DELAY_MASK | NAU8821_DAC_VREF_MASK,
+ (0x2 << NAU8821_DAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8821_DAC_VREF_SFT));
+
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_VOLTAGE_MASK, nau8821->micbias_voltage);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1,
+ NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64);
+}
+
+static int nau8821_setup_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Jack detection */
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_OUTPUT_EN,
+ nau8821->jkdet_enable ? 0 : NAU8821_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_EN,
+ nau8821->jkdet_pull_enable ? 0 : NAU8821_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_UP,
+ nau8821->jkdet_pull_up ? NAU8821_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8821->jkdet_polarity ? 0 : NAU8821_JACK_POLARITY);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_INSERT_DEBOUNCE_MASK,
+ nau8821->jack_insert_debounce <<
+ NAU8821_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_EJECT_DEBOUNCE_MASK,
+ nau8821->jack_eject_debounce <<
+ NAU8821_JACK_EJECT_DEBOUNCE_SFT);
+ /* Pull up IRQ pin */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_PIN_PULL_UP | NAU8821_IRQ_PIN_PULL_EN |
+ NAU8821_IRQ_OUTPUT_EN, NAU8821_IRQ_PIN_PULL_UP |
+ NAU8821_IRQ_PIN_PULL_EN | NAU8821_IRQ_OUTPUT_EN);
+ /* Disable interruption before codec initiation done */
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, 0x3f5, 0x3f5);
+
+ return 0;
+}
+
+static int nau8821_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8821) {
+ nau8821 = devm_kzalloc(dev, sizeof(*nau8821), GFP_KERNEL);
+ if (!nau8821)
+ return -ENOMEM;
+ nau8821_read_device_properties(dev, nau8821);
+ }
+ i2c_set_clientdata(i2c, nau8821);
+
+ nau8821->regmap = devm_regmap_init_i2c(i2c, &nau8821_regmap_config);
+ if (IS_ERR(nau8821->regmap))
+ return PTR_ERR(nau8821->regmap);
+
+ nau8821->dev = dev;
+ nau8821->irq = i2c->irq;
+ nau8821_print_device_properties(nau8821);
+
+ nau8821_reset_chip(nau8821->regmap);
+ ret = regmap_read(nau8821->regmap, NAU8821_R58_I2C_DEVICE_ID, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read device id (%d)\n", ret);
+ return ret;
+ }
+ nau8821_init_regs(nau8821);
+
+ if (i2c->irq)
+ nau8821_setup_irq(nau8821);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &nau8821_component_driver, &nau8821_dai, 1);
+
+ return ret;
+}
+
+static int nau8821_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct nau8821 *nau8821 = i2c_get_clientdata(i2c_client);
+
+ devm_free_irq(nau8821->dev, nau8821->irq, nau8821);
+
+ return 0;
+}
+
+static const struct i2c_device_id nau8821_i2c_ids[] = {
+ { "nau8821", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8821_of_ids[] = {
+ { .compatible = "nuvoton,nau8821", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8821_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8821_acpi_match[] = {
+ { "NVTN2020", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8821_acpi_match);
+#endif
+
+static struct i2c_driver nau8821_driver = {
+ .driver = {
+ .name = "nau8821",
+ .of_match_table = of_match_ptr(nau8821_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8821_acpi_match),
+ },
+ .probe = nau8821_i2c_probe,
+ .remove = nau8821_i2c_remove,
+ .id_table = nau8821_i2c_ids,
+};
+module_i2c_driver(nau8821_driver);
+
+MODULE_DESCRIPTION("ASoC nau8821 driver");
+MODULE_AUTHOR("John Hsu <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
new file mode 100644
index 000000000000..a92edfeb9d3a
--- /dev/null
+++ b/sound/soc/codecs/nau8821.h
@@ -0,0 +1,533 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L21 ALSA SoC audio driver
+ *
+ * Copyright 2021 Nuvoton Technology Corp.
+ * Author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Lee <wtli@nuvoton.com>
+ */
+
+#ifndef __NAU8821_H__
+#define __NAU8821_H__
+
+#define NAU8821_R00_RESET 0x00
+#define NAU8821_R01_ENA_CTRL 0x01
+#define NAU8821_R03_CLK_DIVIDER 0x03
+#define NAU8821_R04_FLL1 0x04
+#define NAU8821_R05_FLL2 0x05
+#define NAU8821_R06_FLL3 0x06
+#define NAU8821_R07_FLL4 0x07
+#define NAU8821_R08_FLL5 0x08
+#define NAU8821_R09_FLL6 0x09
+#define NAU8821_R0A_FLL7 0x0a
+#define NAU8821_R0B_FLL8 0x0b
+#define NAU8821_R0D_JACK_DET_CTRL 0x0d
+#define NAU8821_R0F_INTERRUPT_MASK 0x0f
+#define NAU8821_R10_IRQ_STATUS 0x10
+#define NAU8821_R11_INT_CLR_KEY_STATUS 0x11
+#define NAU8821_R12_INTERRUPT_DIS_CTRL 0x12
+#define NAU8821_R13_DMIC_CTRL 0x13
+#define NAU8821_R1A_GPIO12_CTRL 0x1a
+#define NAU8821_R1B_TDM_CTRL 0x1b
+#define NAU8821_R1C_I2S_PCM_CTRL1 0x1c
+#define NAU8821_R1D_I2S_PCM_CTRL2 0x1d
+#define NAU8821_R1E_LEFT_TIME_SLOT 0x1e
+#define NAU8821_R1F_RIGHT_TIME_SLOT 0x1f
+#define NAU8821_R21_BIQ0_COF1 0x21
+#define NAU8821_R22_BIQ0_COF2 0x22
+#define NAU8821_R23_BIQ0_COF3 0x23
+#define NAU8821_R24_BIQ0_COF4 0x24
+#define NAU8821_R25_BIQ0_COF5 0x25
+#define NAU8821_R26_BIQ0_COF6 0x26
+#define NAU8821_R27_BIQ0_COF7 0x27
+#define NAU8821_R28_BIQ0_COF8 0x28
+#define NAU8821_R29_BIQ0_COF9 0x29
+#define NAU8821_R2A_BIQ0_COF10 0x2a
+#define NAU8821_R2B_ADC_RATE 0x2b
+#define NAU8821_R2C_DAC_CTRL1 0x2c
+#define NAU8821_R2D_DAC_CTRL2 0x2d
+#define NAU8821_R2F_DAC_DGAIN_CTRL 0x2f
+#define NAU8821_R30_ADC_DGAIN_CTRL 0x30
+#define NAU8821_R31_MUTE_CTRL 0x31
+#define NAU8821_R32_HSVOL_CTRL 0x32
+#define NAU8821_R34_DACR_CTRL 0x34
+#define NAU8821_R35_ADC_DGAIN_CTRL1 0x35
+#define NAU8821_R36_ADC_DRC_KNEE_IP12 0x36
+#define NAU8821_R37_ADC_DRC_KNEE_IP34 0x37
+#define NAU8821_R38_ADC_DRC_SLOPES 0x38
+#define NAU8821_R39_ADC_DRC_ATKDCY 0x39
+#define NAU8821_R3A_DAC_DRC_KNEE_IP12 0x3a
+#define NAU8821_R3B_DAC_DRC_KNEE_IP34 0x3b
+#define NAU8821_R3C_DAC_DRC_SLOPES 0x3c
+#define NAU8821_R3D_DAC_DRC_ATKDCY 0x3d
+#define NAU8821_R41_BIQ1_COF1 0x41
+#define NAU8821_R42_BIQ1_COF2 0x42
+#define NAU8821_R43_BIQ1_COF3 0x43
+#define NAU8821_R44_BIQ1_COF4 0x44
+#define NAU8821_R45_BIQ1_COF5 0x45
+#define NAU8821_R46_BIQ1_COF6 0x46
+#define NAU8821_R47_BIQ1_COF7 0x47
+#define NAU8821_R48_BIQ1_COF8 0x48
+#define NAU8821_R49_BIQ1_COF9 0x49
+#define NAU8821_R4A_BIQ1_COF10 0x4a
+#define NAU8821_R4B_CLASSG_CTRL 0x4b
+#define NAU8821_R4C_IMM_MODE_CTRL 0x4c
+#define NAU8821_R4D_IMM_RMS_L 0x4d
+#define NAU8821_R4E_FUSE_CTRL2 0x4e
+#define NAU8821_R4F_FUSE_CTRL3 0x4f
+#define NAU8821_R51_FUSE_CTRL1 0x51
+#define NAU8821_R53_OTPDOUT_1 0x53
+#define NAU8821_R54_OTPDOUT_2 0x54
+#define NAU8821_R55_MISC_CTRL 0x55
+#define NAU8821_R58_I2C_DEVICE_ID 0x58
+#define NAU8821_R59_SARDOUT_RAM_STATUS 0x59
+#define NAU8821_R5A_SOFTWARE_RST 0x5a
+#define NAU8821_R66_BIAS_ADJ 0x66
+#define NAU8821_R68_TRIM_SETTINGS 0x68
+#define NAU8821_R69_ANALOG_CONTROL_1 0x69
+#define NAU8821_R6A_ANALOG_CONTROL_2 0x6a
+#define NAU8821_R6B_PGA_MUTE 0x6b
+#define NAU8821_R71_ANALOG_ADC_1 0x71
+#define NAU8821_R72_ANALOG_ADC_2 0x72
+#define NAU8821_R73_RDAC 0x73
+#define NAU8821_R74_MIC_BIAS 0x74
+#define NAU8821_R76_BOOST 0x76
+#define NAU8821_R77_FEPGA 0x77
+#define NAU8821_R7E_PGA_GAIN 0x7e
+#define NAU8821_R7F_POWER_UP_CONTROL 0x7f
+#define NAU8821_R80_CHARGE_PUMP 0x80
+#define NAU8821_R81_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8821_R82_GENERAL_STATUS 0x82
+#define NAU8821_REG_MAX NAU8821_R82_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8821_REG_ADDR_LEN 16
+#define NAU8821_REG_DATA_LEN 16
+
+/* ENA_CTRL (0x01) */
+#define NAU8821_CLK_DAC_INV_SFT 14
+#define NAU8821_CLK_DAC_INV (0x1 << NAU8821_CLK_DAC_INV)
+#define NAU8821_EN_DACR_SFT 11
+#define NAU8821_EN_DACR (0x1 << NAU8821_EN_DACR_SFT)
+#define NAU8821_EN_DACL_SFT 10
+#define NAU8821_EN_DACL (0x1 << NAU8821_EN_DACL_SFT)
+#define NAU8821_EN_ADCR_SFT 9
+#define NAU8821_EN_ADCR (0x1 << NAU8821_EN_ADCR_SFT)
+#define NAU8821_EN_ADCL_SFT 8
+#define NAU8821_EN_ADCL (0x1 << NAU8821_EN_ADCL_SFT)
+#define NAU8821_EN_ADC_CLK_SFT 7
+#define NAU8821_EN_ADC_CLK (0x1 << NAU8821_EN_ADC_CLK_SFT)
+#define NAU8821_EN_DAC_CLK_SFT 6
+#define NAU8821_EN_DAC_CLK (0x1 << NAU8821_EN_DAC_CLK_SFT)
+#define NAU8821_EN_I2S_CLK_SFT 4
+#define NAU8821_EN_I2S_CLK (0x1 << NAU8821_EN_I2S_CLK_SFT)
+#define NAU8821_EN_DRC_CLK_SFT 0
+#define NAU8821_EN_DRC_CLK (0x1 << NAU8821_EN_DRC_CLK_SFT)
+
+/* CLK_DIVIDER (0x03) */
+#define NAU8821_CLK_SRC_SFT 15
+#define NAU8821_CLK_SRC_MASK (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_VCO (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_MCLK (0x0 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_SFT 13
+#define NAU8821_CLK_CODEC_SRC_MASK (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_VCO (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_MCLK (0x0 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_ADC_SRC_SFT 6
+#define NAU8821_CLK_ADC_SRC_MASK (0x3 << NAU8821_CLK_ADC_SRC_SFT)
+#define NAU8821_CLK_DAC_SRC_SFT 4
+#define NAU8821_CLK_DAC_SRC_MASK (0x3 << NAU8821_CLK_DAC_SRC_SFT)
+#define NAU8821_CLK_MCLK_SRC_MASK 0xf
+
+/* FLL1 (0x04) */
+#define NAU8821_ICTRL_LATCH_SFT 10
+#define NAU8821_ICTRL_LATCH_MASK (0x7 << NAU8821_ICTRL_LATCH_SFT)
+#define NAU8821_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8821_GAIN_ERR_SFT 12
+#define NAU8821_GAIN_ERR_MASK (0xf << NAU8821_GAIN_ERR_SFT)
+#define NAU8821_FLL_CLK_SRC_SFT 10
+#define NAU8821_FLL_CLK_SRC_MASK (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_FS (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_BLK (0x2 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_MCLK (0x0 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_INTEGER_MASK 0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8821_HIGHBW_EN_SFT 15
+#define NAU8821_HIGHBW_EN (0x1 << NAU8821_HIGHBW_EN_SFT)
+#define NAU8821_FLL_REF_DIV_SFT 10
+#define NAU8821_FLL_REF_DIV_MASK (0x3 << NAU8821_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8821_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8821_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8821_FLL_CLK_SW_SFT 13
+#define NAU8821_FLL_CLK_SW_MASK (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_N2 (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_REF (0x0 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_FTR_SW_SFT 12
+#define NAU8821_FLL_FTR_SW_MASK (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_ACCU (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_FILTER (0x0 << NAU8821_FLL_FTR_SW_SFT)
+
+/* FLL6 (0x09) */
+#define NAU8821_DCO_EN (0x1 << 15)
+#define NAU8821_SDM_EN (0x1 << 14)
+#define NAU8821_CUTOFF500 (0x1 << 13)
+
+/* FLL7 (0x0a) */
+#define NAU8821_FLL_FRACH_MASK 0xff
+
+/* FLL8 (0x0b) */
+#define NAU8821_FLL_FRACL_MASK 0xffff
+
+/* JACK_DET_CTRL (0x0d) */
+/* 0 - open, 1 - short to GND */
+#define NAU8821_SPKR_DWN1R_SFT 15
+#define NAU8821_SPKR_DWN1R (0x1 << NAU8821_SPKR_DWN1R_SFT)
+#define NAU8821_SPKR_DWN1L_SFT 14
+#define NAU8821_SPKR_DWN1L (0x1 << NAU8821_SPKR_DWN1L_SFT)
+#define NAU8821_JACK_DET_RESTART (0x1 << 9)
+#define NAU8821_JACK_DET_DB_BYPASS (0x1 << 8)
+#define NAU8821_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8821_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8821_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8821_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8821_JACK_POLARITY (0x1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0x0f) */
+#define NAU8821_IRQ_PIN_PULL_UP (0x1 << 14)
+#define NAU8821_IRQ_PIN_PULL_EN (0x1 << 13)
+#define NAU8821_IRQ_OUTPUT_EN (0x1 << 11)
+#define NAU8821_IRQ_RMS_EN (0x1 << 8)
+#define NAU8821_IRQ_KEY_RELEASE_EN (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_EN (0x1 << 6)
+#define NAU8821_IRQ_MIC_DET_EN (0x1 << 4)
+#define NAU8821_IRQ_EJECT_EN (0x1 << 2)
+#define NAU8821_IRQ_INSERT_EN 0x1
+
+/* IRQ_STATUS (0x10) */
+#define NAU8821_SHORT_CIRCUIT_IRQ (0x1 << 9)
+#define NAU8821_IMPEDANCE_MEAS_IRQ (0x1 << 8)
+#define NAU8821_KEY_IRQ_SFT 6
+#define NAU8821_KEY_IRQ_MASK (0x3 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_RELEASE_IRQ (0x2 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_SHORT_PRESS_IRQ (0x1 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_MIC_DETECT_IRQ (0x1 << 4)
+#define NAU8821_JACK_EJECT_IRQ_MASK (0x3 << 2)
+#define NAU8821_JACK_EJECT_DETECTED (0x1 << 2)
+#define NAU8821_JACK_INSERT_IRQ_MASK 0x3
+#define NAU8821_JACK_INSERT_DETECTED 0x1
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8821_IRQ_KEY_RELEASE_DIS (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_DIS (0x1 << 6)
+#define NAU8821_IRQ_MIC_DIS (0x1 << 4)
+#define NAU8821_IRQ_EJECT_DIS (0x1 << 2)
+#define NAU8821_IRQ_INSERT_DIS 0x1
+
+/* DMIC_CTRL (0x13) */
+#define NAU8821_DMIC_DS_SFT 7
+#define NAU8821_DMIC_DS_MASK (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_HIGH (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_LOW (0x0 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_SRC_SFT 1
+#define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_DMIC_EN_SFT 0
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8821_JKDET_PULL_EN (0x1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8821_JKDET_OUTPUT_EN (0x1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* TDM_CTRL (0x1b) */
+#define NAU8821_TDM_EN_SFT 15
+#define NAU8821_TDM_EN (0x1 << NAU8821_TDM_EN_SFT)
+#define NAU8821_ADCPHS_SFT 13
+#define NAU8821_DACL_CH_SFT 7
+#define NAU8821_DACL_CH_MASK (0x7 << NAU8821_DACL_CH_SFT)
+#define NAU8821_DACR_CH_SFT 4
+#define NAU8821_DACR_CH_MASK (0x7 << NAU8821_DACR_CH_SFT)
+#define NAU8821_ADCL_CH_SFT 2
+#define NAU8821_ADCL_CH_MASK (0x3 << NAU8821_ADCL_CH_SFT)
+#define NAU8821_ADCR_CH_SFT 0
+#define NAU8821_ADCR_CH_MASK 0x3
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8821_I2S_BP_SFT 7
+#define NAU8821_I2S_BP_MASK (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_BP_INV (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_PCMB_SFT 6
+#define NAU8821_I2S_PCMB_MASK (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_PCMB_EN (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_DL_SFT 2
+#define NAU8821_I2S_DL_MASK (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_32 (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_24 (0x2 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_20 (0x1 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_16 (0x0 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DF_MASK 0x3
+#define NAU8821_I2S_DF_PCM_AB 0x3
+#define NAU8821_I2S_DF_I2S 0x2
+#define NAU8821_I2S_DF_LEFT 0x1
+#define NAU8821_I2S_DF_RIGTH 0x0
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8821_I2S_TRISTATE_SFT 15
+#define NAU8821_I2S_TRISTATE (0x1 << NAU8821_I2S_TRISTATE_SFT)
+#define NAU8821_I2S_LRC_DIV_SFT 12
+#define NAU8821_I2S_LRC_DIV_MASK (0x3 << NAU8821_I2S_LRC_DIV_SFT)
+#define NAU8821_I2S_MS_SFT 3
+#define NAU8821_I2S_MS_MASK (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_MASTER (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_SLAVE (0x0 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_BLK_DIV_MASK 0x7
+
+/* LEFT_TIME_SLOT (0x1e) */
+#define NAU8821_TSLOT_L_OFFSET_MASK 0x3ff
+#define NAU8821_DIS_FS_SHORT_DET (0x1 << 13)
+
+/* RIGHT_TIME_SLOT (0x1f) */
+#define NAU8821_TSLOT_R_OFFSET_MASK 0x3ff
+
+/* BIQ0_COF10 (0x2a) */
+#define NAU8821_BIQ0_ADC_EN_SFT 3
+#define NAU8821_BIQ0_ADC_EN_EN (0x1 << NAU8821_BIQ0_ADC_EN_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8821_ADC_SYNC_DOWN_SFT 0
+#define NAU8821_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8821_ADC_SYNC_DOWN_256 0x3
+#define NAU8821_ADC_SYNC_DOWN_128 0x2
+#define NAU8821_ADC_SYNC_DOWN_64 0x1
+#define NAU8821_ADC_SYNC_DOWN_32 0x0
+#define NAU8821_ADC_L_SRC_SFT 15
+#define NAU8821_ADC_L_SRC_EN (0x1 << NAU8821_ADC_L_SRC_SFT)
+#define NAU8821_ADC_R_SRC_SFT 14
+#define NAU8821_ADC_R_SRC_EN (0x1 << NAU8821_ADC_R_SRC_SFT)
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8821_DAC_OVERSAMPLE_SFT 0
+#define NAU8821_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8821_DAC_OVERSAMPLE_32 0x4
+#define NAU8821_DAC_OVERSAMPLE_128 0x2
+#define NAU8821_DAC_OVERSAMPLE_256 0x1
+#define NAU8821_DAC_OVERSAMPLE_64 0x0
+
+/* DAC_DGAIN_CTRL (0x2f) */
+#define NAU8821_DAC1_TO_DAC0_ST_SFT 8
+#define NAU8821_DAC1_TO_DAC0_ST_MASK (0xff << NAU8821_DAC1_TO_DAC0_ST_SFT)
+#define NAU8821_DAC0_TO_DAC1_ST_SFT 0
+#define NAU8821_DAC0_TO_DAC1_ST_MASK 0xff
+
+/* MUTE_CTRL (0x31) */
+#define NAU8821_DAC_ZC_EN (0x1 << 12)
+#define NAU8821_DAC_SOFT_MUTE (0x1 << 9)
+#define NAU8821_ADC_ZC_EN (0x1 << 2)
+#define NAU8821_ADC_SOFT_MUTE (0x1 << 1)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8821_HP_MUTE (0x1 << 15)
+#define NAU8821_HP_MUTE_AUTO (0x1 << 14)
+#define NAU8821_HPL_MUTE (0x1 << 13)
+#define NAU8821_HPR_MUTE (0x1 << 12)
+#define NAU8821_HPL_VOL_SFT 4
+#define NAU8821_HPL_VOL_MASK (0x3 << NAU8821_HPL_VOL_SFT)
+#define NAU8821_HPR_VOL_SFT 0
+#define NAU8821_HPR_VOL_MASK (0x3 << NAU8821_HPR_VOL_SFT)
+
+/* DACR_CTRL (0x34) */
+#define NAU8821_DACR_CH_VOL_SFT 8
+#define NAU8821_DACR_CH_VOL_MASK (0xff << NAU8821_DACR_CH_VOL_SFT)
+#define NAU8821_DACL_CH_VOL_SFT 0
+#define NAU8821_DACL_CH_VOL_MASK 0xff
+
+/* ADC_DGAIN_CTRL1 (0x35) */
+#define NAU8821_ADCR_CH_VOL_SFT 8
+#define NAU8821_ADCR_CH_VOL_MASK (0xff << NAU8821_ADCR_CH_VOL_SFT)
+#define NAU8821_ADCL_CH_VOL_SFT 0
+#define NAU8821_ADCL_CH_VOL_MASK 0xff
+
+/* BIQ1_COF10 (0x4a) */
+#define NAU8821_BIQ1_DAC_EN_SFT 3
+#define NAU8821_BIQ1_DAC_EN_EN (0x1 << NAU8821_BIQ1_DAC_EN_SFT)
+
+/* CLASSG_CTRL (0x4b) */
+#define NAU8821_CLASSG_TIMER_SFT 8
+#define NAU8821_CLASSG_TIMER_MASK (0x3f << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_64MS (0x20 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_32MS (0x10 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_16MS (0x8 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_8MS (0x4 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_2MS (0x2 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_1MS (0x1 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_RDAC_EN_SFT 2
+#define NAU8821_CLASSG_RDAC_EN (0x1 << NAU8821_CLASSG_RDAC_EN_SFT)
+#define NAU8821_CLASSG_LDAC_EN_SFT 1
+#define NAU8821_CLASSG_LDAC_EN (0x1 << NAU8821_CLASSG_LDAC_EN_SFT)
+#define NAU8821_CLASSG_EN_SFT 0
+#define NAU8821_CLASSG_EN 0x1
+
+/* IMM_MODE_CTRL (0x4c) */
+#define NAU8821_IMM_THD_SFT 8
+#define NAU8821_IMM_THD_MASK (0x3f << NAU8821_IMM_THD_SFT)
+#define NAU8821_IMM_GEN_VOL_SFT 6
+#define NAU8821_IMM_GEN_VOL_MASK (0x3 << NAU8821_IMM_GEN_VOL_SFT)
+#define NAU8821_IMM_CYC_SFT 4
+#define NAU8821_IMM_CYC_MASK (0x3 << NAU8821_IMM_CYC_SFT)
+#define NAU8821_IMM_EN (0x1 << 3)
+#define NAU8821_IMM_DAC_SRC_MASK 0x3
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8821_KEYDET (0x1 << 7)
+#define NAU8821_MICDET (0x1 << 6)
+#define NAU8821_SOFTWARE_ID_MASK 0x3
+
+/* BIAS_ADJ (0x66) */
+#define NAU8821_BIAS_HP_IMP (0x1 << 15)
+#define NAU8821_BIAS_TESTDAC_SFT 8
+#define NAU8821_BIAS_TESTDAC_EN (0x3 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACR_EN (0x2 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACL_EN (0x1 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_VMID (0x1 << 6)
+#define NAU8821_BIAS_VMID_SEL_SFT 4
+#define NAU8821_BIAS_VMID_SEL_MASK (0x3 << NAU8821_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8821_JD_POL_SFT 2
+#define NAU8821_JD_POL_MASK (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_POL_INV (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_OUT_POL_SFT 1
+#define NAU8821_JD_OUT_POL_MASK (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_OUT_POL_INV (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_EN_SFT 0
+#define NAU8821_JD_EN 0x1
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8821_HP_NON_CLASSG_CURRENT_2xADJ (0x1 << 12)
+#define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1)
+#define NAU8821_DAC_CAPACITOR_LSB 0x1
+
+/* ANALOG_ADC_1 (0x71) */
+#define NAU8821_MICDET_EN_SFT 0
+#define NAU8821_MICDET_MASK 0x1
+#define NAU8821_MICDET_DIS 0x1
+#define NAU8821_MICDET_EN 0x0
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8821_ADC_VREFSEL_SFT 8
+#define NAU8821_ADC_VREFSEL_MASK (0x3 << NAU8821_ADC_VREFSEL_SFT)
+#define NAU8821_POWERUP_ADCL_SFT 6
+#define NAU8821_POWERUP_ADCL (0x1 << NAU8821_POWERUP_ADCL_SFT)
+#define NAU8821_POWERUP_ADCR_SFT 4
+#define NAU8821_POWERUP_ADCR (0x1 << NAU8821_POWERUP_ADCR_SFT)
+
+/* RDAC (0x73) */
+#define NAU8821_DACR_EN_SFT 13
+#define NAU8821_DACR_EN (0x3 << NAU8821_DACR_EN_SFT)
+#define NAU8821_DACL_EN_SFT 12
+#define NAU8821_DACL_EN (0x3 << NAU8821_DACL_EN_SFT)
+#define NAU8821_DACR_CLK_EN_SFT 9
+#define NAU8821_DACR_CLK_EN (0x3 << NAU8821_DACR_CLK_EN_SFT)
+#define NAU8821_DACL_CLK_EN_SFT 8
+#define NAU8821_DACL_CLK_EN (0x3 << NAU8821_DACL_CLK_EN_SFT)
+#define NAU8821_DAC_CLK_DELAY_SFT 4
+#define NAU8821_DAC_CLK_DELAY_MASK (0x7 << NAU8821_DAC_CLK_DELAY_SFT)
+#define NAU8821_DAC_VREF_SFT 2
+#define NAU8821_DAC_VREF_MASK (0x3 << NAU8821_DAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8821_MICBIAS_JKR2 (0x1 << 12)
+#define NAU8821_MICBIAS_POWERUP_SFT 8
+#define NAU8821_MICBIAS_VOLTAGE_SFT 0
+#define NAU8821_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8821_PRECHARGE_DIS (0x1 << 13)
+#define NAU8821_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8821_HP_BOOST_DIS_SFT 9
+#define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT)
+#define NAU8821_HP_BOOST_G_DIS (0x1 << 8)
+#define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8821_FEPGA_MODEL_SFT 4
+#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODER_SFT 0
+#define NAU8821_FEPGA_MODER_MASK 0xf
+
+/* PGA_GAIN (0x7e) */
+#define NAU8821_PGA_GAIN_L_SFT 8
+#define NAU8821_PGA_GAIN_L_MASK (0x3f << NAU8821_PGA_GAIN_L_SFT)
+#define NAU8821_PGA_GAIN_R_SFT 0
+#define NAU8821_PGA_GAIN_R_MASK 0x3f
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8821_PUP_PGA_L_SFT 15
+#define NAU8821_PUP_PGA_L (0x1 << NAU8821_PUP_PGA_L_SFT)
+#define NAU8821_PUP_PGA_R_SFT 14
+#define NAU8821_PUP_PGA_R (0x1 << NAU8821_PUP_PGA_R_SFT)
+#define NAU8821_PUP_INTEG_R_SFT 5
+#define NAU8821_PUP_INTEG_R (0x1 << NAU8821_PUP_INTEG_R_SFT)
+#define NAU8821_PUP_INTEG_L_SFT 4
+#define NAU8821_PUP_INTEG_L (0x1 << NAU8821_PUP_INTEG_L_SFT)
+#define NAU8821_PUP_DRV_INSTG_R_SFT 3
+#define NAU8821_PUP_DRV_INSTG_R (0x1 << NAU8821_PUP_DRV_INSTG_R_SFT)
+#define NAU8821_PUP_DRV_INSTG_L_SFT 2
+#define NAU8821_PUP_DRV_INSTG_L (0x1 << NAU8821_PUP_DRV_INSTG_L_SFT)
+#define NAU8821_PUP_MAIN_DRV_R_SFT 1
+#define NAU8821_PUP_MAIN_DRV_R (0x1 << NAU8821_PUP_MAIN_DRV_R_SFT)
+#define NAU8821_PUP_MAIN_DRV_L_SFT 0
+#define NAU8821_PUP_MAIN_DRV_L 0x1
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8821_JAMNODCLOW (0x1 << 10)
+#define NAU8821_POWER_DOWN_DACR_SFT 9
+#define NAU8821_POWER_DOWN_DACR (0x1 << NAU8821_POWER_DOWN_DACR_SFT)
+#define NAU8821_POWER_DOWN_DACL_SFT 8
+#define NAU8821_POWER_DOWN_DACL (0x1 << NAU8821_POWER_DOWN_DACL_SFT)
+#define NAU8821_CHANRGE_PUMP_EN_SFT 5
+#define NAU8821_CHANRGE_PUMP_EN (0x1 << NAU8821_CHANRGE_PUMP_EN_SFT)
+
+/* GENERAL_STATUS (0x82) */
+#define NAU8821_GPIO2_IN_SFT 1
+#define NAU8821_GPIO2_IN (0x1 << NAU8821_GPIO2_IN_SFT)
+
+#define NUVOTON_CODEC_DAI "nau8821-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8821_CLK_DIS,
+ NAU8821_CLK_MCLK,
+ NAU8821_CLK_INTERNAL,
+ NAU8821_CLK_FLL_MCLK,
+ NAU8821_CLK_FLL_BLK,
+ NAU8821_CLK_FLL_FS,
+};
+
+struct nau8821 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct work_struct jdet_work;
+ int irq;
+ int clk_id;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+ int fs;
+ int dmic_clk_threshold;
+};
+
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+#endif /* __NAU8821_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
index f946ef65a4c1..d0dd1542f78a 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -27,6 +28,13 @@
#include "nau8824.h"
+#define NAU8824_JD_ACTIVE_HIGH BIT(0)
+#define NAU8824_MONO_SPEAKER BIT(1)
+
+static int nau8824_quirk;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
static int nau8824_config_sysclk(struct nau8824 *nau8824,
int clk_id, unsigned int freq);
@@ -1845,6 +1853,63 @@ static int nau8824_read_device_properties(struct device *dev,
return 0;
}
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id nau8824_quirk_table[] = {
+ {
+ /* Cyberbook T116 rugged tablet */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH |
+ NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* CUBE iwork8 Air */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* Pipo W2S */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PIPO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "W2S"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {}
+};
+
+static void nau8824_check_quirks(void)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (quirk_override != -1) {
+ nau8824_quirk = quirk_override;
+ return;
+ }
+
+ dmi_id = dmi_first_match(nau8824_quirk_table);
+ if (dmi_id)
+ nau8824_quirk = (unsigned long)dmi_id->driver_data;
+}
+
+const char *nau8824_components(void)
+{
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_MONO_SPEAKER)
+ return "cfg-spk:1";
+ else
+ return "cfg-spk:2";
+}
+EXPORT_SYMBOL_GPL(nau8824_components);
+
static int nau8824_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1869,6 +1934,11 @@ static int nau8824_i2c_probe(struct i2c_client *i2c,
nau8824->irq = i2c->irq;
sema_init(&nau8824->jd_sem, 1);
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_JD_ACTIVE_HIGH)
+ nau8824->jkdet_polarity = 0;
+
nau8824_print_device_properties(nau8824);
ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value);
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
index 1d7bdd8e0523..de4bae8281d0 100644
--- a/sound/soc/codecs/nau8824.h
+++ b/sound/soc/codecs/nau8824.h
@@ -197,7 +197,7 @@
/* JACK_DET_CTRL (0x0D) */
#define NAU8824_JACK_EJECT_DT_SFT 2
#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT)
-#define NAU8824_JACK_LOGIC 0x1
+#define NAU8824_JACK_LOGIC (0x1 << 1)
/* INTERRUPT_SETTING_1 (0x0F) */
@@ -470,6 +470,7 @@ struct nau8824_osr_attr {
int nau8824_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
+const char *nau8824_components(void);
#endif /* _NAU8824_H */
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 67de0e49ccf4..7734bc35ab21 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -47,6 +47,7 @@
static int nau8825_configure_sysclk(struct nau8825 *nau8825,
int clk_id, unsigned int freq);
+static bool nau8825_is_jack_inserted(struct regmap *regmap);
struct nau8825_fll {
int mclk_src;
@@ -981,6 +982,31 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8825->regmap;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8825->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ nau8825_configure_sysclk(nau8825,
+ NAU8825_CLK_INTERNAL, 0);
+ } else {
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+ }
+ }
+
+ return 0;
+}
+
static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1094,6 +1120,9 @@ static const struct snd_kcontrol_new nau8825_dacr_mux =
static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
15, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("MIC"),
SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
@@ -1182,9 +1211,11 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"ADC", NULL, "ADC Clock"},
{"ADC", NULL, "ADC Power"},
{"AIFTX", NULL, "ADC"},
+ {"AIFTX", NULL, "System Clock"},
- {"DDACL", NULL, "Playback"},
- {"DDACR", NULL, "Playback"},
+ {"AIFRX", NULL, "System Clock"},
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
{"DDACL", NULL, "DDAC Clock"},
{"DDACR", NULL, "DDAC Clock"},
{"DACL Mux", "DACL", "DDACL"},
@@ -1434,6 +1465,12 @@ int nau8825_enable_jack_detect(struct snd_soc_component *component,
nau8825->jack = jack;
+ if (!nau8825->jack) {
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, 0);
+ return 0;
+ }
/* Ground HP Outputs[1:0], needed for headset auto detection
* Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
*/
@@ -2416,6 +2453,12 @@ static int __maybe_unused nau8825_resume(struct snd_soc_component *component)
return 0;
}
+static int nau8825_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ return nau8825_enable_jack_detect(component, jack);
+}
+
static const struct snd_soc_component_driver nau8825_component_driver = {
.probe = nau8825_component_probe,
.remove = nau8825_component_remove,
@@ -2430,6 +2473,7 @@ static const struct snd_soc_component_driver nau8825_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
.dapm_routes = nau8825_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+ .set_jack = nau8825_set_jack,
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index b8cfc250612c..f39f98bbc97f 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -17,7 +17,7 @@ static struct snd_soc_dai_driver pcm5102a_dai = {
.playback = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index faff2b558687..297af7ff824c 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -1311,6 +1311,14 @@ static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol,
.put = rt1011_r0_load_mode_put \
}
+static const char * const rt1011_i2s_ref_texts[] = {
+ "Left Channel", "Right Channel"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum,
+ RT1011_TDM1_SET_1, 7,
+ rt1011_i2s_ref_texts);
+
static const struct snd_kcontrol_new rt1011_snd_controls[] = {
/* I2S Data In Selection */
SOC_ENUM("DIN Source", rt1011_din_source_enum),
@@ -1349,6 +1357,8 @@ static const struct snd_kcontrol_new rt1011_snd_controls[] = {
/* R0 temperature */
SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP,
2, 255, 0),
+ /* I2S Reference */
+ SOC_ENUM("I2S Reference", rt1011_i2s_ref_enum),
};
static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index c0c5952cdff7..6a27dfacd81c 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -864,7 +864,7 @@ static int rt1015_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
index 7561d202274c..9845cdddcb4c 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -490,7 +490,7 @@ static int rt1016_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out * 4, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index 8c0b00242bb8..80b7ca0e4e1e 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -359,7 +359,7 @@ static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
index 7a0094578e46..a9c473537a91 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -841,7 +841,7 @@ static int rt1305_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index b4e5546e2e21..c555b77b3c5c 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -664,7 +664,7 @@ static int rt1308_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 4b1ad5054e8d..577680df7052 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -936,7 +936,7 @@ static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index fd0d3a08e9dd..8e6414468a87 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1133,7 +1133,7 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index cd1db5caabad..d01fe73ab9c8 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1909,7 +1909,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 9408ee63cb26..197c56047947 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -2969,7 +2969,7 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index fc0c83b73f09..f302c25688d1 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1487,7 +1487,7 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
@@ -2261,11 +2261,8 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT, "rt5651", rt5651);
- if (ret == 0) {
- /* Gets re-enabled by rt5651_set_jack() */
- disable_irq(rt5651->irq);
- } else {
+ | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
+ if (ret) {
dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n",
rt5651->irq, ret);
rt5651->irq = -ENXIO;
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 4a50b169fe03..e1503c2eee81 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3509,7 +3509,7 @@ static int rt5659_set_component_pll(struct snd_soc_component *component, int pll
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 33ff9156358b..3b50fb29864e 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1046,7 +1046,7 @@ static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index be9fc58ff681..0389b2bb360e 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -2941,7 +2941,7 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index e59323fd5bf2..33e889802ff8 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4374,7 +4374,7 @@ static int rt5665_set_component_pll(struct snd_soc_component *component, int pll
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 6ab1a8bc3735..fb09715bf932 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2171,7 +2171,7 @@ static int rt5668_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index ecbaf129a6e3..ce7684752bb0 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2577,7 +2577,7 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index f655228c8c4b..4a8c267d4fbc 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4557,7 +4557,7 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
ret = rt5677_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index b9d5d7a0975b..983347b65127 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -139,6 +139,8 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5682);
+ rt5682->i2c_dev = &i2c->dev;
+
rt5682->pdata = i2s_default_platform_data;
if (pdata)
@@ -276,6 +278,21 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
}
+#ifdef CONFIG_COMMON_CLK
+ /* Check if MCLK provided */
+ rt5682->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5682->mclk))
+ return PTR_ERR(rt5682->mclk);
+
+ /* Register CCF DAI clock control */
+ ret = rt5682_register_dai_clks(rt5682);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682->lrck[RT5682_AIF1] = 48000;
+#endif
+
return devm_snd_soc_register_component(&i2c->dev,
&rt5682_soc_component_dev,
rt5682_dai, ARRAY_SIZE(rt5682_dai));
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 4a64cab99c55..78b4cb5fb6c8 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -46,6 +46,8 @@ static const struct reg_sequence patch_list[] = {
{RT5682_SAR_IL_CMD_1, 0x22b7},
{RT5682_SAR_IL_CMD_3, 0x0365},
{RT5682_SAR_IL_CMD_6, 0x0110},
+ {RT5682_CHARGE_PUMP_1, 0x0210},
+ {RT5682_HP_LOGIC_CTRL_2, 0x0007},
};
void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev)
@@ -1515,21 +1517,29 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_component_write(component,
- RT5682_HP_LOGIC_CTRL_2, 0x0012);
- snd_soc_component_write(component,
- RT5682_HP_CTRL_2, 0x6000);
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_AMP_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5682_HP_LOGIC_CTRL_2,
+ RT5682_HP_LC2_SIG_SOUR2_MASK, RT5682_HP_LC2_SIG_SOUR2_REG);
snd_soc_component_update_bits(component,
RT5682_DEPOP_1, 0x60, 0x60);
snd_soc_component_update_bits(component,
RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L);
break;
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_M);
snd_soc_component_update_bits(component,
RT5682_DEPOP_1, 0x60, 0x0);
- snd_soc_component_write(component,
- RT5682_HP_CTRL_2, 0x0000);
snd_soc_component_update_bits(component,
RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000);
break;
@@ -1637,6 +1647,23 @@ static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum,
static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl =
SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum);
+static const unsigned int rt5682_hpo_sig_out_values[] = {
+ 2,
+ 7,
+};
+
+static const char * const rt5682_hpo_sig_out_mode[] = {
+ "Legacy",
+ "OneBit",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_hpo_sig_out_enum,
+ RT5682_HP_LOGIC_CTRL_2, 0, RT5682_HP_LC2_SIG_SOUR1_MASK,
+ rt5682_hpo_sig_out_mode, rt5682_hpo_sig_out_values);
+
+static const struct snd_kcontrol_new rt5682_hpo_sig_demux =
+ SOC_DAPM_ENUM("HPO Signal Demux", rt5682_hpo_sig_out_enum);
+
static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT,
0, NULL, 0),
@@ -1820,6 +1847,10 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0,
&hpor_switch),
+ SND_SOC_DAPM_OUT_DRV("HPO Legacy", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HPO OneBit", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DEMUX("HPO Signal Demux", SND_SOC_NOPM, 0, 0, &rt5682_hpo_sig_demux),
+
/* CLK DET */
SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET,
RT5682_SYS_CLK_DET_SFT, 0, NULL, 0),
@@ -1987,10 +2018,19 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
{"HP Amp", NULL, "Charge Pump"},
{"HP Amp", NULL, "CLKDET SYS"},
{"HP Amp", NULL, "Vref1"},
- {"HPOL Playback", "Switch", "HP Amp"},
- {"HPOR Playback", "Switch", "HP Amp"},
+
+ {"HPO Signal Demux", NULL, "HP Amp"},
+
+ {"HPO Legacy", "Legacy", "HPO Signal Demux"},
+ {"HPO OneBit", "OneBit", "HPO Signal Demux"},
+
+ {"HPOL Playback", "Switch", "HPO Legacy"},
+ {"HPOR Playback", "Switch", "HPO Legacy"},
+
{"HPOL", NULL, "HPOL Playback"},
{"HPOR", NULL, "HPOR Playback"},
+ {"HPOL", NULL, "HPO OneBit"},
+ {"HPOR", NULL, "HPO OneBit"},
};
static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -2327,7 +2367,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component,
pll2_fout1 = 3840000;
ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n",
+ dev_err(component->dev, "Unsupported input clock %d\n",
freq_in);
return ret;
}
@@ -2339,7 +2379,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n",
+ dev_err(component->dev, "Unsupported input clock %d\n",
pll2_fout1);
return ret;
}
@@ -2390,7 +2430,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component,
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(component->dev, "Unsupport input clock %d\n",
+ dev_err(component->dev, "Unsupported input clock %d\n",
freq_in);
return ret;
}
@@ -2510,7 +2550,7 @@ static int rt5682_set_bias_level(struct snd_soc_component *component,
static bool rt5682_clk_check(struct rt5682_priv *rt5682)
{
if (!rt5682->master[RT5682_AIF1]) {
- dev_dbg(rt5682->component->dev, "sysclk/dai not set correctly\n");
+ dev_dbg(rt5682->i2c_dev, "sysclk/dai not set correctly\n");
return false;
}
return true;
@@ -2521,13 +2561,15 @@ static int rt5682_wclk_prepare(struct clk_hw *hw)
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
if (!rt5682_clk_check(rt5682))
return -EINVAL;
+ component = rt5682->component;
+ dapm = snd_soc_component_get_dapm(component);
+
snd_soc_dapm_mutex_lock(dapm);
snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
@@ -2557,13 +2599,15 @@ static void rt5682_wclk_unprepare(struct clk_hw *hw)
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
if (!rt5682_clk_check(rt5682))
return;
+ component = rt5682->component;
+ dapm = snd_soc_component_get_dapm(component);
+
snd_soc_dapm_mutex_lock(dapm);
snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
@@ -2587,7 +2631,6 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
const char * const clk_name = clk_hw_get_name(hw);
if (!rt5682_clk_check(rt5682))
@@ -2597,7 +2640,7 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
*/
if (rt5682->lrck[RT5682_AIF1] != CLK_48 &&
rt5682->lrck[RT5682_AIF1] != CLK_44) {
- dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
__func__, clk_name, CLK_44, CLK_48);
return 0;
}
@@ -2611,7 +2654,6 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
const char * const clk_name = clk_hw_get_name(hw);
if (!rt5682_clk_check(rt5682))
@@ -2621,7 +2663,7 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
* It will force to 48kHz if not both.
*/
if (rate != CLK_48 && rate != CLK_44) {
- dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
__func__, clk_name, CLK_44, CLK_48);
rate = CLK_48;
}
@@ -2635,7 +2677,7 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
+ struct snd_soc_component *component;
struct clk_hw *parent_hw;
const char * const clk_name = clk_hw_get_name(hw);
int pre_div;
@@ -2644,6 +2686,8 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
if (!rt5682_clk_check(rt5682))
return -EINVAL;
+ component = rt5682->component;
+
/*
* Whether the wclk's parent clk (mclk) exists or not, please ensure
* it is fixed or set to 48MHz before setting wclk rate. It's a
@@ -2653,12 +2697,12 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
*/
parent_hw = clk_hw_get_parent(hw);
if (!parent_hw)
- dev_warn(component->dev,
+ dev_warn(rt5682->i2c_dev,
"Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
CLK_PLL2_FIN);
if (parent_rate != CLK_PLL2_FIN)
- dev_warn(component->dev, "clk %s only support %d Hz input\n",
+ dev_warn(rt5682->i2c_dev, "clk %s only support %d Hz input\n",
clk_name, CLK_PLL2_FIN);
/*
@@ -2690,10 +2734,9 @@ static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_BCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
unsigned int bclks_per_wclk;
- bclks_per_wclk = snd_soc_component_read(component, RT5682_TDM_TCON_CTRL);
+ regmap_read(rt5682->regmap, RT5682_TDM_TCON_CTRL, &bclks_per_wclk);
switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) {
case RT5682_TDM_BCLK_MS1_256:
@@ -2754,20 +2797,22 @@ static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_BCLK_IDX]);
- struct snd_soc_component *component = rt5682->component;
+ struct snd_soc_component *component;
struct snd_soc_dai *dai;
unsigned long factor;
if (!rt5682_clk_check(rt5682))
return -EINVAL;
+ component = rt5682->component;
+
factor = rt5682_bclk_get_factor(rate, parent_rate);
for_each_component_dais(component, dai)
if (dai->id == RT5682_AIF1)
break;
if (!dai) {
- dev_err(component->dev, "dai %d not found in component\n",
+ dev_err(rt5682->i2c_dev, "dai %d not found in component\n",
RT5682_AIF1);
return -ENODEV;
}
@@ -2790,10 +2835,9 @@ static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = {
},
};
-static int rt5682_register_dai_clks(struct snd_soc_component *component)
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682)
{
- struct device *dev = component->dev;
- struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct device *dev = rt5682->i2c_dev;
struct rt5682_platform_data *pdata = &rt5682->pdata;
struct clk_hw *dai_clk_hw;
int i, ret;
@@ -2851,6 +2895,7 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component)
return 0;
}
+EXPORT_SYMBOL_GPL(rt5682_register_dai_clks);
#endif /* CONFIG_COMMON_CLK */
static int rt5682_probe(struct snd_soc_component *component)
@@ -2860,9 +2905,6 @@ static int rt5682_probe(struct snd_soc_component *component)
unsigned long time;
struct snd_soc_dapm_context *dapm = &component->dapm;
-#ifdef CONFIG_COMMON_CLK
- int ret;
-#endif
rt5682->component = component;
if (rt5682->is_sdw) {
@@ -2874,26 +2916,6 @@ static int rt5682_probe(struct snd_soc_component *component)
dev_err(&slave->dev, "Initialization not complete, timed out\n");
return -ETIMEDOUT;
}
- } else {
-#ifdef CONFIG_COMMON_CLK
- /* Check if MCLK provided */
- rt5682->mclk = devm_clk_get(component->dev, "mclk");
- if (IS_ERR(rt5682->mclk)) {
- if (PTR_ERR(rt5682->mclk) != -ENOENT) {
- ret = PTR_ERR(rt5682->mclk);
- return ret;
- }
- rt5682->mclk = NULL;
- }
-
- /* Register CCF DAI clock control */
- ret = rt5682_register_dai_clks(component);
- if (ret)
- return ret;
-
- /* Initial setup for CCF */
- rt5682->lrck[RT5682_AIF1] = CLK_48;
-#endif
}
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
index b59221048ebf..d93829c35585 100644
--- a/sound/soc/codecs/rt5682.h
+++ b/sound/soc/codecs/rt5682.h
@@ -375,6 +375,14 @@
#define RT5682_R_VOL_MASK (0x3f)
#define RT5682_R_VOL_SFT 0
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682_HP_C2_DAC_AMP_MUTE_SFT 15
+#define RT5682_HP_C2_DAC_AMP_MUTE (0x1 << 15)
+#define RT5682_HP_C2_DAC_L_EN_SFT 14
+#define RT5682_HP_C2_DAC_L_EN (0x1 << 14)
+#define RT5682_HP_C2_DAC_R_EN_SFT 13
+#define RT5682_HP_C2_DAC_R_EN (0x1 << 13)
+
/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
#define RT5682_G_HP (0xf << 8)
#define RT5682_G_HP_SFT 8
@@ -1265,6 +1273,10 @@
#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2)
/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682_CP_SW_SIZE_MASK (0x7 << 8)
+#define RT5682_CP_SW_SIZE_L (0x4 << 8)
+#define RT5682_CP_SW_SIZE_M (0x2 << 8)
+#define RT5682_CP_SW_SIZE_S (0x1 << 8)
#define RT5682_CP_CLK_HP_MASK (0x3 << 4)
#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4)
#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4)
@@ -1315,6 +1327,14 @@
#define RT5682_DEB_STO_DAC_MASK (0x7 << 4)
#define RT5682_DEB_80_MS (0x0 << 4)
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682_HP_LC2_SIG_SOUR2_MASK (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_REG (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_DC_CAL (0x0 << 4)
+#define RT5682_HP_LC2_SIG_SOUR1_MASK (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_1BIT (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_LEGA (0x2)
+
/* SAR ADC Inline Command Control 1 (0x0210) */
#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15)
#define RT5682_SAR_BUTT_DET_EN (0x1 << 15)
@@ -1408,6 +1428,7 @@ enum {
struct rt5682_priv {
struct snd_soc_component *component;
+ struct device *i2c_dev;
struct rt5682_platform_data pdata;
struct regmap *regmap;
struct regmap *sdw_regmap;
@@ -1462,6 +1483,8 @@ void rt5682_calibrate(struct rt5682_priv *rt5682);
void rt5682_reset(struct rt5682_priv *rt5682);
int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev);
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682);
+
#define RT5682_REG_NUM 318
extern const struct reg_default rt5682_reg[RT5682_REG_NUM];
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
new file mode 100644
index 000000000000..470957fcad6b
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.c
@@ -0,0 +1,3197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682s.c -- RT5682I-VS ALSA SoC audio component driver
+//
+// Copyright 2021 Realtek Semiconductor Corp.
+// Author: Derek Fang <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682s.h>
+
+#include "rt5682s.h"
+
+#define DEVICE_ID 0x6749
+
+static const struct rt5682s_platform_data i2s_default_platform_data = {
+ .dmic1_data_pin = RT5682S_DMIC1_DATA_GPIO2,
+ .dmic1_clk_pin = RT5682S_DMIC1_CLK_GPIO3,
+ .jd_src = RT5682S_JD1,
+ .dai_clk_names[RT5682S_DAI_WCLK_IDX] = "rt5682-dai-wclk",
+ .dai_clk_names[RT5682S_DAI_BCLK_IDX] = "rt5682-dai-bclk",
+};
+
+static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = {
+ "AVDD",
+ "MICVDD",
+};
+
+static const struct reg_sequence patch_list[] = {
+ {RT5682S_I2C_CTRL, 0x0007},
+ {RT5682S_DIG_IN_CTRL_1, 0x0000},
+ {RT5682S_CHOP_DAC_2, 0x2020},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_2, 0x0101},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_1, 0x80c0},
+ {RT5682S_HP_CALIB_CTRL_9, 0x0002},
+ {RT5682S_DEPOP_1, 0x0000},
+ {RT5682S_HP_CHARGE_PUMP_2, 0x3c15},
+ {RT5682S_DAC1_DIG_VOL, 0xfefe},
+ {RT5682S_SAR_IL_CMD_2, 0xac00},
+ {RT5682S_SAR_IL_CMD_3, 0x024c},
+ {RT5682S_CBJ_CTRL_6, 0x0804},
+};
+
+static void rt5682s_apply_patch_list(struct rt5682s_priv *rt5682s,
+ struct device *dev)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(rt5682s->regmap, patch_list, ARRAY_SIZE(patch_list));
+ if (ret)
+ dev_warn(dev, "Failed to apply regmap patch: %d\n", ret);
+}
+
+static const struct reg_default rt5682s_reg[] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x0001},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x8007},
+ {0x000b, 0x0000},
+ {0x000f, 0x4000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x0000},
+ {0x0013, 0x1200},
+ {0x0014, 0x200a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0017, 0x05a4},
+ {0x0019, 0xffff},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x08c0},
+ {0x0044, 0x1818},
+ {0x004b, 0x00c0},
+ {0x004c, 0x0000},
+ {0x004d, 0x0000},
+ {0x0061, 0x00c0},
+ {0x0062, 0x008a},
+ {0x0063, 0x0800},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x000c},
+ {0x0068, 0x0000},
+ {0x0069, 0x0000},
+ {0x006a, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0810},
+ {0x006f, 0xe4de},
+ {0x0070, 0x3320},
+ {0x0071, 0x0000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x007f, 0x0000},
+ {0x0080, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008e, 0x0060},
+ {0x008f, 0x4da1},
+ {0x0091, 0x1c15},
+ {0x0092, 0x0425},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0095, 0x008f},
+ {0x0096, 0x0000},
+ {0x0097, 0x0000},
+ {0x0098, 0x0000},
+ {0x0099, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x0000},
+ {0x009f, 0x0009},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0000},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6749},
+ {0x0100, 0xa000},
+ {0x010b, 0x0066},
+ {0x010c, 0x6666},
+ {0x010d, 0x2202},
+ {0x010e, 0x6666},
+ {0x010f, 0xa800},
+ {0x0110, 0x0006},
+ {0x0111, 0x0460},
+ {0x0112, 0x2000},
+ {0x0113, 0x0200},
+ {0x0117, 0x8000},
+ {0x0118, 0x0303},
+ {0x0125, 0x0020},
+ {0x0132, 0x5026},
+ {0x0136, 0x8000},
+ {0x0139, 0x0005},
+ {0x013a, 0x3030},
+ {0x013b, 0xa000},
+ {0x013c, 0x4110},
+ {0x013f, 0x0000},
+ {0x0145, 0x0022},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0156, 0x0022},
+ {0x0157, 0x0303},
+ {0x0158, 0x2222},
+ {0x0159, 0x0000},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0001},
+ {0x0190, 0x4131},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01ac, 0x0000},
+ {0x01ad, 0x0000},
+ {0x01ae, 0x0000},
+ {0x01af, 0x2000},
+ {0x01b0, 0x0000},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0017},
+ {0x01b4, 0x004b},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x03e8},
+ {0x01b7, 0x0000},
+ {0x01b8, 0x0000},
+ {0x01b9, 0x0400},
+ {0x01ba, 0xb5b6},
+ {0x01bb, 0x9124},
+ {0x01bc, 0x4924},
+ {0x01bd, 0x0009},
+ {0x01be, 0x0018},
+ {0x01bf, 0x002a},
+ {0x01c0, 0x004c},
+ {0x01c1, 0x0097},
+ {0x01c2, 0x01c3},
+ {0x01c3, 0x03e9},
+ {0x01c4, 0x1389},
+ {0x01c5, 0xc351},
+ {0x01c6, 0x02a0},
+ {0x01c7, 0x0b0f},
+ {0x01c8, 0x402f},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0000},
+ {0x01d8, 0x0162},
+ {0x01d9, 0x0007},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0020},
+ {0x01e0, 0x04c1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e5, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa004},
+ {0x0212, 0x0365},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x021d, 0x024c},
+ {0x02fa, 0x0000},
+ {0x02fb, 0x0000},
+ {0x02fc, 0x0000},
+ {0x03fe, 0x0000},
+ {0x03ff, 0x0000},
+ {0x0500, 0x0000},
+ {0x0600, 0x0000},
+ {0x0610, 0x6666},
+ {0x0611, 0xa9aa},
+ {0x0620, 0x6666},
+ {0x0621, 0xa9aa},
+ {0x0630, 0x6666},
+ {0x0631, 0xa9aa},
+ {0x0640, 0x6666},
+ {0x0641, 0xa9aa},
+ {0x07fa, 0x0000},
+ {0x08fa, 0x0000},
+ {0x08fb, 0x0000},
+ {0x0d00, 0x0000},
+ {0x1100, 0x0000},
+ {0x1101, 0x0000},
+ {0x1102, 0x0000},
+ {0x1103, 0x0000},
+ {0x1104, 0x0000},
+ {0x1105, 0x0000},
+ {0x1106, 0x0000},
+ {0x1107, 0x0000},
+ {0x1108, 0x0000},
+ {0x1109, 0x0000},
+ {0x110a, 0x0000},
+ {0x110b, 0x0000},
+ {0x110c, 0x0000},
+ {0x1111, 0x0000},
+ {0x1112, 0x0000},
+ {0x1113, 0x0000},
+ {0x1114, 0x0000},
+ {0x1115, 0x0000},
+ {0x1116, 0x0000},
+ {0x1117, 0x0000},
+ {0x1118, 0x0000},
+ {0x1119, 0x0000},
+ {0x111a, 0x0000},
+ {0x111b, 0x0000},
+ {0x111c, 0x0000},
+ {0x1401, 0x0404},
+ {0x1402, 0x0007},
+ {0x1403, 0x0365},
+ {0x1404, 0x0210},
+ {0x1405, 0x0365},
+ {0x1406, 0x0210},
+ {0x1407, 0x0000},
+ {0x1408, 0x0000},
+ {0x1409, 0x0000},
+ {0x140a, 0x0000},
+ {0x140b, 0x0000},
+ {0x140c, 0x0000},
+ {0x140d, 0x0000},
+ {0x140e, 0x0000},
+ {0x140f, 0x0000},
+ {0x1410, 0x0000},
+ {0x1411, 0x0000},
+ {0x1801, 0x0004},
+ {0x1802, 0x0000},
+ {0x1803, 0x0000},
+ {0x1804, 0x0000},
+ {0x1805, 0x00ff},
+ {0x2c00, 0x0000},
+ {0x3400, 0x0200},
+ {0x3404, 0x0000},
+ {0x3405, 0x0000},
+ {0x3406, 0x0000},
+ {0x3407, 0x0000},
+ {0x3408, 0x0000},
+ {0x3409, 0x0000},
+ {0x340a, 0x0000},
+ {0x340b, 0x0000},
+ {0x340c, 0x0000},
+ {0x340d, 0x0000},
+ {0x340e, 0x0000},
+ {0x340f, 0x0000},
+ {0x3410, 0x0000},
+ {0x3411, 0x0000},
+ {0x3412, 0x0000},
+ {0x3413, 0x0000},
+ {0x3414, 0x0000},
+ {0x3415, 0x0000},
+ {0x3424, 0x0000},
+ {0x3425, 0x0000},
+ {0x3426, 0x0000},
+ {0x3427, 0x0000},
+ {0x3428, 0x0000},
+ {0x3429, 0x0000},
+ {0x342a, 0x0000},
+ {0x342b, 0x0000},
+ {0x342c, 0x0000},
+ {0x342d, 0x0000},
+ {0x342e, 0x0000},
+ {0x342f, 0x0000},
+ {0x3430, 0x0000},
+ {0x3431, 0x0000},
+ {0x3432, 0x0000},
+ {0x3433, 0x0000},
+ {0x3434, 0x0000},
+ {0x3435, 0x0000},
+ {0x3440, 0x6319},
+ {0x3441, 0x3771},
+ {0x3500, 0x0002},
+ {0x3501, 0x5728},
+ {0x3b00, 0x3010},
+ {0x3b01, 0x3300},
+ {0x3b02, 0x2200},
+ {0x3b03, 0x0100},
+};
+
+static bool rt5682s_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_CBJ_CTRL_2:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_ST:
+ case RT5682S_IL_CMD_1:
+ case RT5682S_4BTN_IL_CMD_1:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_VERSION_ID...RT5682S_DEVICE_ID:
+ case RT5682S_STO_NG2_CTRL_1:
+ case RT5682S_STO_NG2_CTRL_5...RT5682S_STO_NG2_CTRL_7:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_4:
+ case RT5682S_HP_IMP_SENS_CTRL_13:
+ case RT5682S_HP_IMP_SENS_CTRL_14:
+ case RT5682S_HP_IMP_SENS_CTRL_43...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_CALIB_CTRL_1:
+ case RT5682S_HP_CALIB_CTRL_10:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_2...RT5682S_SAR_IL_CMD_5:
+ case RT5682S_SAR_IL_CMD_10:
+ case RT5682S_SAR_IL_CMD_11:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1:
+ case RT5682S_NEW_CBJ_DET_CTL_10...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_PILOT_DIG_CTL_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5682s_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_VERSION_ID:
+ case RT5682S_VENDOR_ID:
+ case RT5682S_DEVICE_ID:
+ case RT5682S_HP_CTRL_1:
+ case RT5682S_HP_CTRL_2:
+ case RT5682S_HPL_GAIN:
+ case RT5682S_HPR_GAIN:
+ case RT5682S_I2C_CTRL:
+ case RT5682S_CBJ_BST_CTRL:
+ case RT5682S_CBJ_DET_CTRL:
+ case RT5682S_CBJ_CTRL_1...RT5682S_CBJ_CTRL_8:
+ case RT5682S_DAC1_DIG_VOL:
+ case RT5682S_STO1_ADC_DIG_VOL:
+ case RT5682S_STO1_ADC_BOOST:
+ case RT5682S_HP_IMP_GAIN_1:
+ case RT5682S_HP_IMP_GAIN_2:
+ case RT5682S_SIDETONE_CTRL:
+ case RT5682S_STO1_ADC_MIXER:
+ case RT5682S_AD_DA_MIXER:
+ case RT5682S_STO1_DAC_MIXER:
+ case RT5682S_A_DAC1_MUX:
+ case RT5682S_DIG_INF2_DATA:
+ case RT5682S_REC_MIXER:
+ case RT5682S_CAL_REC:
+ case RT5682S_HP_ANA_OST_CTRL_1...RT5682S_HP_ANA_OST_CTRL_3:
+ case RT5682S_PWR_DIG_1...RT5682S_PWR_MIXER:
+ case RT5682S_MB_CTRL:
+ case RT5682S_CLK_GATE_TCON_1...RT5682S_CLK_GATE_TCON_3:
+ case RT5682S_CLK_DET...RT5682S_LPF_AD_DMIC:
+ case RT5682S_I2S1_SDP:
+ case RT5682S_I2S2_SDP:
+ case RT5682S_ADDA_CLK_1:
+ case RT5682S_ADDA_CLK_2:
+ case RT5682S_I2S1_F_DIV_CTRL_1:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_TDM_CTRL:
+ case RT5682S_TDM_ADDA_CTRL_1:
+ case RT5682S_TDM_ADDA_CTRL_2:
+ case RT5682S_DATA_SEL_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_2:
+ case RT5682S_GLB_CLK:
+ case RT5682S_PLL_TRACK_1...RT5682S_PLL_TRACK_6:
+ case RT5682S_PLL_TRACK_11:
+ case RT5682S_DEPOP_1:
+ case RT5682S_HP_CHARGE_PUMP_1:
+ case RT5682S_HP_CHARGE_PUMP_2:
+ case RT5682S_HP_CHARGE_PUMP_3:
+ case RT5682S_MICBIAS_1...RT5682S_MICBIAS_3:
+ case RT5682S_PLL_TRACK_12...RT5682S_PLL_CTRL_7:
+ case RT5682S_RC_CLK_CTRL:
+ case RT5682S_I2S2_M_CLK_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_IRQ_CTRL_1...RT5682S_IRQ_CTRL_4:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_CTRL_1:
+ case RT5682S_GPIO_CTRL_2:
+ case RT5682S_GPIO_ST:
+ case RT5682S_HP_AMP_DET_CTRL_1:
+ case RT5682S_MID_HP_AMP_DET:
+ case RT5682S_LOW_HP_AMP_DET:
+ case RT5682S_DELAY_BUF_CTRL:
+ case RT5682S_SV_ZCD_1:
+ case RT5682S_SV_ZCD_2:
+ case RT5682S_IL_CMD_1...RT5682S_IL_CMD_6:
+ case RT5682S_4BTN_IL_CMD_1...RT5682S_4BTN_IL_CMD_7:
+ case RT5682S_ADC_STO1_HP_CTRL_1:
+ case RT5682S_ADC_STO1_HP_CTRL_2:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_JD_CTRL_1:
+ case RT5682S_DUMMY_1...RT5682S_DUMMY_3:
+ case RT5682S_DAC_ADC_DIG_VOL1:
+ case RT5682S_BIAS_CUR_CTRL_2...RT5682S_BIAS_CUR_CTRL_10:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_1:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_2:
+ case RT5682S_CHARGE_PUMP_1:
+ case RT5682S_DIG_IN_CTRL_1:
+ case RT5682S_PAD_DRIVING_CTRL:
+ case RT5682S_CHOP_DAC_1:
+ case RT5682S_CHOP_DAC_2:
+ case RT5682S_CHOP_ADC:
+ case RT5682S_CALIB_ADC_CTRL:
+ case RT5682S_VOL_TEST:
+ case RT5682S_SPKVDD_DET_ST:
+ case RT5682S_TEST_MODE_CTRL_1...RT5682S_TEST_MODE_CTRL_4:
+ case RT5682S_PLL_INTERNAL_1...RT5682S_PLL_INTERNAL_4:
+ case RT5682S_STO_NG2_CTRL_1...RT5682S_STO_NG2_CTRL_10:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_SIL_PSV_CTRL1:
+ case RT5682S_SIL_PSV_CTRL2:
+ case RT5682S_SIL_PSV_CTRL3:
+ case RT5682S_SIL_PSV_CTRL4:
+ case RT5682S_SIL_PSV_CTRL5:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_LOGIC_CTRL_1...RT5682S_HP_LOGIC_CTRL_3:
+ case RT5682S_HP_CALIB_CTRL_1...RT5682S_HP_CALIB_CTRL_11:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_1...RT5682S_SAR_IL_CMD_14:
+ case RT5682S_DUMMY_4...RT5682S_DUMMY_6:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_SCAN_CTL:
+ case RT5682S_HP_AMP_DET:
+ case RT5682S_BIAS_CUR_CTRL_11:
+ case RT5682S_BIAS_CUR_CTRL_12:
+ case RT5682S_BIAS_CUR_CTRL_13:
+ case RT5682S_BIAS_CUR_CTRL_14:
+ case RT5682S_BIAS_CUR_CTRL_15:
+ case RT5682S_BIAS_CUR_CTRL_16:
+ case RT5682S_BIAS_CUR_CTRL_17:
+ case RT5682S_BIAS_CUR_CTRL_18:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DUMMY_7:
+ case RT5682S_DUMMY_8:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1...RT5682S_HA_CMP_OP_13:
+ case RT5682S_HA_CMP_OP_14...RT5682S_HA_CMP_OP_25:
+ case RT5682S_NEW_CBJ_DET_CTL_1...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_DA_FILTER_1...RT5682S_DA_FILTER_5:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_CLK_SW_TEST_3...RT5682S_CLK_SW_TEST_14:
+ case RT5682S_EFUSE_MANU_WRITE_1...RT5682S_EFUSE_MANU_WRITE_6:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_EFUSE_TIMING_CTL_1:
+ case RT5682S_EFUSE_TIMING_CTL_2:
+ case RT5682S_PILOT_DIG_CTL_1:
+ case RT5682S_PILOT_DIG_CTL_2:
+ case RT5682S_HP_AMP_DET_CTL_1...RT5682S_HP_AMP_DET_CTL_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void rt5682s_reset(struct rt5682s_priv *rt5682s)
+{
+ regmap_write(rt5682s->regmap, RT5682S_RESET, 0);
+}
+
+static int rt5682s_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5682S_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5682S_4BTN_IL_CMD_1, val);
+ dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+
+ return btn_type;
+}
+
+enum {
+ SAR_PWR_OFF,
+ SAR_PWR_NORMAL,
+ SAR_PWR_SAVING,
+};
+
+static void rt5682s_sar_power_mode(struct snd_soc_component *component,
+ int mode, int jd_step)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5682s->sar_mutex);
+
+ switch (mode) {
+ case SAR_PWR_SAVING:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_REG | RT5682S_CTRL_MB2_REG);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK, RT5682S_SAR_BUTDET_EN);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+ break;
+ case SAR_PWR_NORMAL:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM);
+ if (!jd_step) {
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK,
+ RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM);
+ }
+ break;
+ case SAR_PWR_OFF:
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU);
+ break;
+ default:
+ dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&rt5682s->sar_mutex);
+}
+
+static void rt5682s_enable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_BTN);
+ snd_soc_component_write(component, RT5682S_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK | RT5682S_4BTN_IL_RST_MASK,
+ RT5682S_4BTN_IL_EN | RT5682S_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_EN);
+}
+
+static void rt5682s_disable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+}
+
+/**
+ * rt5682s_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ unsigned int val, count;
+ int jack_type = 0;
+
+ if (jack_insert) {
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, RT5682S_PWR_CBJ);
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x0365);
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_DIS | RT5682S_OSW_R_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+ rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 1);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+ usleep_range(45000, 50000);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_HIGH);
+
+ count = 0;
+ do {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component, RT5682S_CBJ_CTRL_2)
+ & RT5682S_JACK_TYPE_MASK;
+ count++;
+ } while (val == 0 && count < 50);
+
+ dev_dbg(component->dev, "%s, val=%d, count=%d\n", __func__, val, count);
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ jack_type = SND_JACK_HEADSET;
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x024c);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_EN);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_MASK, val << RT5682S_SAR_SEL_MB1_2_SFT);
+ if (!snd_soc_dapm_get_pin_status(&component->dapm, "SAR"))
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING, 1);
+ rt5682s_enable_push_button_irq(component);
+ break;
+ default:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_EN | RT5682S_OSW_R_EN);
+ usleep_range(35000, 40000);
+ } else {
+ rt5682s_sar_power_mode(component, SAR_PWR_OFF, 1);
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+
+ if (!snd_soc_dapm_get_pin_status(&component->dapm, "MICBIAS"))
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(&component->dapm, "Vref2"))
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2, 0);
+
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, 0);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS);
+ jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", jack_type);
+
+ return jack_type;
+}
+
+static void rt5682s_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jack_detect_work.work);
+ int val, btn_type;
+
+ while (!rt5682s->component)
+ usleep_range(10000, 15000);
+
+ while (!rt5682s->component->card->instantiated)
+ usleep_range(10000, 15000);
+
+ mutex_lock(&rt5682s->jdet_mutex);
+ mutex_lock(&rt5682s->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
+ & RT5682S_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5682s->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 1);
+ rt5682s->irq_work_delay_time = 0;
+ } else if ((rt5682s->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ rt5682s->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5682s_button_detect(rt5682s->component);
+ /**
+ * rt5682s can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5682s->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5682s->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5682s->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5682s->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5682s->component->dev,
+ "Unexpected button code 0x%04x\n", btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0);
+ rt5682s->irq_work_delay_time = 50;
+ }
+
+ snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5682s->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5682s->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ mutex_unlock(&rt5682s->calibrate_mutex);
+ mutex_unlock(&rt5682s->jdet_mutex);
+}
+
+static void rt5682s_jd_check_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jd_check_work.work);
+
+ if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
+ & RT5682S_JDH_RS_MASK) {
+ /* jack out */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0);
+
+ snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ schedule_delayed_work(&rt5682s->jd_check_work, 500);
+ }
+}
+
+static irqreturn_t rt5682s_irq(int irq, void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+
+ mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work,
+ msecs_to_jiffies(rt5682s->irq_work_delay_time));
+
+ return IRQ_HANDLED;
+}
+
+static int rt5682s_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int btndet_delay = 16;
+
+ rt5682s->hs_jack = hs_jack;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+
+ return 0;
+ }
+
+ switch (rt5682s->pdata.jd_src) {
+ case RT5682S_JD1:
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_5,
+ RT5682S_JD_FAST_OFF_SRC_MASK, RT5682S_JD_FAST_OFF_SRC_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_2,
+ RT5682S_EXT_JD_SRC, RT5682S_EXT_JD_SRC_MANUAL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_1,
+ RT5682S_EMB_JD_MASK | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_MASK | RT5682S_MIC_CAP_MASK,
+ RT5682S_EMB_JD_EN | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_HIGH | RT5682S_MIC_CAP_HS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_POW_MASK, RT5682S_SAR_POW_EN);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_IRQ);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BGLDO, RT5682S_PWR_BGLDO);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_JD_MASK, RT5682S_PWR_JD_ENABLE);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_IRQ | RT5682S_POW_JDH, RT5682S_POW_IRQ | RT5682S_POW_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK | RT5682S_JD1_POL_MASK,
+ RT5682S_JD1_EN | RT5682S_JD1_POL_NOR);
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_4,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_5,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_6,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_7,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(250));
+ break;
+
+ case RT5682S_JD_NULL:
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9562, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+static const DECLARE_TLV_DB_SCALE(cbj_bst_tlv, -1200, 150, 0);
+
+static const struct snd_kcontrol_new rt5682s_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682S_DAC1_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 127, 0, dac_vol_tlv),
+
+ /* CBJ Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682S_REC_MIXER,
+ RT5682S_BST_CBJ_SFT, 35, 0, cbj_bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE_SFT, RT5682S_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682S_STO1_ADC_BOOST,
+ RT5682S_STO1_ADC_L_BST_SFT, RT5682S_STO1_ADC_R_BST_SFT, 3, 0, adc_bst_tlv),
+};
+
+/**
+ * rt5682s_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @component: SoC audio component device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682S can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the component driver will turn on
+ * ASRC for these filters if ASRC is selected as their clock source.
+ */
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ switch (clk_src) {
+ case RT5682S_CLK_SEL_SYS:
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5682S_DA_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_2,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5682S_AD_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_3,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_11,
+ RT5682S_ASRCIN_AUTO_CLKOUT_MASK, RT5682S_ASRCIN_AUTO_CLKOUT_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682s_sel_asrc_clk_src);
+
+static int rt5682s_div_sel(struct rt5682s_priv *rt5682s,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5682s->sysclk < target) {
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too low\n", rt5682s->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ dev_dbg(rt5682s->component->dev, "div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5682s->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5682s->sysclk) {
+ dev_dbg(rt5682s->component->dev,
+ "can't find div for sysclk %d\n", rt5682s->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5682s->sysclk)
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too high\n", rt5682s->sysclk);
+
+ return size - 1;
+}
+
+static int get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int idx, dmic_clk_rate = 3072000;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ if (rt5682s->pdata.dmic_clk_rate)
+ dmic_clk_rate = rt5682s->pdata.dmic_clk_rate;
+
+ idx = rt5682s_div_sel(rt5682s, dmic_clk_rate, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_CLK_MASK, idx << RT5682S_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+static int set_filter_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ref, val, reg, idx;
+ static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+ static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ val = snd_soc_component_read(component, RT5682S_GPIO_CTRL_1)
+ & RT5682S_GP4_PIN_MASK;
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT && val == RT5682S_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5682s->lrck[RT5682S_AIF2];
+ else
+ ref = 256 * rt5682s->lrck[RT5682S_AIF1];
+
+ idx = rt5682s_div_sel(rt5682s, ref, div_f, ARRAY_SIZE(div_f));
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT)
+ reg = RT5682S_PLL_TRACK_3;
+ else
+ reg = RT5682S_PLL_TRACK_2;
+
+ snd_soc_component_update_bits(component, reg,
+ RT5682S_FILTER_CLK_DIV_MASK, idx << RT5682S_FILTER_CLK_DIV_SFT);
+
+ /* select over sample rate */
+ for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) {
+ if (rt5682s->sysclk <= 12288000 * div_o[idx])
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_ADC_OSR_MASK | RT5682S_DAC_OSR_MASK,
+ (idx << RT5682S_ADC_OSR_SFT) | (idx << RT5682S_DAC_OSR_SFT));
+
+ return 0;
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 50, val;
+
+ if (rt5682s->pdata.dmic_delay)
+ delay = rt5682s->pdata.dmic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = (snd_soc_component_read(component, RT5682S_GLB_CLK)
+ & RT5682S_SCLK_SRC_MASK) >> RT5682S_SCLK_SRC_SFT;
+ if (val == RT5682S_CLK_SRC_PLL1 || val == RT5682S_CLK_SRC_PLL2)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+
+ /*Add delay to avoid pop noise*/
+ msleep(delay);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (!rt5682s->jack_type) {
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS"))
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2"))
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int set_i2s_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int pre_div, id;
+ unsigned int reg, mask, sft;
+
+ if (event != SND_SOC_DAPM_PRE_PMU)
+ return 0;
+
+ if (w->shift == RT5682S_PWR_I2S2_BIT) {
+ id = RT5682S_AIF2;
+ reg = RT5682S_I2S2_M_CLK_CTRL_1;
+ mask = RT5682S_I2S2_M_D_MASK;
+ sft = RT5682S_I2S2_M_D_SFT;
+ } else {
+ id = RT5682S_AIF1;
+ reg = RT5682S_ADDA_CLK_1;
+ mask = RT5682S_I2S_M_D_MASK;
+ sft = RT5682S_I2S_M_D_SFT;
+ }
+
+ if (!rt5682s->master[id])
+ return 0;
+
+ pre_div = get_clk_info(rt5682s->sysclk, rt5682s->lrck[id]);
+ if (pre_div < 0) {
+ dev_err(component->dev, "get pre_div failed\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d master\n",
+ rt5682s->lrck[id], pre_div, id);
+ snd_soc_component_update_bits(component, reg, mask, pre_div << sft);
+
+ return 0;
+}
+
+static int is_sys_clk_from_plla(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL1) ||
+ (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2 && rt5682s->pll_comb == USE_PLLAB))
+ return 1;
+
+ return 0;
+}
+
+static int is_sys_clk_from_pllb(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2)
+ return 1;
+
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, sft, val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5682S_ADC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_3;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5682S_DAC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_2;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> sft) & 0xf;
+ switch (val) {
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a);
+
+ mutex_lock(&rt5682s->jdet_mutex);
+
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN |
+ RT5682S_HPO_R_PATH_EN | RT5682S_HPO_IP_EN_GATING);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S);
+
+ mutex_unlock(&rt5682s->jdet_mutex);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, 0);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int sar_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->jack_type & SND_JACK_HEADSET) != SND_JACK_HEADSET)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Interface data select */
+static const char * const rt5682s_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if2_adc_enum, RT5682S_DIG_INF2_DATA,
+ RT5682S_IF2_ADC_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_01_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC1_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_23_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC2_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_45_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC3_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_67_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC4_SEL_SFT, rt5682s_data_select);
+
+static const struct snd_kcontrol_new rt5682s_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682s_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682s_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682s_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682s_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682s_if1_67_adc_enum);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5682s_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5682s_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_rec1_r_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_R_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5682s_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1L_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1R_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5682s_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcl_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCL_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682s_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcr_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCR_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682s_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5682s_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2L_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682s_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2R_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682s_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5682s_if1_adc_slot_values[] = {
+ 0, 2, 4, 6,
+};
+
+static const char * const rt5682s_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_if1_adc_slot_enum,
+ RT5682S_TDM_CTRL, RT5682S_TDM_ADC_LCA_SFT, RT5682S_TDM_ADC_LCA_MASK,
+ rt5682s_if1_adc_slot_src, rt5682s_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5682s_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682s_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5682s_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_l1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACL1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682s_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_r1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACR1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682s_alg_dac_r1_enum);
+
+static const unsigned int rt5682s_adcdat_pin_values[] = {
+ 1, 3,
+};
+
+static const char * const rt5682s_adcdat_pin_select[] = {
+ "ADCDAT1", "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_adcdat_pin_enum,
+ RT5682S_GPIO_CTRL_1, RT5682S_GP4_PIN_SFT, RT5682S_GP4_PIN_MASK,
+ rt5682s_adcdat_pin_select, rt5682s_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5682s_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5682s_adcdat_pin_enum);
+
+static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO MB1", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO MB2", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* PLL Powers */
+ SND_SOC_DAPM_SUPPLY_S("PLLA_LDO", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLB_LDO", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLB_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_BIAS", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BIAS_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLB_BIAS", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BIAS_PLLB_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLB", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_PLLB_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_RST", 1, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLB_RST", 1, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLB_BIT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB2_BIT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682S_DMIC_CTRL_1, RT5682S_DMIC_1_EN_SFT, 0,
+ set_dmic_power, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682s_rec1_l_mix,
+ ARRAY_SIZE(rt5682s_rec1_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5682s_rec1_r_mix,
+ ARRAY_SIZE(rt5682s_rec1_r_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_R_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682S_CHOP_ADC,
+ RT5682S_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE_SFT, 1, rt5682s_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5682s_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5682s_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5682S_PWR_DIG_1, RT5682S_PWR_I2S1_BIT,
+ 0, set_i2s_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5682S_PWR_DIG_1, RT5682S_PWR_I2S2_BIT,
+ 0, set_i2s_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682s_adcdat_pin_ctrl),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682S_I2S1_SDP,
+ RT5682S_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_l_mix, ARRAY_SIZE(rt5682s_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_r_mix, ARRAY_SIZE(rt5682s_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_DAC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_l_mix, ARRAY_SIZE(rt5682s_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_r_mix, ARRAY_SIZE(rt5682s_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_R1_BIT, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682s_hp_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682S_CLK_DET,
+ RT5682S_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682S_CLK_DET,
+ RT5682S_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MCLK0 DET PWR", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MCLK0_WD_BIT, 0, NULL, 0),
+
+ /* SAR */
+ SND_SOC_DAPM_SUPPLY("SAR", SND_SOC_NOPM, 0, 0, sar_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5682s_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"ADC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"DAC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"DAC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"PLLA", NULL, "PLLA_LDO"},
+ {"PLLA", NULL, "PLLA_BIAS"},
+ {"PLLA", NULL, "PLLA_RST"},
+ {"PLLB", NULL, "PLLB_LDO"},
+ {"PLLB", NULL, "PLLB_BIAS"},
+ {"PLLB", NULL, "PLLB_RST"},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"ADC STO1 ASRC", NULL, "DA ASRC"},
+ {"DAC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+
+ {"CLKDET SYS", NULL, "MCLK0 DET PWR"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "SAR"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+ {"RECMIX1R", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1R", NULL, "RECMIX1R Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+ {"ADC1 R", NULL, "RECMIX1R"},
+ {"ADC1 R", NULL, "ADC1 R Power"},
+ {"ADC1 R", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "I2S1"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "SAR"},
+
+ {"HPOL", NULL, "HP Amp"},
+ {"HPOR", NULL, "HP Amp"},
+};
+
+static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int cl, val = 0;
+
+ if (tx_mask || rx_mask)
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, RT5682S_TDM_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, 0);
+
+ switch (slots) {
+ case 4:
+ val |= RT5682S_TDM_TX_CH_4;
+ val |= RT5682S_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5682S_TDM_TX_CH_6;
+ val |= RT5682S_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5682S_TDM_TX_CH_8;
+ val |= RT5682S_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_CTRL,
+ RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK, val);
+
+ switch (slot_width) {
+ case 8:
+ if (tx_mask || rx_mask)
+ return -EINVAL;
+ cl = RT5682S_I2S1_TX_CHL_8 | RT5682S_I2S1_RX_CHL_8;
+ break;
+ case 16:
+ val = RT5682S_TDM_CL_16;
+ cl = RT5682S_I2S1_TX_CHL_16 | RT5682S_I2S1_RX_CHL_16;
+ break;
+ case 20:
+ val = RT5682S_TDM_CL_20;
+ cl = RT5682S_I2S1_TX_CHL_20 | RT5682S_I2S1_RX_CHL_20;
+ break;
+ case 24:
+ val = RT5682S_TDM_CL_24;
+ cl = RT5682S_I2S1_TX_CHL_24 | RT5682S_I2S1_RX_CHL_24;
+ break;
+ case 32:
+ val = RT5682S_TDM_CL_32;
+ cl = RT5682S_I2S1_TX_CHL_32 | RT5682S_I2S1_RX_CHL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_CL_MASK, val);
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_TX_CHL_MASK | RT5682S_I2S1_RX_CHL_MASK, cl);
+
+ return 0;
+}
+
+static int rt5682s_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int frame_size;
+
+ rt5682s->lrck[dai->id] = params_rate(params);
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5682S_I2S1_DL_20;
+ len_2 |= RT5682S_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5682S_I2S1_DL_24;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5682S_I2S1_DL_32;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5682S_I2S2_DL_8;
+ len_2 |= RT5682S_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_DL_MASK, len_1);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_DIS);
+ break;
+ case RT5682S_AIF2:
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_DL_MASK, len_2);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5682s->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ rt5682s->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5682S_I2S_BP_INV;
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_LP_INV | RT5682S_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV | RT5682S_TDM_S_LP_INV |
+ RT5682S_TDM_M_BP_INV | RT5682S_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5682S_I2S_DF_LEFT;
+ tdm_ctrl |= RT5682S_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5682S_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5682S_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_MS_MASK | RT5682S_TDM_S_BP_MASK |
+ RT5682S_TDM_DF_MASK | RT5682S_TDM_M_BP_MASK |
+ RT5682S_TDM_M_LP_MASK | RT5682S_TDM_S_LP_MASK,
+ tdm_ctrl | rt5682s->master[dai->id]);
+ break;
+ case RT5682S_AIF2:
+ if (rt5682s->master[dai->id] == 0)
+ reg_val |= RT5682S_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MS_MASK | RT5682S_I2S_BP_MASK |
+ RT5682S_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5682s_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int src = 0;
+
+ if (freq == rt5682s->sysclk && clk_id == rt5682s->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5682S_SCLK_S_MCLK:
+ src = RT5682S_CLK_SRC_MCLK;
+ break;
+ case RT5682S_SCLK_S_PLL1:
+ src = RT5682S_CLK_SRC_PLL1;
+ break;
+ case RT5682S_SCLK_S_PLL2:
+ src = RT5682S_CLK_SRC_PLL2;
+ break;
+ case RT5682S_SCLK_S_RCCLK:
+ src = RT5682S_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, src << RT5682S_SCLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_I2S_M_CLK_SRC_MASK, src << RT5682S_I2S_M_CLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_I2S2_M_CLK_CTRL_1,
+ RT5682S_I2S2_M_CLK_SRC_MASK, src << RT5682S_I2S2_M_CLK_SRC_SFT);
+
+ rt5682s->sysclk = freq;
+ rt5682s->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static const struct pll_calc_map plla_table[] = {
+ {2048000, 24576000, 0, 46, 2, true, false, false, false},
+ {256000, 24576000, 0, 382, 2, true, false, false, false},
+ {512000, 24576000, 0, 190, 2, true, false, false, false},
+ {4096000, 24576000, 0, 22, 2, true, false, false, false},
+ {1024000, 24576000, 0, 94, 2, true, false, false, false},
+ {11289600, 22579200, 1, 22, 2, false, false, false, false},
+ {1411200, 22579200, 0, 62, 2, true, false, false, false},
+ {2822400, 22579200, 0, 30, 2, true, false, false, false},
+ {12288000, 24576000, 1, 22, 2, false, false, false, false},
+ {1536000, 24576000, 0, 62, 2, true, false, false, false},
+ {3072000, 24576000, 0, 30, 2, true, false, false, false},
+ {24576000, 49152000, 4, 22, 0, false, false, false, false},
+ {3072000, 49152000, 0, 30, 0, true, false, false, false},
+ {6144000, 49152000, 0, 30, 0, false, false, false, false},
+ {49152000, 98304000, 10, 22, 0, false, true, false, false},
+ {6144000, 98304000, 0, 30, 0, false, true, false, false},
+ {12288000, 98304000, 1, 22, 0, false, true, false, false},
+ {48000000, 3840000, 10, 22, 23, false, false, false, false},
+ {24000000, 3840000, 4, 22, 23, false, false, false, false},
+ {19200000, 3840000, 3, 23, 23, false, false, false, false},
+ {38400000, 3840000, 8, 23, 23, false, false, false, false},
+};
+
+static const struct pll_calc_map pllb_table[] = {
+ {48000000, 24576000, 8, 6, 3, false, false, false, false},
+ {48000000, 22579200, 23, 12, 3, false, false, false, true},
+ {24000000, 24576000, 3, 6, 3, false, false, false, false},
+ {24000000, 22579200, 23, 26, 3, false, false, false, true},
+ {19200000, 24576000, 2, 6, 3, false, false, false, false},
+ {19200000, 22579200, 3, 5, 3, false, false, false, true},
+ {38400000, 24576000, 6, 6, 3, false, false, false, false},
+ {38400000, 22579200, 8, 5, 3, false, false, false, true},
+ {3840000, 49152000, 0, 6, 0, true, false, false, false},
+};
+
+static int find_pll_inter_combination(unsigned int f_in, unsigned int f_out,
+ struct pll_calc_map *a, struct pll_calc_map *b)
+{
+ int i, j;
+
+ /* Look at PLLA table */
+ for (i = 0; i < ARRAY_SIZE(plla_table); i++) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ return USE_PLLA;
+ }
+ }
+
+ /* Look at PLLB table */
+ for (i = 0; i < ARRAY_SIZE(pllb_table); i++) {
+ if (pllb_table[i].freq_in == f_in && pllb_table[i].freq_out == f_out) {
+ memcpy(b, pllb_table + i, sizeof(*b));
+ return USE_PLLB;
+ }
+ }
+
+ /* Find a combination of PLLA & PLLB */
+ for (i = ARRAY_SIZE(plla_table) - 1; i >= 0; i--) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == 3840000) {
+ for (j = ARRAY_SIZE(pllb_table) - 1; j >= 0; j--) {
+ if (pllb_table[j].freq_in == 3840000 &&
+ pllb_table[j].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ memcpy(b, pllb_table + j, sizeof(*b));
+ return USE_PLLAB;
+ }
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int rt5682s_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct pll_calc_map a_map, b_map;
+
+ if (source == rt5682s->pll_src[pll_id] && freq_in == rt5682s->pll_in[pll_id] &&
+ freq_out == rt5682s->pll_out[pll_id])
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt5682s->pll_in[pll_id] = 0;
+ rt5682s->pll_out[pll_id] = 0;
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, RT5682S_CLK_SRC_MCLK << RT5682S_SCLK_SRC_SFT);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5682S_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_MCLK);
+ break;
+ case RT5682S_PLL_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ rt5682s->pll_comb = find_pll_inter_combination(freq_in, freq_out,
+ &a_map, &b_map);
+
+ if ((pll_id == RT5682S_PLL1 && rt5682s->pll_comb == USE_PLLA) ||
+ (pll_id == RT5682S_PLL2 && (rt5682s->pll_comb == USE_PLLB ||
+ rt5682s->pll_comb == USE_PLLAB))) {
+ dev_dbg(component->dev,
+ "Supported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ } else {
+ dev_err(component->dev,
+ "Unsupported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ return -EINVAL;
+ }
+
+ if (rt5682s->pll_comb == USE_PLLA || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLA: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d\n",
+ a_map.freq_in, a_map.freq_out, a_map.m_bp, a_map.k_bp,
+ (a_map.m_bp ? 0 : a_map.m), a_map.n, (a_map.k_bp ? 0 : a_map.k));
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_1,
+ RT5682S_PLLA_N_MASK, a_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_2,
+ RT5682S_PLLA_M_MASK | RT5682S_PLLA_K_MASK,
+ a_map.m << RT5682S_PLLA_M_SFT | a_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLA_M_BP_MASK | RT5682S_PLLA_K_BP_MASK,
+ a_map.m_bp << RT5682S_PLLA_M_BP_SFT |
+ a_map.k_bp << RT5682S_PLLA_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLB: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d byp_ps=%d sel_ps=%d\n",
+ b_map.freq_in, b_map.freq_out, b_map.m_bp, b_map.k_bp,
+ (b_map.m_bp ? 0 : b_map.m), b_map.n, (b_map.k_bp ? 0 : b_map.k),
+ b_map.byp_ps, b_map.sel_ps);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_3,
+ RT5682S_PLLB_N_MASK, b_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_4,
+ RT5682S_PLLB_M_MASK | RT5682S_PLLB_K_MASK,
+ b_map.m << RT5682S_PLLB_M_SFT | b_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLB_SEL_PS_MASK | RT5682S_PLLB_BYP_PS_MASK |
+ RT5682S_PLLB_M_BP_MASK | RT5682S_PLLB_K_BP_MASK,
+ b_map.sel_ps << RT5682S_PLLB_SEL_PS_SFT |
+ b_map.byp_ps << RT5682S_PLLB_BYP_PS_SFT |
+ b_map.m_bp << RT5682S_PLLB_M_BP_SFT |
+ b_map.k_bp << RT5682S_PLLB_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB)
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_7,
+ RT5682S_PLLB_SRC_MASK, RT5682S_PLLB_SRC_DFIN);
+
+ rt5682s->pll_in[pll_id] = freq_in;
+ rt5682s->pll_out[pll_id] = freq_out;
+ rt5682s->pll_src[pll_id] = source;
+
+ return 0;
+}
+
+static int rt5682s_set_bclk1_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 256:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_256);
+ break;
+ case 128:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_128);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_PWR_LDO, RT5682S_PWR_LDO);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL | RT5682S_PWR_LDO, 0);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+#define CLK_PLL2_FIN 48000000
+#define CLK_48 48000
+#define CLK_44 44100
+
+static bool rt5682s_clk_check(struct rt5682s_priv *rt5682s)
+{
+ if (!rt5682s->master[RT5682S_AIF1]) {
+ dev_dbg(rt5682s->component->dev, "dai clk fmt not set correctly\n");
+ return false;
+ }
+ return true;
+}
+
+static int rt5682s_wclk_prepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_MB, RT5682S_PWR_MB);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Vref2");
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2, RT5682S_PWR_VREF2);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV2, RT5682S_PWR_FV2);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1");
+ /* Only need to power PLLB due to the rate set restriction */
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLLB");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void rt5682s_wclk_unprepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Vref2");
+ if (!rt5682s->jack_type)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, 0);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLLB");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return 0;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ */
+ if (rt5682s->lrck[RT5682S_AIF1] != CLK_48 &&
+ rt5682s->lrck[RT5682S_AIF1] != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682s->lrck[RT5682S_AIF1];
+}
+
+static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
+ */
+ if (rate != CLK_48 && rate != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ rate = CLK_48;
+ }
+
+ return rate;
+}
+
+static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct clk *parent_clk;
+ const char * const clk_name = clk_hw_get_name(hw);
+ unsigned int clk_pll2_fout;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * Whether the wclk's parent clk (mclk) exists or not, please ensure
+ * it is fixed or set to 48MHz before setting wclk rate. It's a
+ * temporary limitation. Only accept 48MHz clk as the clk provider.
+ *
+ * It will set the codec anyway by assuming mclk is 48MHz.
+ */
+ parent_clk = clk_get_parent(hw->clk);
+ if (!parent_clk)
+ dev_warn(component->dev,
+ "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
+ CLK_PLL2_FIN);
+
+ if (parent_rate != CLK_PLL2_FIN)
+ dev_warn(component->dev, "clk %s only support %d Hz input\n",
+ clk_name, CLK_PLL2_FIN);
+
+ /*
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
+ */
+ clk_pll2_fout = rate * 512;
+ rt5682s_set_component_pll(component, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ CLK_PLL2_FIN, clk_pll2_fout);
+
+ rt5682s_set_component_sysclk(component, RT5682S_SCLK_S_PLL2, 0,
+ clk_pll2_fout, SND_SOC_CLOCK_IN);
+
+ rt5682s->lrck[RT5682S_AIF1] = rate;
+
+ return 0;
+}
+
+static unsigned long rt5682s_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ unsigned int bclks_per_wclk;
+
+ bclks_per_wclk = snd_soc_component_read(component, RT5682S_TDM_TCON_CTRL_1);
+
+ switch (bclks_per_wclk & RT5682S_TDM_BCLK_MS1_MASK) {
+ case RT5682S_TDM_BCLK_MS1_256:
+ return parent_rate * 256;
+ case RT5682S_TDM_BCLK_MS1_128:
+ return parent_rate * 128;
+ case RT5682S_TDM_BCLK_MS1_64:
+ return parent_rate * 64;
+ case RT5682S_TDM_BCLK_MS1_32:
+ return parent_rate * 32;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long rt5682s_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
+ else
+ return 256;
+}
+
+static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!*parent_rate || !rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * BCLK rates are set as a multiplier of WCLK in HW.
+ * We don't allow changing the parent WCLK. We just do
+ * some rounding down based on the parent WCLK rate
+ * and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = rt5682s_bclk_get_factor(rate, *parent_rate);
+
+ return *parent_rate * factor;
+}
+
+static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct snd_soc_dai *dai;
+ unsigned long factor;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ factor = rt5682s_bclk_get_factor(rate, parent_rate);
+
+ for_each_component_dais(component, dai)
+ if (dai->id == RT5682S_AIF1)
+ break;
+ if (!dai) {
+ dev_err(component->dev, "dai %d not found in component\n",
+ RT5682S_AIF1);
+ return -ENODEV;
+ }
+
+ return rt5682s_set_bclk1_ratio(dai, factor);
+}
+
+static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = {
+ [RT5682S_DAI_WCLK_IDX] = {
+ .prepare = rt5682s_wclk_prepare,
+ .unprepare = rt5682s_wclk_unprepare,
+ .recalc_rate = rt5682s_wclk_recalc_rate,
+ .round_rate = rt5682s_wclk_round_rate,
+ .set_rate = rt5682s_wclk_set_rate,
+ },
+ [RT5682S_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682s_bclk_recalc_rate,
+ .round_rate = rt5682s_bclk_round_rate,
+ .set_rate = rt5682s_bclk_set_rate,
+ },
+};
+
+static int rt5682s_register_dai_clks(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct rt5682s_platform_data *pdata = &rt5682s->pdata;
+ struct clk_hw *dai_clk_hw;
+ int i, ret;
+
+ for (i = 0; i < RT5682S_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = { };
+
+ dai_clk_hw = &rt5682s->dai_clks_hw[i];
+
+ switch (i) {
+ case RT5682S_DAI_WCLK_IDX:
+ /* Make MCLK the parent of WCLK */
+ if (rt5682s->mclk) {
+ init.parent_data = &(struct clk_parent_data){
+ .fw_name = "mclk",
+ };
+ init.num_parents = 1;
+ }
+ break;
+ case RT5682S_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ init.parent_hws = &(const struct clk_hw *){
+ &rt5682s->dai_clks_hw[RT5682S_DAI_WCLK_IDX]
+ };
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ return -EINVAL;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &rt5682s_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n", init.name, ret);
+ return ret;
+ }
+
+ if (dev->of_node) {
+ devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw);
+ } else {
+ ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
+ init.name, dev_name(dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Check if MCLK provided */
+ rt5682s->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(rt5682s->mclk)) {
+ if (PTR_ERR(rt5682s->mclk) != -ENOENT) {
+ ret = PTR_ERR(rt5682s->mclk);
+ return ret;
+ }
+ rt5682s->mclk = NULL;
+ }
+
+ /* Register CCF DAI clock control */
+ ret = rt5682s_register_dai_clks(component);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682s->lrck[RT5682S_AIF1] = CLK_48;
+
+ return 0;
+}
+#else
+static inline int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+static int rt5682s_probe(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = &component->dapm;
+ int ret;
+
+ rt5682s->component = component;
+
+ ret = rt5682s_dai_probe_clks(component);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(dapm, "Vref2");
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static void rt5682s_remove(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s_reset(rt5682s);
+}
+
+#ifdef CONFIG_PM
+static int rt5682s_suspend(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ if (rt5682s->hs_jack && rt5682s->jack_type == SND_JACK_HEADSET)
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS);
+
+ regcache_cache_only(rt5682s->regmap, true);
+ regcache_mark_dirty(rt5682s->regmap);
+
+ return 0;
+}
+
+static int rt5682s_resume(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5682s->regmap, false);
+ regcache_sync(rt5682s->regmap);
+
+ if (rt5682s->hs_jack) {
+ rt5682s->jack_type = 0;
+ rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 0);
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(0));
+ }
+
+ return 0;
+}
+#else
+#define rt5682s_suspend NULL
+#define rt5682s_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops rt5682s_aif1_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_tdm_slot = rt5682s_set_tdm_slot,
+ .set_bclk_ratio = rt5682s_set_bclk1_ratio,
+};
+
+static const struct snd_soc_dai_ops rt5682s_aif2_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_bclk_ratio = rt5682s_set_bclk2_ratio,
+};
+
+static const struct snd_soc_component_driver rt5682s_soc_component_dev = {
+ .probe = rt5682s_probe,
+ .remove = rt5682s_remove,
+ .suspend = rt5682s_suspend,
+ .resume = rt5682s_resume,
+ .set_bias_level = rt5682s_set_bias_level,
+ .controls = rt5682s_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5682s_snd_controls),
+ .dapm_widgets = rt5682s_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5682s_dapm_widgets),
+ .dapm_routes = rt5682s_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5682s_dapm_routes),
+ .set_sysclk = rt5682s_set_component_sysclk,
+ .set_pll = rt5682s_set_component_pll,
+ .set_jack = rt5682s_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5682s->pdata.dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic1-clk-pin",
+ &rt5682s->pdata.dmic1_clk_pin);
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt5682s->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,dmic-clk-rate-hz",
+ &rt5682s->pdata.dmic_clk_rate);
+ device_property_read_u32(dev, "realtek,dmic-delay-ms",
+ &rt5682s->pdata.dmic_delay);
+
+ rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
+ "realtek,ldo1-en-gpios", 0);
+
+ if (device_property_read_string_array(dev, "clock-output-names",
+ rt5682s->pdata.dai_clk_names,
+ RT5682S_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_WCLK_IDX],
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_BCLK_IDX]);
+
+ rt5682s->pdata.dmic_clk_driving_high = device_property_read_bool(dev,
+ "realtek,dmic-clk-driving-high");
+
+ return 0;
+}
+
+static void rt5682s_calibrate(struct rt5682s_priv *rt5682s)
+{
+ unsigned int count, value;
+
+ mutex_lock(&rt5682s->calibrate_mutex);
+
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80);
+ usleep_range(15000, 20000);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xfa80);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x01c0);
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0380);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x8000);
+ regmap_write(rt5682s->regmap, RT5682S_ADDA_CLK_1, 0x1001);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_DAC_2, 0x3030);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_ADC, 0xb000);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5151);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5682s->regmap, RT5682S_HP_CALIB_ST_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ dev_err(rt5682s->component->dev, "HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0180);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5858);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0320);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000);
+
+ mutex_unlock(&rt5682s->calibrate_mutex);
+}
+
+static const struct regmap_config rt5682s_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682S_MAX_REG,
+ .volatile_reg = rt5682s_volatile_register,
+ .readable_reg = rt5682s_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5682s_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5682s_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static struct snd_soc_dai_driver rt5682s_dai[] = {
+ {
+ .name = "rt5682s-aif1",
+ .id = RT5682S_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682s-aif2",
+ .id = RT5682S_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif2_dai_ops,
+ },
+};
+
+static void rt5682s_i2c_disable_regulators(void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+}
+
+static int rt5682s_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5682s_priv *rt5682s;
+ int i, ret;
+ unsigned int val;
+
+ rt5682s = devm_kzalloc(&i2c->dev, sizeof(struct rt5682s_priv), GFP_KERNEL);
+ if (!rt5682s)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5682s);
+
+ rt5682s->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt5682s->pdata = *pdata;
+ else
+ rt5682s_parse_dt(rt5682s, &i2c->dev);
+
+ rt5682s->regmap = devm_regmap_init_i2c(i2c, &rt5682s_regmap);
+ if (IS_ERR(rt5682s->regmap)) {
+ ret = PTR_ERR(rt5682s->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5682s->supplies); i++)
+ rt5682s->supplies[i].supply = rt5682s_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, rt5682s_i2c_disable_regulators, rt5682s);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (gpio_is_valid(rt5682s->pdata.ldo1_en)) {
+ if (devm_gpio_request_one(&i2c->dev, rt5682s->pdata.ldo1_en,
+ GPIOF_OUT_INIT_HIGH, "rt5682s"))
+ dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
+ }
+
+ /* Sleep for 50 ms minimum */
+ usleep_range(50000, 55000);
+
+ regmap_read(rt5682s->regmap, RT5682S_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev, "Device with ID register %x is not rt5682s\n", val);
+ return -ENODEV;
+ }
+
+ rt5682s_reset(rt5682s);
+ rt5682s_apply_patch_list(rt5682s, &i2c->dev);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_2,
+ RT5682S_DLDO_I_LIMIT_MASK, RT5682S_DLDO_I_LIMIT_DIS);
+ usleep_range(20000, 25000);
+
+ mutex_init(&rt5682s->calibrate_mutex);
+ mutex_init(&rt5682s->sar_mutex);
+ mutex_init(&rt5682s->jdet_mutex);
+ rt5682s_calibrate(rt5682s);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2,
+ RT5682S_PWR_CLK25M_MASK | RT5682S_PWR_CLK1M_MASK,
+ RT5682S_PWR_CLK25M_PD | RT5682S_PWR_CLK1M_PU);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_BG, RT5682S_PWR_BG);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2,
+ RT5682S_HP_SIG_SRC_MASK, RT5682S_HP_SIG_SRC_1BIT_CTL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_PM_HP_MASK, RT5682S_PM_HP_HV);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+
+ /* DMIC data pin */
+ switch (rt5682s->pdata.dmic1_data_pin) {
+ case RT5682S_DMIC1_DATA_NULL:
+ break;
+ case RT5682S_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP2_PIN_MASK, RT5682S_GP2_PIN_DMIC_SDA);
+ break;
+ case RT5682S_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP5_PIN_MASK, RT5682S_GP5_PIN_DMIC_SDA);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ /* DMIC clk pin */
+ switch (rt5682s->pdata.dmic1_clk_pin) {
+ case RT5682S_DMIC1_CLK_NULL:
+ break;
+ case RT5682S_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_DMIC_CLK);
+ break;
+ case RT5682S_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP3_PIN_MASK, RT5682S_GP3_PIN_DMIC_CLK);
+ if (rt5682s->pdata.dmic_clk_driving_high)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PAD_DRIVING_CTRL,
+ RT5682S_PAD_DRV_GP3_MASK, RT5682S_PAD_DRV_GP3_HIGH);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+
+ INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5682s", rt5682s);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev,
+ rt5682s_dai, ARRAY_SIZE(rt5682s_dai));
+}
+
+static void rt5682s_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5682s_priv *rt5682s = i2c_get_clientdata(client);
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ rt5682s_reset(rt5682s);
+}
+
+static int rt5682s_i2c_remove(struct i2c_client *client)
+{
+ rt5682s_i2c_shutdown(client);
+
+ return 0;
+}
+
+static const struct of_device_id rt5682s_of_match[] = {
+ {.compatible = "realtek,rt5682s"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5682s_of_match);
+
+static const struct acpi_device_id rt5682s_acpi_match[] = {
+ {"RTL5682", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match);
+
+static const struct i2c_device_id rt5682s_i2c_id[] = {
+ {"rt5682s", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id);
+
+static struct i2c_driver rt5682s_i2c_driver = {
+ .driver = {
+ .name = "rt5682s",
+ .of_match_table = rt5682s_of_match,
+ .acpi_match_table = rt5682s_acpi_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = rt5682s_i2c_probe,
+ .remove = rt5682s_i2c_remove,
+ .shutdown = rt5682s_i2c_shutdown,
+ .id_table = rt5682s_i2c_id,
+};
+module_i2c_driver(rt5682s_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682I-VS driver");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
new file mode 100644
index 000000000000..1bf2ef7ce578
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.h
@@ -0,0 +1,1474 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682s.h -- RT5682I-VS ALSA SoC audio driver
+ *
+ * Copyright 2021 Realtek Microelectronics
+ * Author: Derek Fang <derek.fang@realtek.com>
+ */
+
+#ifndef __RT5682S_H__
+#define __RT5682S_H__
+
+#include <sound/rt5682s.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+
+/* Info */
+#define RT5682S_RESET 0x0000
+#define RT5682S_VERSION_ID 0x00fd
+#define RT5682S_VENDOR_ID 0x00fe
+#define RT5682S_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5682S_HP_CTRL_1 0x0002
+#define RT5682S_HP_CTRL_2 0x0003
+#define RT5682S_HPL_GAIN 0x0005
+#define RT5682S_HPR_GAIN 0x0006
+
+#define RT5682S_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5682S_CBJ_BST_CTRL 0x000b
+#define RT5682S_CBJ_DET_CTRL 0x000f
+#define RT5682S_CBJ_CTRL_1 0x0010
+#define RT5682S_CBJ_CTRL_2 0x0011
+#define RT5682S_CBJ_CTRL_3 0x0012
+#define RT5682S_CBJ_CTRL_4 0x0013
+#define RT5682S_CBJ_CTRL_5 0x0014
+#define RT5682S_CBJ_CTRL_6 0x0015
+#define RT5682S_CBJ_CTRL_7 0x0016
+#define RT5682S_CBJ_CTRL_8 0x0017
+/* I/O - ADC/DAC/DMIC */
+#define RT5682S_DAC1_DIG_VOL 0x0019
+#define RT5682S_STO1_ADC_DIG_VOL 0x001c
+#define RT5682S_STO1_ADC_BOOST 0x001f
+#define RT5682S_HP_IMP_GAIN_1 0x0022
+#define RT5682S_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5682S_SIDETONE_CTRL 0x0024
+#define RT5682S_STO1_ADC_MIXER 0x0026
+#define RT5682S_AD_DA_MIXER 0x0029
+#define RT5682S_STO1_DAC_MIXER 0x002a
+#define RT5682S_A_DAC1_MUX 0x002b
+#define RT5682S_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5682S_REC_MIXER 0x003c
+#define RT5682S_CAL_REC 0x0044
+/* HP Analog Offset Control */
+#define RT5682S_HP_ANA_OST_CTRL_1 0x004b
+#define RT5682S_HP_ANA_OST_CTRL_2 0x004c
+#define RT5682S_HP_ANA_OST_CTRL_3 0x004d
+/* Power */
+#define RT5682S_PWR_DIG_1 0x0061
+#define RT5682S_PWR_DIG_2 0x0062
+#define RT5682S_PWR_ANLG_1 0x0063
+#define RT5682S_PWR_ANLG_2 0x0064
+#define RT5682S_PWR_ANLG_3 0x0065
+#define RT5682S_PWR_MIXER 0x0066
+
+#define RT5682S_MB_CTRL 0x0067
+#define RT5682S_CLK_GATE_TCON_1 0x0068
+#define RT5682S_CLK_GATE_TCON_2 0x0069
+#define RT5682S_CLK_GATE_TCON_3 0x006a
+/* Clock Detect */
+#define RT5682S_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5682S_RESET_LPF_CTRL 0x006c
+#define RT5682S_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5682S_DMIC_CTRL_1 0x006e
+#define RT5682S_LPF_AD_DMIC 0x006f
+/* Format - ADC/DAC */
+#define RT5682S_I2S1_SDP 0x0070
+#define RT5682S_I2S2_SDP 0x0071
+#define RT5682S_ADDA_CLK_1 0x0073
+#define RT5682S_ADDA_CLK_2 0x0074
+#define RT5682S_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5682S_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5682S_TDM_CTRL 0x0079
+#define RT5682S_TDM_ADDA_CTRL_1 0x007a
+#define RT5682S_TDM_ADDA_CTRL_2 0x007b
+#define RT5682S_DATA_SEL_CTRL_1 0x007c
+#define RT5682S_TDM_TCON_CTRL_1 0x007e
+#define RT5682S_TDM_TCON_CTRL_2 0x007f
+/* Function - Analog */
+#define RT5682S_GLB_CLK 0x0080
+#define RT5682S_PLL_TRACK_1 0x0083
+#define RT5682S_PLL_TRACK_2 0x0084
+#define RT5682S_PLL_TRACK_3 0x0085
+#define RT5682S_PLL_TRACK_4 0x0086
+#define RT5682S_PLL_TRACK_5 0x0087
+#define RT5682S_PLL_TRACK_6 0x0088
+#define RT5682S_PLL_TRACK_11 0x008c
+#define RT5682S_DEPOP_1 0x008e
+#define RT5682S_HP_CHARGE_PUMP_1 0x008f
+#define RT5682S_HP_CHARGE_PUMP_2 0x0091
+#define RT5682S_HP_CHARGE_PUMP_3 0x0092
+#define RT5682S_MICBIAS_1 0x0093
+#define RT5682S_MICBIAS_2 0x0094
+#define RT5682S_MICBIAS_3 0x0095
+
+#define RT5682S_PLL_TRACK_12 0x0096
+#define RT5682S_PLL_TRACK_14 0x0097
+#define RT5682S_PLL_CTRL_1 0x0098
+#define RT5682S_PLL_CTRL_2 0x0099
+#define RT5682S_PLL_CTRL_3 0x009a
+#define RT5682S_PLL_CTRL_4 0x009b
+#define RT5682S_PLL_CTRL_5 0x009c
+#define RT5682S_PLL_CTRL_6 0x009d
+#define RT5682S_PLL_CTRL_7 0x009e
+
+#define RT5682S_RC_CLK_CTRL 0x009f
+#define RT5682S_I2S2_M_CLK_CTRL_1 0x00a0
+#define RT5682S_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5682S_I2S2_F_DIV_CTRL_2 0x00a4
+
+#define RT5682S_IRQ_CTRL_1 0x00b6
+#define RT5682S_IRQ_CTRL_2 0x00b7
+#define RT5682S_IRQ_CTRL_3 0x00b8
+#define RT5682S_IRQ_CTRL_4 0x00b9
+#define RT5682S_INT_ST_1 0x00be
+#define RT5682S_GPIO_CTRL_1 0x00c0
+#define RT5682S_GPIO_CTRL_2 0x00c1
+#define RT5682S_GPIO_ST 0x00c2
+#define RT5682S_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5682S_MID_HP_AMP_DET 0x00d2
+#define RT5682S_LOW_HP_AMP_DET 0x00d3
+#define RT5682S_DELAY_BUF_CTRL 0x00d4
+#define RT5682S_SV_ZCD_1 0x00d9
+#define RT5682S_SV_ZCD_2 0x00da
+#define RT5682S_IL_CMD_1 0x00db
+#define RT5682S_IL_CMD_2 0x00dc
+#define RT5682S_IL_CMD_3 0x00dd
+#define RT5682S_IL_CMD_4 0x00de
+#define RT5682S_IL_CMD_5 0x00df
+#define RT5682S_IL_CMD_6 0x00e0
+#define RT5682S_4BTN_IL_CMD_1 0x00e2
+#define RT5682S_4BTN_IL_CMD_2 0x00e3
+#define RT5682S_4BTN_IL_CMD_3 0x00e4
+#define RT5682S_4BTN_IL_CMD_4 0x00e5
+#define RT5682S_4BTN_IL_CMD_5 0x00e6
+#define RT5682S_4BTN_IL_CMD_6 0x00e7
+#define RT5682S_4BTN_IL_CMD_7 0x00e8
+
+#define RT5682S_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5682S_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5682S_AJD1_CTRL 0x00f0
+#define RT5682S_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5682S_DUMMY_1 0x00fa
+#define RT5682S_DUMMY_2 0x00fb
+#define RT5682S_DUMMY_3 0x00fc
+
+#define RT5682S_DAC_ADC_DIG_VOL1 0x0100
+#define RT5682S_BIAS_CUR_CTRL_2 0x010b
+#define RT5682S_BIAS_CUR_CTRL_3 0x010c
+#define RT5682S_BIAS_CUR_CTRL_4 0x010d
+#define RT5682S_BIAS_CUR_CTRL_5 0x010e
+#define RT5682S_BIAS_CUR_CTRL_6 0x010f
+#define RT5682S_BIAS_CUR_CTRL_7 0x0110
+#define RT5682S_BIAS_CUR_CTRL_8 0x0111
+#define RT5682S_BIAS_CUR_CTRL_9 0x0112
+#define RT5682S_BIAS_CUR_CTRL_10 0x0113
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_1 0x0117
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_2 0x0118
+#define RT5682S_CHARGE_PUMP_1 0x0125
+#define RT5682S_DIG_IN_CTRL_1 0x0132
+#define RT5682S_PAD_DRIVING_CTRL 0x0136
+#define RT5682S_CHOP_DAC_1 0x0139
+#define RT5682S_CHOP_DAC_2 0x013a
+#define RT5682S_CHOP_ADC 0x013b
+#define RT5682S_CALIB_ADC_CTRL 0x013c
+#define RT5682S_VOL_TEST 0x013f
+#define RT5682S_SPKVDD_DET_ST 0x0142
+#define RT5682S_TEST_MODE_CTRL_1 0x0145
+#define RT5682S_TEST_MODE_CTRL_2 0x0146
+#define RT5682S_TEST_MODE_CTRL_3 0x0147
+#define RT5682S_TEST_MODE_CTRL_4 0x0148
+#define RT5682S_PLL_INTERNAL_1 0x0156
+#define RT5682S_PLL_INTERNAL_2 0x0157
+#define RT5682S_PLL_INTERNAL_3 0x0158
+#define RT5682S_PLL_INTERNAL_4 0x0159
+#define RT5682S_STO_NG2_CTRL_1 0x0160
+#define RT5682S_STO_NG2_CTRL_2 0x0161
+#define RT5682S_STO_NG2_CTRL_3 0x0162
+#define RT5682S_STO_NG2_CTRL_4 0x0163
+#define RT5682S_STO_NG2_CTRL_5 0x0164
+#define RT5682S_STO_NG2_CTRL_6 0x0165
+#define RT5682S_STO_NG2_CTRL_7 0x0166
+#define RT5682S_STO_NG2_CTRL_8 0x0167
+#define RT5682S_STO_NG2_CTRL_9 0x0168
+#define RT5682S_STO_NG2_CTRL_10 0x0169
+#define RT5682S_STO1_DAC_SIL_DET 0x0190
+#define RT5682S_SIL_PSV_CTRL1 0x0194
+#define RT5682S_SIL_PSV_CTRL2 0x0195
+#define RT5682S_SIL_PSV_CTRL3 0x0197
+#define RT5682S_SIL_PSV_CTRL4 0x0198
+#define RT5682S_SIL_PSV_CTRL5 0x0199
+#define RT5682S_HP_IMP_SENS_CTRL_1 0x01ac
+#define RT5682S_HP_IMP_SENS_CTRL_2 0x01ad
+#define RT5682S_HP_IMP_SENS_CTRL_3 0x01ae
+#define RT5682S_HP_IMP_SENS_CTRL_4 0x01af
+#define RT5682S_HP_IMP_SENS_CTRL_5 0x01b0
+#define RT5682S_HP_IMP_SENS_CTRL_6 0x01b1
+#define RT5682S_HP_IMP_SENS_CTRL_7 0x01b2
+#define RT5682S_HP_IMP_SENS_CTRL_8 0x01b3
+#define RT5682S_HP_IMP_SENS_CTRL_9 0x01b4
+#define RT5682S_HP_IMP_SENS_CTRL_10 0x01b5
+#define RT5682S_HP_IMP_SENS_CTRL_11 0x01b6
+#define RT5682S_HP_IMP_SENS_CTRL_12 0x01b7
+#define RT5682S_HP_IMP_SENS_CTRL_13 0x01b8
+#define RT5682S_HP_IMP_SENS_CTRL_14 0x01b9
+#define RT5682S_HP_IMP_SENS_CTRL_15 0x01ba
+#define RT5682S_HP_IMP_SENS_CTRL_16 0x01bb
+#define RT5682S_HP_IMP_SENS_CTRL_17 0x01bc
+#define RT5682S_HP_IMP_SENS_CTRL_18 0x01bd
+#define RT5682S_HP_IMP_SENS_CTRL_19 0x01be
+#define RT5682S_HP_IMP_SENS_CTRL_20 0x01bf
+#define RT5682S_HP_IMP_SENS_CTRL_21 0x01c0
+#define RT5682S_HP_IMP_SENS_CTRL_22 0x01c1
+#define RT5682S_HP_IMP_SENS_CTRL_23 0x01c2
+#define RT5682S_HP_IMP_SENS_CTRL_24 0x01c3
+#define RT5682S_HP_IMP_SENS_CTRL_25 0x01c4
+#define RT5682S_HP_IMP_SENS_CTRL_26 0x01c5
+#define RT5682S_HP_IMP_SENS_CTRL_27 0x01c6
+#define RT5682S_HP_IMP_SENS_CTRL_28 0x01c7
+#define RT5682S_HP_IMP_SENS_CTRL_29 0x01c8
+#define RT5682S_HP_IMP_SENS_CTRL_30 0x01c9
+#define RT5682S_HP_IMP_SENS_CTRL_31 0x01ca
+#define RT5682S_HP_IMP_SENS_CTRL_32 0x01cb
+#define RT5682S_HP_IMP_SENS_CTRL_33 0x01cc
+#define RT5682S_HP_IMP_SENS_CTRL_34 0x01cd
+#define RT5682S_HP_IMP_SENS_CTRL_35 0x01ce
+#define RT5682S_HP_IMP_SENS_CTRL_36 0x01cf
+#define RT5682S_HP_IMP_SENS_CTRL_37 0x01d0
+#define RT5682S_HP_IMP_SENS_CTRL_38 0x01d1
+#define RT5682S_HP_IMP_SENS_CTRL_39 0x01d2
+#define RT5682S_HP_IMP_SENS_CTRL_40 0x01d3
+#define RT5682S_HP_IMP_SENS_CTRL_41 0x01d4
+#define RT5682S_HP_IMP_SENS_CTRL_42 0x01d5
+#define RT5682S_HP_IMP_SENS_CTRL_43 0x01d6
+#define RT5682S_HP_IMP_SENS_CTRL_44 0x01d7
+#define RT5682S_HP_IMP_SENS_CTRL_45 0x01d8
+#define RT5682S_HP_IMP_SENS_CTRL_46 0x01d9
+#define RT5682S_HP_LOGIC_CTRL_1 0x01da
+#define RT5682S_HP_LOGIC_CTRL_2 0x01db
+#define RT5682S_HP_LOGIC_CTRL_3 0x01dc
+#define RT5682S_HP_CALIB_CTRL_1 0x01de
+#define RT5682S_HP_CALIB_CTRL_2 0x01df
+#define RT5682S_HP_CALIB_CTRL_3 0x01e0
+#define RT5682S_HP_CALIB_CTRL_4 0x01e1
+#define RT5682S_HP_CALIB_CTRL_5 0x01e2
+#define RT5682S_HP_CALIB_CTRL_6 0x01e3
+#define RT5682S_HP_CALIB_CTRL_7 0x01e4
+#define RT5682S_HP_CALIB_CTRL_8 0x01e5
+#define RT5682S_HP_CALIB_CTRL_9 0x01e6
+#define RT5682S_HP_CALIB_CTRL_10 0x01e7
+#define RT5682S_HP_CALIB_CTRL_11 0x01e8
+#define RT5682S_HP_CALIB_ST_1 0x01ea
+#define RT5682S_HP_CALIB_ST_2 0x01eb
+#define RT5682S_HP_CALIB_ST_3 0x01ec
+#define RT5682S_HP_CALIB_ST_4 0x01ed
+#define RT5682S_HP_CALIB_ST_5 0x01ee
+#define RT5682S_HP_CALIB_ST_6 0x01ef
+#define RT5682S_HP_CALIB_ST_7 0x01f0
+#define RT5682S_HP_CALIB_ST_8 0x01f1
+#define RT5682S_HP_CALIB_ST_9 0x01f2
+#define RT5682S_HP_CALIB_ST_10 0x01f3
+#define RT5682S_HP_CALIB_ST_11 0x01f4
+#define RT5682S_SAR_IL_CMD_1 0x0210
+#define RT5682S_SAR_IL_CMD_2 0x0211
+#define RT5682S_SAR_IL_CMD_3 0x0212
+#define RT5682S_SAR_IL_CMD_4 0x0213
+#define RT5682S_SAR_IL_CMD_5 0x0214
+#define RT5682S_SAR_IL_CMD_6 0x0215
+#define RT5682S_SAR_IL_CMD_7 0x0216
+#define RT5682S_SAR_IL_CMD_8 0x0217
+#define RT5682S_SAR_IL_CMD_9 0x0218
+#define RT5682S_SAR_IL_CMD_10 0x0219
+#define RT5682S_SAR_IL_CMD_11 0x021a
+#define RT5682S_SAR_IL_CMD_12 0x021b
+#define RT5682S_SAR_IL_CMD_13 0x021c
+#define RT5682S_SAR_IL_CMD_14 0x021d
+#define RT5682S_DUMMY_4 0x02fa
+#define RT5682S_DUMMY_5 0x02fb
+#define RT5682S_DUMMY_6 0x02fc
+#define RT5682S_VERSION_ID_HIDE 0x03fe
+#define RT5682S_VERSION_ID_CUS 0x03ff
+#define RT5682S_SCAN_CTL 0x0500
+#define RT5682S_HP_AMP_DET 0x0600
+#define RT5682S_BIAS_CUR_CTRL_11 0x0610
+#define RT5682S_BIAS_CUR_CTRL_12 0x0611
+#define RT5682S_BIAS_CUR_CTRL_13 0x0620
+#define RT5682S_BIAS_CUR_CTRL_14 0x0621
+#define RT5682S_BIAS_CUR_CTRL_15 0x0630
+#define RT5682S_BIAS_CUR_CTRL_16 0x0631
+#define RT5682S_BIAS_CUR_CTRL_17 0x0640
+#define RT5682S_BIAS_CUR_CTRL_18 0x0641
+#define RT5682S_I2C_TRANS_CTRL 0x07fa
+#define RT5682S_DUMMY_7 0x08fa
+#define RT5682S_DUMMY_8 0x08fb
+#define RT5682S_DMIC_FLOAT_DET 0x0d00
+#define RT5682S_HA_CMP_OP_1 0x1100
+#define RT5682S_HA_CMP_OP_2 0x1101
+#define RT5682S_HA_CMP_OP_3 0x1102
+#define RT5682S_HA_CMP_OP_4 0x1103
+#define RT5682S_HA_CMP_OP_5 0x1104
+#define RT5682S_HA_CMP_OP_6 0x1105
+#define RT5682S_HA_CMP_OP_7 0x1106
+#define RT5682S_HA_CMP_OP_8 0x1107
+#define RT5682S_HA_CMP_OP_9 0x1108
+#define RT5682S_HA_CMP_OP_10 0x1109
+#define RT5682S_HA_CMP_OP_11 0x110a
+#define RT5682S_HA_CMP_OP_12 0x110b
+#define RT5682S_HA_CMP_OP_13 0x110c
+#define RT5682S_HA_CMP_OP_14 0x1111
+#define RT5682S_HA_CMP_OP_15 0x1112
+#define RT5682S_HA_CMP_OP_16 0x1113
+#define RT5682S_HA_CMP_OP_17 0x1114
+#define RT5682S_HA_CMP_OP_18 0x1115
+#define RT5682S_HA_CMP_OP_19 0x1116
+#define RT5682S_HA_CMP_OP_20 0x1117
+#define RT5682S_HA_CMP_OP_21 0x1118
+#define RT5682S_HA_CMP_OP_22 0x1119
+#define RT5682S_HA_CMP_OP_23 0x111a
+#define RT5682S_HA_CMP_OP_24 0x111b
+#define RT5682S_HA_CMP_OP_25 0x111c
+#define RT5682S_NEW_CBJ_DET_CTL_1 0x1401
+#define RT5682S_NEW_CBJ_DET_CTL_2 0x1402
+#define RT5682S_NEW_CBJ_DET_CTL_3 0x1403
+#define RT5682S_NEW_CBJ_DET_CTL_4 0x1404
+#define RT5682S_NEW_CBJ_DET_CTL_5 0x1406
+#define RT5682S_NEW_CBJ_DET_CTL_6 0x1407
+#define RT5682S_NEW_CBJ_DET_CTL_7 0x1408
+#define RT5682S_NEW_CBJ_DET_CTL_8 0x1409
+#define RT5682S_NEW_CBJ_DET_CTL_9 0x140a
+#define RT5682S_NEW_CBJ_DET_CTL_10 0x140b
+#define RT5682S_NEW_CBJ_DET_CTL_11 0x140c
+#define RT5682S_NEW_CBJ_DET_CTL_12 0x140d
+#define RT5682S_NEW_CBJ_DET_CTL_13 0x140e
+#define RT5682S_NEW_CBJ_DET_CTL_14 0x140f
+#define RT5682S_NEW_CBJ_DET_CTL_15 0x1410
+#define RT5682S_NEW_CBJ_DET_CTL_16 0x1411
+#define RT5682S_DA_FILTER_1 0x1801
+#define RT5682S_DA_FILTER_2 0x1802
+#define RT5682S_DA_FILTER_3 0x1803
+#define RT5682S_DA_FILTER_4 0x1804
+#define RT5682S_DA_FILTER_5 0x1805
+#define RT5682S_CLK_SW_TEST_1 0x2c00
+#define RT5682S_CLK_SW_TEST_2 0x3400
+#define RT5682S_CLK_SW_TEST_3 0x3404
+#define RT5682S_CLK_SW_TEST_4 0x3405
+#define RT5682S_CLK_SW_TEST_5 0x3406
+#define RT5682S_CLK_SW_TEST_6 0x3407
+#define RT5682S_CLK_SW_TEST_7 0x3408
+#define RT5682S_CLK_SW_TEST_8 0x3409
+#define RT5682S_CLK_SW_TEST_9 0x340a
+#define RT5682S_CLK_SW_TEST_10 0x340b
+#define RT5682S_CLK_SW_TEST_11 0x340c
+#define RT5682S_CLK_SW_TEST_12 0x340d
+#define RT5682S_CLK_SW_TEST_13 0x340e
+#define RT5682S_CLK_SW_TEST_14 0x340f
+#define RT5682S_EFUSE_MANU_WRITE_1 0x3410
+#define RT5682S_EFUSE_MANU_WRITE_2 0x3411
+#define RT5682S_EFUSE_MANU_WRITE_3 0x3412
+#define RT5682S_EFUSE_MANU_WRITE_4 0x3413
+#define RT5682S_EFUSE_MANU_WRITE_5 0x3414
+#define RT5682S_EFUSE_MANU_WRITE_6 0x3415
+#define RT5682S_EFUSE_READ_1 0x3424
+#define RT5682S_EFUSE_READ_2 0x3425
+#define RT5682S_EFUSE_READ_3 0x3426
+#define RT5682S_EFUSE_READ_4 0x3427
+#define RT5682S_EFUSE_READ_5 0x3428
+#define RT5682S_EFUSE_READ_6 0x3429
+#define RT5682S_EFUSE_READ_7 0x342a
+#define RT5682S_EFUSE_READ_8 0x342b
+#define RT5682S_EFUSE_READ_9 0x342c
+#define RT5682S_EFUSE_READ_10 0x342d
+#define RT5682S_EFUSE_READ_11 0x342e
+#define RT5682S_EFUSE_READ_12 0x342f
+#define RT5682S_EFUSE_READ_13 0x3430
+#define RT5682S_EFUSE_READ_14 0x3431
+#define RT5682S_EFUSE_READ_15 0x3432
+#define RT5682S_EFUSE_READ_16 0x3433
+#define RT5682S_EFUSE_READ_17 0x3434
+#define RT5682S_EFUSE_READ_18 0x3435
+#define RT5682S_EFUSE_TIMING_CTL_1 0x3440
+#define RT5682S_EFUSE_TIMING_CTL_2 0x3441
+#define RT5682S_PILOT_DIG_CTL_1 0x3500
+#define RT5682S_PILOT_DIG_CTL_2 0x3501
+#define RT5682S_HP_AMP_DET_CTL_1 0x3b00
+#define RT5682S_HP_AMP_DET_CTL_2 0x3b01
+#define RT5682S_HP_AMP_DET_CTL_3 0x3b02
+#define RT5682S_HP_AMP_DET_CTL_4 0x3b03
+
+#define RT5682S_MAX_REG (RT5682S_HP_AMP_DET_CTL_4)
+
+/* global definition */
+#define RT5682S_L_MUTE (0x1 << 15)
+#define RT5682S_L_MUTE_SFT 15
+#define RT5682S_R_MUTE (0x1 << 7)
+#define RT5682S_R_MUTE_SFT 7
+#define RT5682S_L_VOL_SFT 8
+#define RT5682S_R_VOL_SFT 0
+#define RT5682S_CLK_SRC_MCLK (0x0)
+#define RT5682S_CLK_SRC_PLL1 (0x1)
+#define RT5682S_CLK_SRC_PLL2 (0x2)
+#define RT5682S_CLK_SRC_RCCLK (0x4) /* 25M */
+
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682S_HPO_L_PATH_MASK (0x1 << 14)
+#define RT5682S_HPO_L_PATH_EN (0x1 << 14)
+#define RT5682S_HPO_L_PATH_DIS (0x0 << 14)
+#define RT5682S_HPO_R_PATH_MASK (0x1 << 13)
+#define RT5682S_HPO_R_PATH_EN (0x1 << 13)
+#define RT5682S_HPO_R_PATH_DIS (0x0 << 13)
+#define RT5682S_HPO_SEL_IP_EN_SW (0x1)
+#define RT5682S_HPO_IP_EN_GATING (0x1)
+#define RT5682S_HPO_IP_NO_GATING (0x0)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5682S_G_HP (0xf << 8)
+#define RT5682S_G_HP_SFT 8
+#define RT5682S_G_STO_DA_DMIX (0xf)
+#define RT5682S_G_STO_DA_SFT 0
+
+/* Embeeded Jack and Type Detection Control 2 (0x0010) */
+#define RT5682S_EMB_JD_MASK (0x1 << 15)
+#define RT5682S_EMB_JD_EN (0x1 << 15)
+#define RT5682S_EMB_JD_EN_SFT 15
+#define RT5682S_EMB_JD_RST (0x1 << 14)
+#define RT5682S_JD_MODE (0x1 << 13)
+#define RT5682S_JD_MODE_SFT 13
+#define RT5682S_DET_TYPE (0x1 << 12)
+#define RT5682S_DET_TYPE_SFT 12
+#define RT5682S_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5682S_SEL_FAST_OFF_MASK (0x3 << 9)
+#define RT5682S_SEL_FAST_OFF_SFT 9
+#define RT5682S_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5682S_FAST_OFF_MASK (0x1 << 7)
+#define RT5682S_FAST_OFF_EN (0x1 << 7)
+#define RT5682S_FAST_OFF_DIS (0x0 << 7)
+#define RT5682S_VREF_POW_MASK (0x1 << 6)
+#define RT5682S_VREF_POW_FSM (0x0 << 6)
+#define RT5682S_VREF_POW_REG (0x1 << 6)
+#define RT5682S_MB1_PATH_BIT 5
+#define RT5682S_MB1_PATH_MASK (0x1 << 5)
+#define RT5682S_CTRL_MB1_REG (0x1 << 5)
+#define RT5682S_CTRL_MB1_FSM (0x0 << 5)
+#define RT5682S_MB2_PATH_BIT 4
+#define RT5682S_MB2_PATH_MASK (0x1 << 4)
+#define RT5682S_CTRL_MB2_REG (0x1 << 4)
+#define RT5682S_CTRL_MB2_FSM (0x0 << 4)
+#define RT5682S_TRIG_JD_MASK (0x1 << 3)
+#define RT5682S_TRIG_JD_HIGH (0x1 << 3)
+#define RT5682S_TRIG_JD_LOW (0x0 << 3)
+#define RT5682S_MIC_CAP_MASK (0x1 << 1)
+#define RT5682S_MIC_CAP_HS (0x1 << 1)
+#define RT5682S_MIC_CAP_HP (0x0 << 1)
+#define RT5682S_MIC_CAP_SRC_MASK (0x1)
+#define RT5682S_MIC_CAP_SRC_REG (0x1)
+#define RT5682S_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 3 (0x0011) */
+#define RT5682S_SEL_CBJ_TYPE_SLOW (0x1 << 15)
+#define RT5682S_SEL_CBJ_TYPE_NORM (0x0 << 15)
+#define RT5682S_SEL_CBJ_TYPE_MASK (0x1 << 15)
+#define RT5682S_POW_BG_MB1_MASK (0x1 << 13)
+#define RT5682S_POW_BG_MB1_REG (0x1 << 13)
+#define RT5682S_POW_BG_MB1_FSM (0x0 << 13)
+#define RT5682S_POW_BG_MB2_MASK (0x1 << 12)
+#define RT5682S_POW_BG_MB2_REG (0x1 << 12)
+#define RT5682S_POW_BG_MB2_FSM (0x0 << 12)
+#define RT5682S_EXT_JD_SRC (0x7 << 4)
+#define RT5682S_EXT_JD_SRC_SFT 4
+#define RT5682S_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5682S_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5682S_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5682S_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5682S_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5682S_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 4 (0x0012) */
+#define RT5682S_CBJ_IN_BUF_MASK (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_EN (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_DIS (0x0 << 7)
+#define RT5682S_CBJ_IN_BUF_BIT 7
+
+/* Combo Jack and Type Detection Control 5 (0x0013) */
+#define RT5682S_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5682S_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5682S_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5682S_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5682S_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5682S_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* Combo Jack and Type Detection Control 6 (0x0014) */
+#define RT5682S_JD_FAST_OFF_SRC_MASK (0x7 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_JDH (0x6 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO6 (0x5 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO5 (0x4 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO4 (0x3 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO3 (0x2 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO2 (0x1 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO1 (0x0 << 8)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5682S_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5682S_DAC_L1_VOL_SFT 8
+#define RT5682S_DAC_R1_VOL_MASK (0xff)
+#define RT5682S_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5682S_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5682S_ADC_L_VOL_SFT 8
+#define RT5682S_ADC_R_VOL_MASK (0x7f)
+#define RT5682S_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5682S_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5682S_STO1_ADC_L_BST_SFT 14
+#define RT5682S_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5682S_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5682S_ST_SRC_SEL (0x1 << 8)
+#define RT5682S_ST_SRC_SFT 8
+#define RT5682S_ST_EN_MASK (0x1 << 6)
+#define RT5682S_ST_DIS (0x0 << 6)
+#define RT5682S_ST_EN (0x1 << 6)
+#define RT5682S_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5682S_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5682S_M_STO1_ADC_L1_SFT 15
+#define RT5682S_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5682S_M_STO1_ADC_L2_SFT 14
+#define RT5682S_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5682S_STO1_ADC1L_SRC_SFT 13
+#define RT5682S_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5682S_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5682S_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5682S_STO1_ADC2L_SRC_SFT 12
+#define RT5682S_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5682S_STO1_ADCL_SRC_SFT 10
+#define RT5682S_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5682S_M_STO1_ADC_R1_SFT 7
+#define RT5682S_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5682S_M_STO1_ADC_R2_SFT 6
+#define RT5682S_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5682S_STO1_ADC1R_SRC_SFT 5
+#define RT5682S_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5682S_STO1_ADC2R_SRC_SFT 4
+#define RT5682S_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5682S_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5682S_M_ADCMIX_L (0x1 << 15)
+#define RT5682S_M_ADCMIX_L_SFT 15
+#define RT5682S_M_DAC1_L (0x1 << 14)
+#define RT5682S_M_DAC1_L_SFT 14
+#define RT5682S_M_ADCMIX_R (0x1 << 7)
+#define RT5682S_M_ADCMIX_R_SFT 7
+#define RT5682S_M_DAC1_R (0x1 << 6)
+#define RT5682S_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5682S_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5682S_M_DAC_L1_STO_L_SFT 15
+#define RT5682S_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5682S_G_DAC_L1_STO_L_SFT 14
+#define RT5682S_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5682S_M_DAC_R1_STO_L_SFT 13
+#define RT5682S_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5682S_G_DAC_R1_STO_L_SFT 12
+#define RT5682S_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5682S_M_DAC_L1_STO_R_SFT 7
+#define RT5682S_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5682S_G_DAC_L1_STO_R_SFT 6
+#define RT5682S_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5682S_M_DAC_R1_STO_R_SFT 5
+#define RT5682S_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5682S_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5682S_M_ST_STO_L (0x1 << 9)
+#define RT5682S_M_ST_STO_L_SFT 9
+#define RT5682S_M_ST_STO_R (0x1 << 8)
+#define RT5682S_M_ST_STO_R_SFT 8
+#define RT5682S_DAC_L1_SRC_MASK (0x1 << 4)
+#define RT5682S_A_DACL1_SFT 4
+#define RT5682S_DAC_R1_SRC_MASK (0x1)
+#define RT5682S_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5682S_IF2_DAC_SEL_MASK (0x3 << 2)
+#define RT5682S_IF2_DAC_SEL_SFT 2
+#define RT5682S_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5682S_IF2_ADC_SEL_SFT 0
+
+/* REC Left/Right Mixer Control 2 (0x003c) */
+#define RT5682S_BST_CBJ_MASK (0x3f << 8)
+#define RT5682S_BST_CBJ_SFT 8
+#define RT5682S_M_CBJ_RM1_L (0x1 << 7)
+#define RT5682S_M_CBJ_RM1_L_SFT 7
+#define RT5682S_M_CBJ_RM1_R (0x1 << 6)
+#define RT5682S_M_CBJ_RM1_R_SFT 6
+
+/* REC Left/Right Mixer Calibration Control(0x0044) */
+#define RT5682S_PWR_RM1_R_BIT 8
+#define RT5682S_PWR_RM1_L_BIT 0
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5682S_PWR_I2S1 (0x1 << 15)
+#define RT5682S_PWR_I2S1_BIT 15
+#define RT5682S_PWR_I2S2 (0x1 << 14)
+#define RT5682S_PWR_I2S2_BIT 14
+#define RT5682S_PRE_CHR_DAC_L1 (0x1 << 13)
+#define RT5682S_PRE_CHR_DAC_L1_BIT 13
+#define RT5682S_PRE_CHR_DAC_R1 (0x1 << 12)
+#define RT5682S_PRE_CHR_DAC_R1_BIT 12
+#define RT5682S_PWR_DAC_L1 (0x1 << 11)
+#define RT5682S_PWR_DAC_L1_BIT 11
+#define RT5682S_PWR_DAC_R1 (0x1 << 10)
+#define RT5682S_PWR_DAC_R1_BIT 10
+#define RT5682S_PWR_LDO (0x1 << 8)
+#define RT5682S_PWR_LDO_BIT 8
+#define RT5682S_PWR_D2S_L (0x1 << 7)
+#define RT5682S_PWR_D2S_L_BIT 7
+#define RT5682S_PWR_D2S_R (0x1 << 6)
+#define RT5682S_PWR_D2S_R_BIT 6
+#define RT5682S_PWR_ADC_L1 (0x1 << 4)
+#define RT5682S_PWR_ADC_L1_BIT 4
+#define RT5682S_PWR_ADC_R1 (0x1 << 3)
+#define RT5682S_PWR_ADC_R1_BIT 3
+#define RT5682S_EFUSE_SW_EN (0x1 << 2)
+#define RT5682S_EFUSE_SW_DIS (0x0 << 2)
+#define RT5682S_PWR_EFUSE (0x1 << 1)
+#define RT5682S_PWR_EFUSE_BIT 1
+#define RT5682S_DIG_GATE_CTRL (0x1 << 0)
+#define RT5682S_DIG_GATE_CTRL_SFT 0
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5682S_PWR_ADC_S1F (0x1 << 15)
+#define RT5682S_PWR_ADC_S1F_BIT 15
+#define RT5682S_PWR_DAC_S1F (0x1 << 10)
+#define RT5682S_PWR_DAC_S1F_BIT 10
+#define RT5682S_DLDO_I_LIMIT_MASK (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_EN (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_DIS (0x0 << 7)
+#define RT5682S_DLDO_I_BIAS_SEL_4 (0x1 << 6)
+#define RT5682S_DLDO_I_BIAS_SEL_0 (0x0 << 6)
+#define RT5682S_DLDO_REG_TEST_1 (0x1 << 5)
+#define RT5682S_DLDO_REG_TEST_0 (0x0 << 5)
+#define RT5682S_DLDO_SRC_REG (0x1 << 4)
+#define RT5682S_DLDO_SRC_EFUSE (0x0 << 4)
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5682S_PWR_VREF1 (0x1 << 15)
+#define RT5682S_PWR_VREF1_BIT 15
+#define RT5682S_PWR_FV1 (0x1 << 14)
+#define RT5682S_PWR_FV1_BIT 14
+#define RT5682S_PWR_VREF2 (0x1 << 13)
+#define RT5682S_PWR_VREF2_BIT 13
+#define RT5682S_PWR_FV2 (0x1 << 12)
+#define RT5682S_PWR_FV2_BIT 12
+#define RT5682S_LDO1_DBG_MASK (0x3 << 10)
+#define RT5682S_PWR_MB (0x1 << 9)
+#define RT5682S_PWR_MB_BIT 9
+#define RT5682S_PWR_BG (0x1 << 7)
+#define RT5682S_PWR_BG_BIT 7
+#define RT5682S_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5682S_LDO1_BYPASS (0x1 << 6)
+#define RT5682S_LDO1_NOT_BYPASS (0x0 << 6)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5682S_PWR_MCLK0_WD (0x1 << 15)
+#define RT5682S_PWR_MCLK0_WD_BIT 15
+#define RT5682S_PWR_MCLK1_WD (0x1 << 14)
+#define RT5682S_PWR_MCLK1_WD_BIT 14
+#define RT5682S_RST_MCLK0 (0x1 << 13)
+#define RT5682S_RST_MCLK0_BIT 13
+#define RT5682S_RST_MCLK1 (0x1 << 12)
+#define RT5682S_RST_MCLK1_BIT 12
+#define RT5682S_PWR_MB1 (0x1 << 11)
+#define RT5682S_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5682S_PWR_MB1_BIT 11
+#define RT5682S_PWR_MB2 (0x1 << 10)
+#define RT5682S_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5682S_PWR_MB2_BIT 10
+#define RT5682S_PWR_JD_MASK (0x1 << 0)
+#define RT5682S_PWR_JD_ENABLE (0x1 << 0)
+#define RT5682S_PWR_JD_DISABLE (0x0 << 0)
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5682S_PWR_LDO_PLLA (0x1 << 15)
+#define RT5682S_PWR_LDO_PLLA_BIT 15
+#define RT5682S_PWR_LDO_PLLB (0x1 << 14)
+#define RT5682S_PWR_LDO_PLLB_BIT 14
+#define RT5682S_PWR_BIAS_PLLA (0x1 << 13)
+#define RT5682S_PWR_BIAS_PLLA_BIT 13
+#define RT5682S_PWR_BIAS_PLLB (0x1 << 12)
+#define RT5682S_PWR_BIAS_PLLB_BIT 12
+#define RT5682S_PWR_CBJ (0x1 << 9)
+#define RT5682S_PWR_CBJ_BIT 9
+#define RT5682S_RSTB_PLLB (0x1 << 7)
+#define RT5682S_RSTB_PLLB_BIT 7
+#define RT5682S_RSTB_PLLA (0x1 << 6)
+#define RT5682S_RSTB_PLLA_BIT 6
+#define RT5682S_PWR_PLLB (0x1 << 5)
+#define RT5682S_PWR_PLLB_BIT 5
+#define RT5682S_PWR_PLLA (0x1 << 4)
+#define RT5682S_PWR_PLLA_BIT 4
+#define RT5682S_PWR_LDO_MB2 (0x1 << 2)
+#define RT5682S_PWR_LDO_MB2_BIT 2
+#define RT5682S_PWR_LDO_MB1 (0x1 << 1)
+#define RT5682S_PWR_LDO_MB1_BIT 1
+#define RT5682S_PWR_BGLDO (0x1 << 0)
+#define RT5682S_PWR_BGLDO_BIT 0
+
+/* Power Management for Mixer (0x0066) */
+#define RT5682S_PWR_CLK_COMP_8FS (0x1 << 15)
+#define RT5682S_PWR_CLK_COMP_8FS_BIT 15
+#define RT5682S_DBG_BGLDO_MASK (0x3 << 12)
+#define RT5682S_DBG_BGLDO_SFT 12
+#define RT5682S_DBG_BGLDO_MB1_MASK (0x3 << 10)
+#define RT5682S_DBG_BGLDO_MB1_SFT 10
+#define RT5682S_DBG_BGLDO_MB2_MASK (0x3 << 8)
+#define RT5682S_DBG_BGLDO_MB2_SFT 8
+#define RT5682S_DLDO_BGLDO_MASK (0x3 << 6)
+#define RT5682S_DLDO_BGLDO_MB2_SFT 6
+#define RT5682S_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5682S_PWR_STO1_DAC_L_BIT 5
+#define RT5682S_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5682S_PWR_STO1_DAC_R_BIT 4
+#define RT5682S_DVO_BGLDO_MB1_MASK (0x3 << 2)
+#define RT5682S_DVO_BGLDO_MB1_SFT 2
+#define RT5682S_DVO_BGLDO_MB2_MASK (0x3 << 0)
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5682S_SYS_CLK_DET (0x1 << 15)
+#define RT5682S_SYS_CLK_DET_SFT 15
+#define RT5682S_PLL1_CLK_DET (0x1 << 14)
+#define RT5682S_PLL1_CLK_DET_SFT 14
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5682S_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5682S_DMIC_1_EN_SFT 15
+#define RT5682S_DMIC_1_DIS (0x0 << 15)
+#define RT5682S_DMIC_1_EN (0x1 << 15)
+#define RT5682S_FIFO_CLK_DIV_MASK (0x7 << 12)
+#define RT5682S_FIFO_CLK_DIV_2 (0x1 << 12)
+#define RT5682S_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5682S_DMIC_1_DP_SFT 4
+#define RT5682S_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5682S_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5682S_DMIC_CLK_MASK (0xf << 0)
+#define RT5682S_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5682S_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5682S_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_SFT 15
+#define RT5682S_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5682S_I2S1_TX_CHL_SFT 12
+#define RT5682S_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5682S_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5682S_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5682S_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5682S_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5682S_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5682S_I2S1_RX_CHL_SFT 8
+#define RT5682S_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5682S_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5682S_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5682S_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5682S_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5682S_I2S1_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S1_MONO_EN (0x1 << 7)
+#define RT5682S_I2S1_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S1_DL_MASK (0x7 << 4)
+#define RT5682S_I2S1_DL_SFT 4
+#define RT5682S_I2S1_DL_16 (0x0 << 4)
+#define RT5682S_I2S1_DL_20 (0x1 << 4)
+#define RT5682S_I2S1_DL_24 (0x2 << 4)
+#define RT5682S_I2S1_DL_32 (0x3 << 4)
+#define RT5682S_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0071) */
+#define RT5682S_I2S2_MS_MASK (0x1 << 15)
+#define RT5682S_I2S2_MS_SFT 15
+#define RT5682S_I2S2_MS_M (0x0 << 15)
+#define RT5682S_I2S2_MS_S (0x1 << 15)
+#define RT5682S_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5682S_I2S2_PIN_CFG_SFT 14
+#define RT5682S_I2S2_OUT_MASK (0x1 << 9)
+#define RT5682S_I2S2_OUT_SFT 9
+#define RT5682S_I2S2_OUT_UM (0x0 << 9)
+#define RT5682S_I2S2_OUT_M (0x1 << 9)
+#define RT5682S_I2S_BP_MASK (0x1 << 8)
+#define RT5682S_I2S_BP_SFT 8
+#define RT5682S_I2S_BP_NOR (0x0 << 8)
+#define RT5682S_I2S_BP_INV (0x1 << 8)
+#define RT5682S_I2S2_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S2_MONO_EN (0x1 << 7)
+#define RT5682S_I2S2_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S2_DL_MASK (0x7 << 4)
+#define RT5682S_I2S2_DL_SFT 4
+#define RT5682S_I2S2_DL_8 (0x0 << 4)
+#define RT5682S_I2S2_DL_16 (0x1 << 4)
+#define RT5682S_I2S2_DL_20 (0x2 << 4)
+#define RT5682S_I2S2_DL_24 (0x3 << 4)
+#define RT5682S_I2S2_DL_32 (0x4 << 4)
+#define RT5682S_I2S_DF_MASK (0x7)
+#define RT5682S_I2S_DF_SFT 0
+#define RT5682S_I2S_DF_I2S (0x0)
+#define RT5682S_I2S_DF_LEFT (0x1)
+#define RT5682S_I2S_DF_PCM_A (0x2)
+#define RT5682S_I2S_DF_PCM_B (0x3)
+#define RT5682S_I2S_DF_PCM_A_N (0x6)
+#define RT5682S_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5682S_ADC_OSR_MASK (0xf << 12)
+#define RT5682S_ADC_OSR_SFT 12
+#define RT5682S_ADC_OSR_D_1 (0x0 << 12)
+#define RT5682S_ADC_OSR_D_2 (0x1 << 12)
+#define RT5682S_ADC_OSR_D_4 (0x2 << 12)
+#define RT5682S_ADC_OSR_D_6 (0x3 << 12)
+#define RT5682S_ADC_OSR_D_8 (0x4 << 12)
+#define RT5682S_ADC_OSR_D_12 (0x5 << 12)
+#define RT5682S_ADC_OSR_D_16 (0x6 << 12)
+#define RT5682S_ADC_OSR_D_24 (0x7 << 12)
+#define RT5682S_ADC_OSR_D_32 (0x8 << 12)
+#define RT5682S_ADC_OSR_D_48 (0x9 << 12)
+#define RT5682S_I2S_M_D_MASK (0xf << 8)
+#define RT5682S_I2S_M_D_SFT 8
+#define RT5682S_I2S_M_D_1 (0x0 << 8)
+#define RT5682S_I2S_M_D_2 (0x1 << 8)
+#define RT5682S_I2S_M_D_3 (0x2 << 8)
+#define RT5682S_I2S_M_D_4 (0x3 << 8)
+#define RT5682S_I2S_M_D_6 (0x4 << 8)
+#define RT5682S_I2S_M_D_8 (0x5 << 8)
+#define RT5682S_I2S_M_D_12 (0x6 << 8)
+#define RT5682S_I2S_M_D_16 (0x7 << 8)
+#define RT5682S_I2S_M_D_24 (0x8 << 8)
+#define RT5682S_I2S_M_D_32 (0x9 << 8)
+#define RT5682S_I2S_M_D_48 (0x10 << 8)
+#define RT5682S_I2S_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S_M_CLK_SRC_SFT 4
+#define RT5682S_DAC_OSR_MASK (0xf << 0)
+#define RT5682S_DAC_OSR_SFT 0
+#define RT5682S_DAC_OSR_D_1 (0x0 << 0)
+#define RT5682S_DAC_OSR_D_2 (0x1 << 0)
+#define RT5682S_DAC_OSR_D_4 (0x2 << 0)
+#define RT5682S_DAC_OSR_D_6 (0x3 << 0)
+#define RT5682S_DAC_OSR_D_8 (0x4 << 0)
+#define RT5682S_DAC_OSR_D_12 (0x5 << 0)
+#define RT5682S_DAC_OSR_D_16 (0x6 << 0)
+#define RT5682S_DAC_OSR_D_24 (0x7 << 0)
+#define RT5682S_DAC_OSR_D_32 (0x8 << 0)
+#define RT5682S_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5682S_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5682S_I2S2_BCLK_MS2_SFT 11
+#define RT5682S_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5682S_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5682S_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5682S_TDM_TX_CH_2 (0x0 << 12)
+#define RT5682S_TDM_TX_CH_4 (0x1 << 12)
+#define RT5682S_TDM_TX_CH_6 (0x2 << 12)
+#define RT5682S_TDM_TX_CH_8 (0x3 << 12)
+#define RT5682S_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5682S_TDM_RX_CH_2 (0x0 << 8)
+#define RT5682S_TDM_RX_CH_4 (0x1 << 8)
+#define RT5682S_TDM_RX_CH_6 (0x2 << 8)
+#define RT5682S_TDM_RX_CH_8 (0x3 << 8)
+#define RT5682S_TDM_ADC_LCA_MASK (0x7 << 4)
+#define RT5682S_TDM_ADC_LCA_SFT 4
+#define RT5682S_TDM_ADC_DL_SFT 0
+
+/* TDM control 2 (0x007a) */
+#define RT5682S_IF1_ADC1_SEL_SFT 14
+#define RT5682S_IF1_ADC2_SEL_SFT 12
+#define RT5682S_IF1_ADC3_SEL_SFT 10
+#define RT5682S_IF1_ADC4_SEL_SFT 8
+#define RT5682S_TDM_ADC_SEL_SFT 3
+
+/* TDM control 3 (0x007b) */
+#define RT5682S_TDM_EN (0x1 << 7)
+
+/* TDM/I2S control (0x007e) */
+#define RT5682S_TDM_S_BP_MASK (0x1 << 15)
+#define RT5682S_TDM_S_BP_SFT 15
+#define RT5682S_TDM_S_BP_NOR (0x0 << 15)
+#define RT5682S_TDM_S_BP_INV (0x1 << 15)
+#define RT5682S_TDM_S_LP_MASK (0x1 << 14)
+#define RT5682S_TDM_S_LP_SFT 14
+#define RT5682S_TDM_S_LP_NOR (0x0 << 14)
+#define RT5682S_TDM_S_LP_INV (0x1 << 14)
+#define RT5682S_TDM_DF_MASK (0x7 << 11)
+#define RT5682S_TDM_DF_SFT 11
+#define RT5682S_TDM_DF_I2S (0x0 << 11)
+#define RT5682S_TDM_DF_LEFT (0x1 << 11)
+#define RT5682S_TDM_DF_PCM_A (0x2 << 11)
+#define RT5682S_TDM_DF_PCM_B (0x3 << 11)
+#define RT5682S_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5682S_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5682S_TDM_BCLK_MS1_MASK (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_SFT 8
+#define RT5682S_TDM_BCLK_MS1_32 (0x0 << 8)
+#define RT5682S_TDM_BCLK_MS1_64 (0x1 << 8)
+#define RT5682S_TDM_BCLK_MS1_128 (0x2 << 8)
+#define RT5682S_TDM_BCLK_MS1_256 (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_16 (0x4 << 8)
+#define RT5682S_TDM_CL_MASK (0x3 << 4)
+#define RT5682S_TDM_CL_16 (0x0 << 4)
+#define RT5682S_TDM_CL_20 (0x1 << 4)
+#define RT5682S_TDM_CL_24 (0x2 << 4)
+#define RT5682S_TDM_CL_32 (0x3 << 4)
+#define RT5682S_TDM_M_BP_MASK (0x1 << 2)
+#define RT5682S_TDM_M_BP_SFT 2
+#define RT5682S_TDM_M_BP_NOR (0x0 << 2)
+#define RT5682S_TDM_M_BP_INV (0x1 << 2)
+#define RT5682S_TDM_M_LP_MASK (0x1 << 1)
+#define RT5682S_TDM_M_LP_SFT 1
+#define RT5682S_TDM_M_LP_NOR (0x0 << 1)
+#define RT5682S_TDM_M_LP_INV (0x1 << 1)
+#define RT5682S_TDM_MS_MASK (0x1 << 0)
+#define RT5682S_TDM_MS_SFT 0
+#define RT5682S_TDM_MS_S (0x0 << 0)
+#define RT5682S_TDM_MS_M (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5682S_SCLK_SRC_MASK (0x7 << 13)
+#define RT5682S_SCLK_SRC_SFT 13
+#define RT5682S_PLL_SRC_MASK (0x3 << 8)
+#define RT5682S_PLL_SRC_SFT 8
+#define RT5682S_PLL_SRC_MCLK (0x0 << 8)
+#define RT5682S_PLL_SRC_BCLK1 (0x1 << 8)
+#define RT5682S_PLL_SRC_RC (0x3 << 8)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5682S_DA_ASRC_MASK (0x1 << 13)
+#define RT5682S_DA_ASRC_SFT 13
+#define RT5682S_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5682S_DAC_STO1_ASRC_SFT 12
+#define RT5682S_AD_ASRC_MASK (0x1 << 8)
+#define RT5682S_AD_ASRC_SFT 8
+#define RT5682S_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5682S_AD_ASRC_SEL_SFT 4
+#define RT5682S_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5682S_DMIC_ASRC_SFT 3
+#define RT5682S_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5682S_ADC_STO1_ASRC_SFT 2
+#define RT5682S_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5682S_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5682S_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5682S_FILTER_CLK_SEL_SFT 12
+#define RT5682S_FILTER_CLK_DIV_MASK (0xf << 8)
+#define RT5682S_FILTER_CLK_DIV_SFT 8
+
+/* ASRC Control 4 (0x0086) */
+#define RT5682S_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5682S_ASRCIN_FTK_N1_SFT 14
+#define RT5682S_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5682S_ASRCIN_FTK_N2_SFT 12
+#define RT5682S_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5682S_ASRCIN_FTK_M1_SFT 8
+#define RT5682S_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5682S_ASRCIN_FTK_M2_SFT 4
+
+/* ASRC Control 11 (0x008c) */
+#define RT5682S_ASRCIN_AUTO_CLKOUT_MASK (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_EN (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_DIS (0x0 << 5)
+#define RT5682S_ASRCIN_AUTO_RST_MASK (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_EN (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_DIS (0x0 << 4)
+#define RT5682S_SEL_LRCK_DET_MASK (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV8 (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV4 (0x2)
+#define RT5682S_SEL_LRCK_DET_DIV2 (0x1)
+#define RT5682S_SEL_LRCK_DET_DIV1 (0x0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5682S_OUT_HP_L_EN (0x1 << 6)
+#define RT5682S_OUT_HP_R_EN (0x1 << 5)
+#define RT5682S_LDO_PUMP_EN (0x1 << 4)
+#define RT5682S_LDO_PUMP_EN_SFT 4
+#define RT5682S_PUMP_EN (0x1 << 3)
+#define RT5682S_PUMP_EN_SFT 3
+#define RT5682S_CAPLESS_L_EN (0x1 << 1)
+#define RT5682S_CAPLESS_L_EN_SFT 1
+#define RT5682S_CAPLESS_R_EN (0x1 << 0)
+#define RT5682S_CAPLESS_R_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5682S_RAMP_MASK (0x1 << 12)
+#define RT5682S_RAMP_SFT 12
+#define RT5682S_RAMP_DIS (0x0 << 12)
+#define RT5682S_RAMP_EN (0x1 << 12)
+#define RT5682S_BPS_MASK (0x1 << 11)
+#define RT5682S_BPS_SFT 11
+#define RT5682S_BPS_DIS (0x0 << 11)
+#define RT5682S_BPS_EN (0x1 << 11)
+#define RT5682S_FAST_UPDN_MASK (0x1 << 10)
+#define RT5682S_FAST_UPDN_SFT 10
+#define RT5682S_FAST_UPDN_DIS (0x0 << 10)
+#define RT5682S_FAST_UPDN_EN (0x1 << 10)
+#define RT5682S_VLO_MASK (0x1 << 7)
+#define RT5682S_VLO_SFT 7
+#define RT5682S_VLO_3V (0x0 << 7)
+#define RT5682S_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5682S_OSW_L_MASK (0x1 << 11)
+#define RT5682S_OSW_L_SFT 11
+#define RT5682S_OSW_L_DIS (0x0 << 11)
+#define RT5682S_OSW_L_EN (0x1 << 11)
+#define RT5682S_OSW_R_MASK (0x1 << 10)
+#define RT5682S_OSW_R_SFT 10
+#define RT5682S_OSW_R_DIS (0x0 << 10)
+#define RT5682S_OSW_R_EN (0x1 << 10)
+#define RT5682S_PM_HP_MASK (0x3 << 8)
+#define RT5682S_PM_HP_SFT 8
+#define RT5682S_PM_HP_LV (0x0 << 8)
+#define RT5682S_PM_HP_MV (0x1 << 8)
+#define RT5682S_PM_HP_HV (0x2 << 8)
+
+/* Micbias Control1 (0x93) */
+#define RT5682S_MIC1_OV_MASK (0x3 << 14)
+#define RT5682S_MIC1_OV_SFT 14
+#define RT5682S_MIC1_OV_2V7 (0x0 << 14)
+#define RT5682S_MIC1_OV_2V4 (0x1 << 14)
+#define RT5682S_MIC1_OV_2V25 (0x3 << 14)
+#define RT5682S_MIC1_OV_1V8 (0x4 << 14)
+#define RT5682S_MIC2_OV_MASK (0x3 << 8)
+#define RT5682S_MIC2_OV_SFT 8
+#define RT5682S_MIC2_OV_2V7 (0x0 << 8)
+#define RT5682S_MIC2_OV_2V4 (0x1 << 8)
+#define RT5682S_MIC2_OV_2V25 (0x3 << 8)
+#define RT5682S_MIC2_OV_1V8 (0x4 << 8)
+
+/* Micbias Control2 (0x0094) */
+#define RT5682S_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5682S_PWR_CLK25M_SFT 9
+#define RT5682S_PWR_CLK25M_PD (0x0 << 9)
+#define RT5682S_PWR_CLK25M_PU (0x1 << 9)
+#define RT5682S_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5682S_PWR_CLK1M_SFT 8
+#define RT5682S_PWR_CLK1M_PD (0x0 << 8)
+#define RT5682S_PWR_CLK1M_PU (0x1 << 8)
+
+/* PLL M/N/K Code Control 1 (0x0098) */
+#define RT5682S_PLLA_N_MASK (0x1ff << 0)
+
+/* PLL M/N/K Code Control 2 (0x0099) */
+#define RT5682S_PLLA_M_MASK (0x1f << 8)
+#define RT5682S_PLLA_M_SFT 8
+#define RT5682S_PLLA_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 3 (0x009a) */
+#define RT5682S_PLLB_N_MASK (0x3ff << 0)
+
+/* PLL M/N/K Code Control 4 (0x009b) */
+#define RT5682S_PLLB_M_MASK (0x1f << 8)
+#define RT5682S_PLLB_M_SFT 8
+#define RT5682S_PLLB_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 6 (0x009d) */
+#define RT5682S_PLLB_SEL_PS_MASK (0x1 << 13)
+#define RT5682S_PLLB_SEL_PS_SFT 13
+#define RT5682S_PLLB_BYP_PS_MASK (0x1 << 12)
+#define RT5682S_PLLB_BYP_PS_SFT 12
+#define RT5682S_PLLB_M_BP_MASK (0x1 << 11)
+#define RT5682S_PLLB_M_BP_SFT 11
+#define RT5682S_PLLB_K_BP_MASK (0x1 << 10)
+#define RT5682S_PLLB_K_BP_SFT 10
+#define RT5682S_PLLA_M_BP_MASK (0x1 << 7)
+#define RT5682S_PLLA_M_BP_SFT 7
+#define RT5682S_PLLA_K_BP_MASK (0x1 << 6)
+#define RT5682S_PLLA_K_BP_SFT 6
+
+/* PLL M/N/K Code Control 7 (0x009e) */
+#define RT5682S_PLLB_SRC_MASK (0x1)
+#define RT5682S_PLLB_SRC_DFIN (0x1)
+#define RT5682S_PLLB_SRC_PLLA (0x0)
+
+/* RC Clock Control (0x009f) */
+#define RT5682S_POW_IRQ (0x1 << 15)
+#define RT5682S_POW_JDH (0x1 << 14)
+
+/* I2S2 Master Mode Clock Control 1 (0x00a0) */
+#define RT5682S_I2S2_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S2_M_CLK_SRC_SFT 4
+#define RT5682S_I2S2_M_D_MASK (0xf << 0)
+#define RT5682S_I2S2_M_D_1 (0x0)
+#define RT5682S_I2S2_M_D_2 (0x1)
+#define RT5682S_I2S2_M_D_3 (0x2)
+#define RT5682S_I2S2_M_D_4 (0x3)
+#define RT5682S_I2S2_M_D_6 (0x4)
+#define RT5682S_I2S2_M_D_8 (0x5)
+#define RT5682S_I2S2_M_D_12 (0x6)
+#define RT5682S_I2S2_M_D_16 (0x7)
+#define RT5682S_I2S2_M_D_24 (0x8)
+#define RT5682S_I2S2_M_D_32 (0x9)
+#define RT5682S_I2S2_M_D_48 (0xa)
+#define RT5682S_I2S2_M_D_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5682S_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5682S_JD1_PULSE_EN_SFT 10
+#define RT5682S_JD1_PULSE_DIS (0x0 << 10)
+#define RT5682S_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5682S_JD1_EN_MASK (0x1 << 15)
+#define RT5682S_JD1_EN_SFT 15
+#define RT5682S_JD1_DIS (0x0 << 15)
+#define RT5682S_JD1_EN (0x1 << 15)
+#define RT5682S_JD1_POL_MASK (0x1 << 13)
+#define RT5682S_JD1_POL_NOR (0x0 << 13)
+#define RT5682S_JD1_POL_INV (0x1 << 13)
+#define RT5682S_JD1_IRQ_MASK (0x1 << 10)
+#define RT5682S_JD1_IRQ_LEV (0x0 << 10)
+#define RT5682S_JD1_IRQ_PUL (0x1 << 10)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5682S_IL_IRQ_MASK (0x1 << 7)
+#define RT5682S_IL_IRQ_DIS (0x0 << 7)
+#define RT5682S_IL_IRQ_EN (0x1 << 7)
+#define RT5682S_IL_IRQ_TYPE_MASK (0x1 << 4)
+#define RT5682S_IL_IRQ_LEV (0x0 << 4)
+#define RT5682S_IL_IRQ_PUL (0x1 << 4)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5682S_GP1_PIN_MASK (0x3 << 14)
+#define RT5682S_GP1_PIN_SFT 14
+#define RT5682S_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5682S_GP1_PIN_IRQ (0x1 << 14)
+#define RT5682S_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5682S_GP2_PIN_MASK (0x3 << 12)
+#define RT5682S_GP2_PIN_SFT 12
+#define RT5682S_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5682S_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5682S_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5682S_GP3_PIN_MASK (0x3 << 10)
+#define RT5682S_GP3_PIN_SFT 10
+#define RT5682S_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5682S_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5682S_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5682S_GP4_PIN_MASK (0x3 << 8)
+#define RT5682S_GP4_PIN_SFT 8
+#define RT5682S_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5682S_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5682S_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5682S_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5682S_GP5_PIN_MASK (0x3 << 6)
+#define RT5682S_GP5_PIN_SFT 6
+#define RT5682S_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5682S_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5682S_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5682S_GP6_PIN_MASK (0x1 << 5)
+#define RT5682S_GP6_PIN_SFT 5
+#define RT5682S_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5682S_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5682S_GP1_PF_MASK (0x1 << 15)
+#define RT5682S_GP1_PF_IN (0x0 << 15)
+#define RT5682S_GP1_PF_OUT (0x1 << 15)
+#define RT5682S_GP1_OUT_MASK (0x1 << 14)
+#define RT5682S_GP1_OUT_L (0x0 << 14)
+#define RT5682S_GP1_OUT_H (0x1 << 14)
+#define RT5682S_GP2_PF_MASK (0x1 << 13)
+#define RT5682S_GP2_PF_IN (0x0 << 13)
+#define RT5682S_GP2_PF_OUT (0x1 << 13)
+#define RT5682S_GP2_OUT_MASK (0x1 << 12)
+#define RT5682S_GP2_OUT_L (0x0 << 12)
+#define RT5682S_GP2_OUT_H (0x1 << 12)
+#define RT5682S_GP3_PF_MASK (0x1 << 11)
+#define RT5682S_GP3_PF_IN (0x0 << 11)
+#define RT5682S_GP3_PF_OUT (0x1 << 11)
+#define RT5682S_GP3_OUT_MASK (0x1 << 10)
+#define RT5682S_GP3_OUT_L (0x0 << 10)
+#define RT5682S_GP3_OUT_H (0x1 << 10)
+#define RT5682S_GP4_PF_MASK (0x1 << 9)
+#define RT5682S_GP4_PF_IN (0x0 << 9)
+#define RT5682S_GP4_PF_OUT (0x1 << 9)
+#define RT5682S_GP4_OUT_MASK (0x1 << 8)
+#define RT5682S_GP4_OUT_L (0x0 << 8)
+#define RT5682S_GP4_OUT_H (0x1 << 8)
+#define RT5682S_GP5_PF_MASK (0x1 << 7)
+#define RT5682S_GP5_PF_IN (0x0 << 7)
+#define RT5682S_GP5_PF_OUT (0x1 << 7)
+#define RT5682S_GP5_OUT_MASK (0x1 << 6)
+#define RT5682S_GP5_OUT_L (0x0 << 6)
+#define RT5682S_GP5_OUT_H (0x1 << 6)
+#define RT5682S_GP6_PF_MASK (0x1 << 5)
+#define RT5682S_GP6_PF_IN (0x0 << 5)
+#define RT5682S_GP6_PF_OUT (0x1 << 5)
+#define RT5682S_GP6_OUT_MASK (0x1 << 4)
+#define RT5682S_GP6_OUT_L (0x0 << 4)
+#define RT5682S_GP6_OUT_H (0x1 << 4)
+
+/* GPIO Status (0x00c2) */
+#define RT5682S_GP6_ST (0x1 << 6)
+#define RT5682S_GP5_ST (0x1 << 5)
+#define RT5682S_GP4_ST (0x1 << 4)
+#define RT5682S_GP3_ST (0x1 << 3)
+#define RT5682S_GP2_ST (0x1 << 2)
+#define RT5682S_GP1_ST (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5682S_ZCD_MASK (0x1 << 10)
+#define RT5682S_ZCD_SFT 10
+#define RT5682S_ZCD_PD (0x0 << 10)
+#define RT5682S_ZCD_PU (0x1 << 10)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5682S_4BTN_IL_MASK (0x1 << 15)
+#define RT5682S_4BTN_IL_EN (0x1 << 15)
+#define RT5682S_4BTN_IL_DIS (0x0 << 15)
+#define RT5682S_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5682S_4BTN_IL_NOR (0x1 << 14)
+#define RT5682S_4BTN_IL_RST (0x0 << 14)
+
+/* 4 Button Inline Command Control 3~6 (0x00e5~0x00e8) */
+#define RT5682S_4BTN_IL_HOLD_WIN_MASK (0x7f << 8)
+#define RT5682S_4BTN_IL_HOLD_WIN_SFT 8
+#define RT5682S_4BTN_IL_CLICK_WIN_MASK (0x7f)
+#define RT5682S_4BTN_IL_CLICK_WIN_SFT 0
+
+/* Analog JD Control (0x00f0) */
+#define RT5682S_JDH_RS_MASK (0x1 << 4)
+#define RT5682S_JDH_NO_PLUG (0x1 << 4)
+#define RT5682S_JDH_PLUG (0x0 << 4)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682S_CP_CLK_HP_MASK (0x3 << 4)
+#define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4)
+#define RT5682S_CP_CLK_HP_200KHZ (0x1 << 4)
+#define RT5682S_CP_CLK_HP_300KHZ (0x2 << 4)
+#define RT5682S_CP_CLK_HP_600KHZ (0x3 << 4)
+
+/* Pad Driving Control (0x0136) */
+#define RT5682S_PAD_DRV_GP1_MASK (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_HIGH (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_LOW (0x0 << 14)
+#define RT5682S_PAD_DRV_GP2_MASK (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_HIGH (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_LOW (0x0 << 12)
+#define RT5682S_PAD_DRV_GP3_MASK (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_HIGH (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_LOW (0x0 << 10)
+#define RT5682S_PAD_DRV_GP4_MASK (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_HIGH (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_LOW (0x0 << 8)
+#define RT5682S_PAD_DRV_GP5_MASK (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_HIGH (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_LOW (0x0 << 6)
+#define RT5682S_PAD_DRV_GP6_MASK (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_HIGH (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_LOW (0x0 << 4)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5682S_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_DAC1_SFT 13
+#define RT5682S_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5682S_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_ADC1_SFT 13
+#define RT5682S_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5682S_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5682S_AD2DA_LB_MASK (0x1 << 10)
+#define RT5682S_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5682S_NG2_EN_MASK (0x1 << 15)
+#define RT5682S_NG2_EN (0x1 << 15)
+#define RT5682S_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5682S_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5682S_DEB_80_MS (0x0 << 4)
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682S_HP_SIG_SRC_MASK (0x3)
+#define RT5682S_HP_SIG_SRC_1BIT_CTL (0x3)
+#define RT5682S_HP_SIG_SRC_REG (0x2)
+#define RT5682S_HP_SIG_SRC_IMPE_REG (0x1)
+#define RT5682S_HP_SIG_SRC_DC_CALI (0x0)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5682S_SAR_BUTDET_MASK (0x1 << 15)
+#define RT5682S_SAR_BUTDET_EN (0x1 << 15)
+#define RT5682S_SAR_BUTDET_DIS (0x0 << 15)
+#define RT5682S_SAR_BUTDET_POW_MASK (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5682S_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST_NORM (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST (0x0 << 13)
+#define RT5682S_SAR_POW_MASK (0x1 << 12)
+#define RT5682S_SAR_POW_EN (0x1 << 12)
+#define RT5682S_SAR_POW_DIS (0x0 << 12)
+#define RT5682S_SAR_RST_MASK (0x1 << 11)
+#define RT5682S_SAR_RST_NORMAL (0x1 << 11)
+#define RT5682S_SAR_RST (0x0 << 11)
+#define RT5682S_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5682S_SAR_BYPASS_EN (0x1 << 10)
+#define RT5682S_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5682S_SAR_SEL_MB1_2_MASK (0x3 << 8)
+#define RT5682S_SAR_SEL_MB1_2_SFT 8
+#define RT5682S_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5682S_SAR_SEL_MB1_2_CTL_MASK (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_AUTO (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_MANU (0x0 << 5)
+#define RT5682S_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 2 (0x0211) */
+#define RT5682S_SAR_ADC_PSV_MASK (0x1 << 4)
+#define RT5682S_SAR_ADC_PSV_ENTRY (0x1 << 4)
+
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5682S_SAR_SOUR_MASK (0x3f)
+#define RT5682S_SAR_SOUR_BTN (0x3f)
+#define RT5682S_SAR_SOUR_TYPE (0x0)
+
+/* Headphone Amp Detection Control 1 (0x3b00) */
+#define RT5682S_CP_SW_SIZE_MASK (0x7 << 4)
+#define RT5682S_CP_SW_SIZE_L (0x4 << 4)
+#define RT5682S_CP_SW_SIZE_M (0x2 << 4)
+#define RT5682S_CP_SW_SIZE_S (0x1 << 4)
+
+#define RT5682S_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5682S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+/* System Clock Source */
+enum {
+ RT5682S_SCLK_S_MCLK,
+ RT5682S_SCLK_S_PLL1,
+ RT5682S_SCLK_S_PLL2,
+ RT5682S_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5682S_PLL_S_MCLK,
+ RT5682S_PLL_S_BCLK1,
+ RT5682S_PLL_S_BCLK2,
+ RT5682S_PLL_S_RCCLK,
+};
+
+enum {
+ RT5682S_PLL1,
+ RT5682S_PLL2,
+ RT5682S_PLLS,
+};
+
+enum {
+ RT5682S_AIF1,
+ RT5682S_AIF2,
+ RT5682S_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5682S_DA_STEREO1_FILTER = 0x1,
+ RT5682S_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5682S_CLK_SEL_SYS,
+ RT5682S_CLK_SEL_I2S1_ASRC,
+ RT5682S_CLK_SEL_I2S2_ASRC,
+};
+
+enum {
+ USE_PLLA,
+ USE_PLLB,
+ USE_PLLAB,
+};
+
+struct pll_calc_map {
+ unsigned int freq_in;
+ unsigned int freq_out;
+ int m;
+ int n;
+ int k;
+ bool m_bp;
+ bool k_bp;
+ bool byp_ps;
+ bool sel_ps;
+};
+
+#define RT5682S_NUM_SUPPLIES 2
+
+struct rt5682s_priv {
+ struct snd_soc_component *component;
+ struct rt5682s_platform_data pdata;
+ struct regmap *regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+ struct mutex sar_mutex;
+ struct mutex jdet_mutex;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS];
+ struct clk *mclk;
+#endif
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5682S_AIFS];
+ int bclk[RT5682S_AIFS];
+ int master[RT5682S_AIFS];
+
+ int pll_src[RT5682S_PLLS];
+ int pll_in[RT5682S_PLLS];
+ int pll_out[RT5682S_PLLS];
+ int pll_comb;
+
+ int jack_type;
+ int irq_work_delay_time;
+};
+
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5682S_H__ */
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
new file mode 100644
index 000000000000..f9574980a407
--- /dev/null
+++ b/sound/soc/codecs/rt9120.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RT9120_REG_DEVID 0x00
+#define RT9120_REG_I2SFMT 0x02
+#define RT9120_REG_I2SWL 0x03
+#define RT9120_REG_SDIOSEL 0x04
+#define RT9120_REG_SYSCTL 0x05
+#define RT9120_REG_SPKGAIN 0x07
+#define RT9120_REG_VOLRAMP 0x0A
+#define RT9120_REG_ERRRPT 0x10
+#define RT9120_REG_MSVOL 0x20
+#define RT9120_REG_SWRESET 0x40
+#define RT9120_REG_INTERNAL0 0x65
+#define RT9120_REG_INTERNAL1 0x69
+#define RT9120_REG_UVPOPT 0x6C
+
+#define RT9120_VID_MASK GENMASK(15, 8)
+#define RT9120_SWRST_MASK BIT(7)
+#define RT9120_MUTE_MASK GENMASK(5, 4)
+#define RT9120_I2SFMT_MASK GENMASK(4, 2)
+#define RT9120_I2SFMT_SHIFT 2
+#define RT9120_CFG_FMT_I2S 0
+#define RT9120_CFG_FMT_LEFTJ 1
+#define RT9120_CFG_FMT_RIGHTJ 2
+#define RT9120_CFG_FMT_DSPA 3
+#define RT9120_CFG_FMT_DSPB 7
+#define RT9120_AUDBIT_MASK GENMASK(1, 0)
+#define RT9120_CFG_AUDBIT_16 0
+#define RT9120_CFG_AUDBIT_20 1
+#define RT9120_CFG_AUDBIT_24 2
+#define RT9120_AUDWL_MASK GENMASK(5, 0)
+#define RT9120_CFG_WORDLEN_16 16
+#define RT9120_CFG_WORDLEN_24 24
+#define RT9120_CFG_WORDLEN_32 32
+#define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4)
+
+#define RT9120_VENDOR_ID 0x4200
+#define RT9120_RESET_WAITMS 20
+#define RT9120_CHIPON_WAITMS 20
+#define RT9120_AMPON_WAITMS 50
+#define RT9120_AMPOFF_WAITMS 100
+#define RT9120_LVAPP_THRESUV 2000000
+
+/* 8000 to 192000 supported , only 176400 not support */
+#define RT9120_RATES_MASK (SNDRV_PCM_RATE_8000_192000 &\
+ ~SNDRV_PCM_RATE_176400)
+#define RT9120_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct rt9120_data {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+/* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -1039375, 625, 1);
+
+/* {6, 8, 10, 12, 13, 14, 15, 16}dB */
+static const DECLARE_TLV_DB_RANGE(classd_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(600, 200, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(1300, 100, 0)
+);
+
+static const char * const sdo_select_text[] = {
+ "None", "INTF", "Final", "RMS Detect"
+};
+
+static const struct soc_enum sdo_select_enum =
+ SOC_ENUM_SINGLE(RT9120_REG_SDIOSEL, 4, ARRAY_SIZE(sdo_select_text),
+ sdo_select_text);
+
+static const struct snd_kcontrol_new rt9120_snd_controls[] = {
+ SOC_SINGLE_TLV("MS Volume", RT9120_REG_MSVOL, 0, 2047, 1, digital_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RT9120_REG_SPKGAIN, 0, 7, 0, classd_tlv),
+ SOC_SINGLE("PBTL Switch", RT9120_REG_SYSCTL, 3, 1, 0),
+ SOC_ENUM("SDO Select", sdo_select_enum),
+};
+
+static int internal_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(comp, RT9120_REG_ERRRPT, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(RT9120_AMPON_WAITMS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(RT9120_AMPOFF_WAITMS);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9120_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("DMIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("LDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("RDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("PWND", RT9120_REG_SYSCTL, 6, 1,
+ internal_power_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA("SPKL PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKR PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route rt9120_dapm_routes[] = {
+ { "DMIX", NULL, "AIF Playback" },
+ /* SPKL */
+ { "LDAC", NULL, "PWND" },
+ { "LDAC", NULL, "DMIX" },
+ { "SPKL PA", NULL, "LDAC" },
+ { "SPKL", NULL, "SPKL PA" },
+ /* SPKR */
+ { "RDAC", NULL, "PWND" },
+ { "RDAC", NULL, "DMIX" },
+ { "SPKR PA", NULL, "RDAC" },
+ { "SPKR", NULL, "SPKR PA" },
+ /* Cap */
+ { "AIF Capture", NULL, "LDAC" },
+ { "AIF Capture", NULL, "RDAC" },
+};
+
+static int rt9120_codec_probe(struct snd_soc_component *comp)
+{
+ struct rt9120_data *data = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, data->regmap);
+
+ /* Internal setting */
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL1, 0x03);
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x69);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt9120_component_driver = {
+ .probe = rt9120_codec_probe,
+ .controls = rt9120_snd_controls,
+ .num_controls = ARRAY_SIZE(rt9120_snd_controls),
+ .dapm_widgets = rt9120_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets),
+ .dapm_routes = rt9120_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes),
+};
+
+static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int format;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = RT9120_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = RT9120_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = RT9120_CFG_FMT_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = RT9120_CFG_FMT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = RT9120_CFG_FMT_DSPB;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_I2SFMT_MASK,
+ format << RT9120_I2SFMT_SHIFT);
+ return 0;
+}
+
+static int rt9120_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int param_width, param_slot_width;
+ int width;
+
+ switch (width = params_width(param)) {
+ case 16:
+ param_width = RT9120_CFG_AUDBIT_16;
+ break;
+ case 20:
+ param_width = RT9120_CFG_AUDBIT_20;
+ break;
+ case 24:
+ case 32:
+ param_width = RT9120_CFG_AUDBIT_24;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported data width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_AUDBIT_MASK, param_width);
+
+ switch (width = params_physical_width(param)) {
+ case 16:
+ param_slot_width = RT9120_CFG_WORDLEN_16;
+ break;
+ case 24:
+ param_slot_width = RT9120_CFG_WORDLEN_24;
+ break;
+ case 32:
+ param_slot_width = RT9120_CFG_WORDLEN_32;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported slot width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SWL,
+ RT9120_AUDWL_MASK, param_slot_width);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9120_dai_ops = {
+ .set_fmt = rt9120_set_fmt,
+ .hw_params = rt9120_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9120_dai = {
+ .name = "rt9120_aif",
+ .playback = {
+ .stream_name = "AIF Playback",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9120_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct regmap_range rt9120_rd_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x0C),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x40),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C)
+};
+
+static const struct regmap_access_table rt9120_rd_table = {
+ .yes_ranges = rt9120_rd_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_rd_yes_ranges),
+};
+
+static const struct regmap_range rt9120_wr_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x00),
+ regmap_reg_range(0x02, 0x0A),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x3D),
+ regmap_reg_range(0x40, 0x40),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C)
+};
+
+static const struct regmap_access_table rt9120_wr_table = {
+ .yes_ranges = rt9120_wr_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges),
+};
+
+static int rt9120_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x00:
+ case 0x09:
+ case 0x20 ... 0x27:
+ return 2;
+ case 0x30 ... 0x3D:
+ return 3;
+ case 0x3E ... 0x3F:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ u8 raw[4] = {0};
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, raw);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ switch (size) {
+ case 4:
+ *val = be32_to_cpup((__be32 *)raw);
+ break;
+ case 3:
+ *val = raw[0] << 16 | raw[1] << 8 | raw[0];
+ break;
+ case 2:
+ *val = be16_to_cpup((__be16 *)raw);
+ break;
+ default:
+ *val = raw[0];
+ }
+
+ return 0;
+}
+
+static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ __be32 be32_val;
+ u8 *rawp = (u8 *)&be32_val;
+ int offs = 4 - size;
+
+ be32_val = cpu_to_be32(val);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs);
+}
+
+static const struct regmap_config rt9120_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT9120_REG_UVPOPT,
+
+ .reg_read = rt9120_reg_read,
+ .reg_write = rt9120_reg_write,
+
+ .wr_table = &rt9120_wr_table,
+ .rd_table = &rt9120_rd_table,
+};
+
+static int rt9120_check_vendor_info(struct rt9120_data *data)
+{
+ unsigned int devid;
+ int ret;
+
+ ret = regmap_read(data->regmap, RT9120_REG_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ if ((devid & RT9120_VID_MASK) != RT9120_VENDOR_ID) {
+ dev_err(data->dev, "DEVID not correct [0x%04x]\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt9120_do_register_reset(struct rt9120_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, RT9120_REG_SWRESET,
+ RT9120_SWRST_MASK);
+ if (ret)
+ return ret;
+
+ msleep(RT9120_RESET_WAITMS);
+ return 0;
+}
+
+static int rt9120_probe(struct i2c_client *i2c)
+{
+ struct rt9120_data *data;
+ struct gpio_desc *pwdnn_gpio;
+ struct regulator *dvdd_supply;
+ int dvdd_supply_volt, ret;
+
+ data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &i2c->dev;
+ i2c_set_clientdata(i2c, data);
+
+ pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(pwdnn_gpio)) {
+ dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n");
+ return PTR_ERR(pwdnn_gpio);
+ } else if (pwdnn_gpio) {
+ dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n");
+ msleep(RT9120_CHIPON_WAITMS);
+ }
+
+ data->regmap = devm_regmap_init(&i2c->dev, NULL, data,
+ &rt9120_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap [%d]\n", ret);
+ return ret;
+ }
+
+ ret = rt9120_check_vendor_info(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to check vendor info\n");
+ return ret;
+ }
+
+ ret = rt9120_do_register_reset(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to do register reset\n");
+ return ret;
+ }
+
+ dvdd_supply = devm_regulator_get(&i2c->dev, "dvdd");
+ if (IS_ERR(dvdd_supply)) {
+ dev_err(&i2c->dev, "No dvdd regulator found\n");
+ return PTR_ERR(dvdd_supply);
+ }
+
+ dvdd_supply_volt = regulator_get_voltage(dvdd_supply);
+ if (dvdd_supply_volt <= RT9120_LVAPP_THRESUV) {
+ dev_dbg(&i2c->dev, "dvdd low voltage design\n");
+ ret = regmap_update_bits(data->regmap, RT9120_REG_UVPOPT,
+ RT9120_DVDD_UVSEL_MASK, 0);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to config dvdd uvsel\n");
+ return ret;
+ }
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &rt9120_component_driver,
+ &rt9120_dai, 1);
+}
+
+static const struct of_device_id __maybe_unused rt9120_device_table[] = {
+ { .compatible = "richtek,rt9120", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt9120_device_table);
+
+static struct i2c_driver rt9120_driver = {
+ .driver = {
+ .name = "rt9120",
+ .of_match_table = rt9120_device_table,
+ },
+ .probe_new = rt9120_probe,
+};
+module_i2c_driver(rt9120_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RT9120 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c
index 643b45188b6f..eb2a7870148d 100644
--- a/sound/soc/codecs/tfa989x.c
+++ b/sound/soc/codecs/tfa989x.c
@@ -19,6 +19,7 @@
#define TFA989X_REVISIONNUMBER 0x03
#define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */
#define TFA989X_I2SREG 0x04
+#define TFA989X_I2SREG_RCV 2 /* receiver mode */
#define TFA989X_I2SREG_CHSA 6 /* amplifier input select */
#define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6)
#define TFA989X_I2SREG_I2SSR 12 /* sample rate */
@@ -53,6 +54,7 @@ struct tfa989x_rev {
};
struct tfa989x {
+ const struct tfa989x_rev *rev;
struct regulator *vddd_supply;
};
@@ -97,7 +99,25 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = {
{"Amp Input", "Right", "AIFINR"},
};
+static const char * const mode_text[] = { "Speaker", "Receiver" };
+static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text);
+static const struct snd_kcontrol_new tfa989x_mode_controls[] = {
+ SOC_ENUM("Mode", mode_enum),
+};
+
+static int tfa989x_probe(struct snd_soc_component *component)
+{
+ struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component);
+
+ if (tfa989x->rev->rev == TFA9897_REVISION)
+ return snd_soc_add_component_controls(component, tfa989x_mode_controls,
+ ARRAY_SIZE(tfa989x_mode_controls));
+
+ return 0;
+}
+
static const struct snd_soc_component_driver tfa989x_component = {
+ .probe = tfa989x_probe,
.dapm_widgets = tfa989x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets),
.dapm_routes = tfa989x_dapm_routes,
@@ -273,6 +293,7 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c)
if (!tfa989x)
return -ENOMEM;
+ tfa989x->rev = rev;
i2c_set_clientdata(i2c, tfa989x);
tfa989x->vddd_supply = devm_regulator_get(dev, "vddd");
diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c
index 04ad38311360..ed70e3d9baf2 100644
--- a/sound/soc/codecs/tlv320aic32x4-i2c.c
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -44,7 +44,9 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
static int aic32x4_i2c_remove(struct i2c_client *i2c)
{
- return aic32x4_remove(&i2c->dev);
+ aic32x4_remove(&i2c->dev);
+
+ return 0;
}
static const struct i2c_device_id aic32x4_i2c_id[] = {
diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c
index e81c72958a82..a8958cd1c692 100644
--- a/sound/soc/codecs/tlv320aic32x4-spi.c
+++ b/sound/soc/codecs/tlv320aic32x4-spi.c
@@ -48,7 +48,9 @@ static int aic32x4_spi_probe(struct spi_device *spi)
static int aic32x4_spi_remove(struct spi_device *spi)
{
- return aic32x4_remove(&spi->dev);
+ aic32x4_remove(&spi->dev);
+
+ return 0;
}
static const struct spi_device_id aic32x4_spi_id[] = {
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index d39c7d52ecfd..8f42fd7bc053 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -1418,13 +1418,11 @@ err_disable_regulators:
}
EXPORT_SYMBOL(aic32x4_probe);
-int aic32x4_remove(struct device *dev)
+void aic32x4_remove(struct device *dev)
{
struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev);
aic32x4_disable_regulators(aic32x4);
-
- return 0;
}
EXPORT_SYMBOL(aic32x4_remove);
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
index e9fd2e55d6c3..4de5bd9e8cc5 100644
--- a/sound/soc/codecs/tlv320aic32x4.h
+++ b/sound/soc/codecs/tlv320aic32x4.h
@@ -18,7 +18,7 @@ enum aic32x4_type {
extern const struct regmap_config aic32x4_regmap_config;
int aic32x4_probe(struct device *dev, struct regmap *regmap);
-int aic32x4_remove(struct device *dev);
+void aic32x4_remove(struct device *dev);
int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
/* tlv320aic32x4 register space (in decimal to match datasheet) */
diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c
index cd0558ed4dd4..2f272bc3f5da 100644
--- a/sound/soc/codecs/tlv320aic3x-i2c.c
+++ b/sound/soc/codecs/tlv320aic3x-i2c.c
@@ -32,7 +32,9 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
static int aic3x_i2c_remove(struct i2c_client *i2c)
{
- return aic3x_remove(&i2c->dev);
+ aic3x_remove(&i2c->dev);
+
+ return 0;
}
static const struct i2c_device_id aic3x_i2c_id[] = {
diff --git a/sound/soc/codecs/tlv320aic3x-spi.c b/sound/soc/codecs/tlv320aic3x-spi.c
index 8c7b6bb9223f..494e84402232 100644
--- a/sound/soc/codecs/tlv320aic3x-spi.c
+++ b/sound/soc/codecs/tlv320aic3x-spi.c
@@ -37,7 +37,9 @@ static int aic3x_spi_probe(struct spi_device *spi)
static int aic3x_spi_remove(struct spi_device *spi)
{
- return aic3x_remove(&spi->dev);
+ aic3x_remove(&spi->dev);
+
+ return 0;
}
static const struct spi_device_id aic3x_spi_id[] = {
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 7731593a5509..d53037b1509d 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1870,7 +1870,7 @@ err:
}
EXPORT_SYMBOL(aic3x_probe);
-int aic3x_remove(struct device *dev)
+void aic3x_remove(struct device *dev)
{
struct aic3x_priv *aic3x = dev_get_drvdata(dev);
@@ -1881,7 +1881,6 @@ int aic3x_remove(struct device *dev)
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
- return 0;
}
EXPORT_SYMBOL(aic3x_remove);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index 7e0063913017..14298f9e6d9b 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -14,7 +14,7 @@ struct regmap_config;
extern const struct regmap_config aic3x_regmap;
int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data);
-int aic3x_remove(struct device *dev);
+void aic3x_remove(struct device *dev);
#define AIC3X_MODEL_3X 0
#define AIC3X_MODEL_33 1
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index d885ced34f60..bc5d68c53e5a 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -4859,7 +4859,7 @@ static int wcd9335_codec_probe(struct snd_soc_component *component)
snd_soc_component_init_regmap(component, wcd->regmap);
/* Class-H Init*/
- wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version);
+ wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, WCD9335);
if (IS_ERR(wcd->clsh_ctrl))
return PTR_ERR(wcd->clsh_ctrl);
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index c35673e7f420..8863b533f9c4 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -145,13 +145,13 @@ static const struct regmap_range_cfg wm2200_ranges[] = {
.window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
};
-static const struct wm_adsp_region wm2200_dsp1_regions[] = {
+static const struct cs_dsp_region wm2200_dsp1_regions[] = {
{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE },
{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE },
{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE },
};
-static const struct wm_adsp_region wm2200_dsp2_regions[] = {
+static const struct cs_dsp_region wm2200_dsp2_regions[] = {
{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE },
{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE },
{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE },
@@ -2202,23 +2202,23 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
}
for (i = 0; i < 2; i++) {
- wm2200->dsp[i].type = WMFW_ADSP1;
+ wm2200->dsp[i].cs_dsp.type = WMFW_ADSP1;
wm2200->dsp[i].part = "wm2200";
- wm2200->dsp[i].num = i + 1;
- wm2200->dsp[i].dev = &i2c->dev;
- wm2200->dsp[i].regmap = wm2200->regmap;
- wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3;
- wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
- wm2200->dsp[i].sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT;
+ wm2200->dsp[i].cs_dsp.num = i + 1;
+ wm2200->dsp[i].cs_dsp.dev = &i2c->dev;
+ wm2200->dsp[i].cs_dsp.regmap = wm2200->regmap;
+ wm2200->dsp[i].cs_dsp.sysclk_reg = WM2200_CLOCKING_3;
+ wm2200->dsp[i].cs_dsp.sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
+ wm2200->dsp[i].cs_dsp.sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT;
}
- wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
- wm2200->dsp[0].mem = wm2200_dsp1_regions;
- wm2200->dsp[0].num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
+ wm2200->dsp[0].cs_dsp.base = WM2200_DSP1_CONTROL_1;
+ wm2200->dsp[0].cs_dsp.mem = wm2200_dsp1_regions;
+ wm2200->dsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
- wm2200->dsp[1].base = WM2200_DSP2_CONTROL_1;
- wm2200->dsp[1].mem = wm2200_dsp2_regions;
- wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
+ wm2200->dsp[1].cs_dsp.base = WM2200_DSP2_CONTROL_1;
+ wm2200->dsp[1].cs_dsp.mem = wm2200_dsp2_regions;
+ wm2200->dsp[1].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++)
wm_adsp1_init(&wm2200->dsp[i]);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 621598608bf0..da2f8998df87 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -44,7 +44,7 @@ static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
-static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+static const struct cs_dsp_region wm5102_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
@@ -2046,13 +2046,13 @@ static int wm5102_probe(struct platform_device *pdev)
arizona_init_dvfs(&wm5102->core);
wm5102->core.adsp[0].part = "wm5102";
- wm5102->core.adsp[0].num = 1;
- wm5102->core.adsp[0].type = WMFW_ADSP2;
- wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
- wm5102->core.adsp[0].dev = arizona->dev;
- wm5102->core.adsp[0].regmap = arizona->regmap;
- wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
- wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+ wm5102->core.adsp[0].cs_dsp.num = 1;
+ wm5102->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ wm5102->core.adsp[0].cs_dsp.base = ARIZONA_DSP1_CONTROL_1;
+ wm5102->core.adsp[0].cs_dsp.dev = arizona->dev;
+ wm5102->core.adsp[0].cs_dsp.regmap = arizona->regmap;
+ wm5102->core.adsp[0].cs_dsp.mem = wm5102_dsp1_regions;
+ wm5102->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
ret = wm_adsp2_init(&wm5102->core.adsp[0]);
if (ret != 0)
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 5c2d45d05c97..4973ba1ed779 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -45,35 +45,35 @@ struct wm5110_priv {
unsigned int in_pga_cache[6];
};
-static const struct wm_adsp_region wm5110_dsp1_regions[] = {
+static const struct cs_dsp_region wm5110_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
};
-static const struct wm_adsp_region wm5110_dsp2_regions[] = {
+static const struct cs_dsp_region wm5110_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x280000 },
{ .type = WMFW_ADSP2_XM, .base = 0x290000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
};
-static const struct wm_adsp_region wm5110_dsp3_regions[] = {
+static const struct cs_dsp_region wm5110_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x380000 },
{ .type = WMFW_ADSP2_XM, .base = 0x390000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
};
-static const struct wm_adsp_region wm5110_dsp4_regions[] = {
+static const struct cs_dsp_region wm5110_dsp4_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x400000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x480000 },
{ .type = WMFW_ADSP2_XM, .base = 0x490000 },
{ .type = WMFW_ADSP2_YM, .base = 0x4a8000 },
};
-static const struct wm_adsp_region *wm5110_dsp_regions[] = {
+static const struct cs_dsp_region *wm5110_dsp_regions[] = {
wm5110_dsp1_regions,
wm5110_dsp2_regions,
wm5110_dsp3_regions,
@@ -2409,15 +2409,15 @@ static int wm5110_probe(struct platform_device *pdev)
for (i = 0; i < WM5110_NUM_ADSP; i++) {
wm5110->core.adsp[i].part = "wm5110";
- wm5110->core.adsp[i].num = i + 1;
- wm5110->core.adsp[i].type = WMFW_ADSP2;
- wm5110->core.adsp[i].dev = arizona->dev;
- wm5110->core.adsp[i].regmap = arizona->regmap;
+ wm5110->core.adsp[i].cs_dsp.num = i + 1;
+ wm5110->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ wm5110->core.adsp[i].cs_dsp.dev = arizona->dev;
+ wm5110->core.adsp[i].cs_dsp.regmap = arizona->regmap;
- wm5110->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1
+ wm5110->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1
+ (0x100 * i);
- wm5110->core.adsp[i].mem = wm5110_dsp_regions[i];
- wm5110->core.adsp[i].num_mems
+ wm5110->core.adsp[i].cs_dsp.mem = wm5110_dsp_regions[i];
+ wm5110->core.adsp[i].cs_dsp.num_mems
= ARRAY_SIZE(wm5110_dsp1_regions);
ret = wm_adsp2_init(&wm5110->core.adsp[i]);
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index dcee7b2bd3d7..86b1f6eaa599 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -713,18 +713,12 @@ static int wm8731_spi_probe(struct spi_device *spi)
return 0;
}
-static int wm8731_spi_remove(struct spi_device *spi)
-{
- return 0;
-}
-
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
.of_match_table = wm8731_of_match,
},
.probe = wm8731_spi_probe,
- .remove = wm8731_spi_remove,
};
#endif /* CONFIG_SPI_MASTER */
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index a9a6d766a176..bf3a4415a85f 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1252,17 +1252,11 @@ static int wm8900_spi_probe(struct spi_device *spi)
return ret;
}
-static int wm8900_spi_remove(struct spi_device *spi)
-{
- return 0;
-}
-
static struct spi_driver wm8900_spi_driver = {
.driver = {
.name = "wm8900",
},
.probe = wm8900_spi_probe,
- .remove = wm8900_spi_remove,
};
#endif /* CONFIG_SPI_MASTER */
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index ba16bdf9e478..a5584ba962dc 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3538,9 +3538,8 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
pdata->gpio_init[i] = 0x0;
}
- pdata->mclk = devm_clk_get(&i2c->dev, NULL);
-
- return 0;
+ pdata->mclk = devm_clk_get_optional(&i2c->dev, NULL);
+ return PTR_ERR_OR_ZERO(pdata->mclk);
}
static int wm8962_i2c_probe(struct i2c_client *i2c,
@@ -3572,14 +3571,6 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
return ret;
}
- /* Mark the mclk pointer to NULL if no mclk assigned */
- if (IS_ERR(wm8962->pdata.mclk)) {
- /* But do not ignore the request for probe defer */
- if (PTR_ERR(wm8962->pdata.mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- wm8962->pdata.mclk = NULL;
- }
-
for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
wm8962->supplies[i].supply = wm8962_supply_names[i];
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index f7c800927cb2..d4f0d72cbcc8 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -19,7 +19,6 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <sound/core.h>
@@ -33,15 +32,15 @@
#include "wm_adsp.h"
#define adsp_crit(_dsp, fmt, ...) \
- dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
+ dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_err(_dsp, fmt, ...) \
- dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
+ dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_warn(_dsp, fmt, ...) \
- dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
+ dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_info(_dsp, fmt, ...) \
- dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
+ dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_dbg(_dsp, fmt, ...) \
- dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
+ dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define compr_err(_obj, fmt, ...) \
adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
@@ -50,301 +49,10 @@
adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
##__VA_ARGS__)
-#define ADSP1_CONTROL_1 0x00
-#define ADSP1_CONTROL_2 0x02
-#define ADSP1_CONTROL_3 0x03
-#define ADSP1_CONTROL_4 0x04
-#define ADSP1_CONTROL_5 0x06
-#define ADSP1_CONTROL_6 0x07
-#define ADSP1_CONTROL_7 0x08
-#define ADSP1_CONTROL_8 0x09
-#define ADSP1_CONTROL_9 0x0A
-#define ADSP1_CONTROL_10 0x0B
-#define ADSP1_CONTROL_11 0x0C
-#define ADSP1_CONTROL_12 0x0D
-#define ADSP1_CONTROL_13 0x0F
-#define ADSP1_CONTROL_14 0x10
-#define ADSP1_CONTROL_15 0x11
-#define ADSP1_CONTROL_16 0x12
-#define ADSP1_CONTROL_17 0x13
-#define ADSP1_CONTROL_18 0x14
-#define ADSP1_CONTROL_19 0x16
-#define ADSP1_CONTROL_20 0x17
-#define ADSP1_CONTROL_21 0x18
-#define ADSP1_CONTROL_22 0x1A
-#define ADSP1_CONTROL_23 0x1B
-#define ADSP1_CONTROL_24 0x1C
-#define ADSP1_CONTROL_25 0x1E
-#define ADSP1_CONTROL_26 0x20
-#define ADSP1_CONTROL_27 0x21
-#define ADSP1_CONTROL_28 0x22
-#define ADSP1_CONTROL_29 0x23
-#define ADSP1_CONTROL_30 0x24
-#define ADSP1_CONTROL_31 0x26
-
-/*
- * ADSP1 Control 19
- */
-#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
-#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
-#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
-
-
-/*
- * ADSP1 Control 30
- */
-#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
-#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
-#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
-#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
-#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
-#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
-#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
-#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
-#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
-#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
-#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
-#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
-#define ADSP1_START 0x0001 /* DSP1_START */
-#define ADSP1_START_MASK 0x0001 /* DSP1_START */
-#define ADSP1_START_SHIFT 0 /* DSP1_START */
-#define ADSP1_START_WIDTH 1 /* DSP1_START */
-
-/*
- * ADSP1 Control 31
- */
-#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
-#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
-#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
-
-#define ADSP2_CONTROL 0x0
-#define ADSP2_CLOCKING 0x1
-#define ADSP2V2_CLOCKING 0x2
-#define ADSP2_STATUS1 0x4
-#define ADSP2_WDMA_CONFIG_1 0x30
-#define ADSP2_WDMA_CONFIG_2 0x31
-#define ADSP2V2_WDMA_CONFIG_2 0x32
-#define ADSP2_RDMA_CONFIG_1 0x34
-
-#define ADSP2_SCRATCH0 0x40
-#define ADSP2_SCRATCH1 0x41
-#define ADSP2_SCRATCH2 0x42
-#define ADSP2_SCRATCH3 0x43
-
-#define ADSP2V2_SCRATCH0_1 0x40
-#define ADSP2V2_SCRATCH2_3 0x42
-
-/*
- * ADSP2 Control
- */
-
-#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
-#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
-#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
-#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
-#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
-#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
-#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
-#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
-#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
-#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
-#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
-#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
-#define ADSP2_START 0x0001 /* DSP1_START */
-#define ADSP2_START_MASK 0x0001 /* DSP1_START */
-#define ADSP2_START_SHIFT 0 /* DSP1_START */
-#define ADSP2_START_WIDTH 1 /* DSP1_START */
-
-/*
- * ADSP2 clocking
- */
-#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
-#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
-#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
-
-/*
- * ADSP2V2 clocking
- */
-#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */
-#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */
-#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
-
-#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */
-#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */
-#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */
-
-/*
- * ADSP2 Status 1
- */
-#define ADSP2_RAM_RDY 0x0001
-#define ADSP2_RAM_RDY_MASK 0x0001
-#define ADSP2_RAM_RDY_SHIFT 0
-#define ADSP2_RAM_RDY_WIDTH 1
-
-/*
- * ADSP2 Lock support
- */
-#define ADSP2_LOCK_CODE_0 0x5555
-#define ADSP2_LOCK_CODE_1 0xAAAA
-
-#define ADSP2_WATCHDOG 0x0A
-#define ADSP2_BUS_ERR_ADDR 0x52
-#define ADSP2_REGION_LOCK_STATUS 0x64
-#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66
-#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68
-#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A
-#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C
-#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E
-#define ADSP2_LOCK_REGION_CTRL 0x7A
-#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C
-
-#define ADSP2_REGION_LOCK_ERR_MASK 0x8000
-#define ADSP2_ADDR_ERR_MASK 0x4000
-#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
-#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002
-#define ADSP2_CTRL_ERR_EINT 0x0001
-
-#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF
-#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF
-#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000
-#define ADSP2_PMEM_ERR_ADDR_SHIFT 16
-#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD
-
-#define ADSP2_LOCK_REGION_SHIFT 16
-
#define ADSP_MAX_STD_CTRL_SIZE 512
-#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
-#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
-#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
-#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
-
-/*
- * Event control messages
- */
-#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
-
-/*
- * HALO system info
- */
-#define HALO_AHBM_WINDOW_DEBUG_0 0x02040
-#define HALO_AHBM_WINDOW_DEBUG_1 0x02044
-
-/*
- * HALO core
- */
-#define HALO_SCRATCH1 0x005c0
-#define HALO_SCRATCH2 0x005c8
-#define HALO_SCRATCH3 0x005d0
-#define HALO_SCRATCH4 0x005d8
-#define HALO_CCM_CORE_CONTROL 0x41000
-#define HALO_CORE_SOFT_RESET 0x00010
-#define HALO_WDT_CONTROL 0x47000
-
-/*
- * HALO MPU banks
- */
-#define HALO_MPU_XMEM_ACCESS_0 0x43000
-#define HALO_MPU_YMEM_ACCESS_0 0x43004
-#define HALO_MPU_WINDOW_ACCESS_0 0x43008
-#define HALO_MPU_XREG_ACCESS_0 0x4300C
-#define HALO_MPU_YREG_ACCESS_0 0x43014
-#define HALO_MPU_XMEM_ACCESS_1 0x43018
-#define HALO_MPU_YMEM_ACCESS_1 0x4301C
-#define HALO_MPU_WINDOW_ACCESS_1 0x43020
-#define HALO_MPU_XREG_ACCESS_1 0x43024
-#define HALO_MPU_YREG_ACCESS_1 0x4302C
-#define HALO_MPU_XMEM_ACCESS_2 0x43030
-#define HALO_MPU_YMEM_ACCESS_2 0x43034
-#define HALO_MPU_WINDOW_ACCESS_2 0x43038
-#define HALO_MPU_XREG_ACCESS_2 0x4303C
-#define HALO_MPU_YREG_ACCESS_2 0x43044
-#define HALO_MPU_XMEM_ACCESS_3 0x43048
-#define HALO_MPU_YMEM_ACCESS_3 0x4304C
-#define HALO_MPU_WINDOW_ACCESS_3 0x43050
-#define HALO_MPU_XREG_ACCESS_3 0x43054
-#define HALO_MPU_YREG_ACCESS_3 0x4305C
-#define HALO_MPU_XM_VIO_ADDR 0x43100
-#define HALO_MPU_XM_VIO_STATUS 0x43104
-#define HALO_MPU_YM_VIO_ADDR 0x43108
-#define HALO_MPU_YM_VIO_STATUS 0x4310C
-#define HALO_MPU_PM_VIO_ADDR 0x43110
-#define HALO_MPU_PM_VIO_STATUS 0x43114
-#define HALO_MPU_LOCK_CONFIG 0x43140
-
-/*
- * HALO_AHBM_WINDOW_DEBUG_1
- */
-#define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00
-#define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8
-#define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff
-
-/*
- * HALO_CCM_CORE_CONTROL
- */
-#define HALO_CORE_RESET 0x00000200
-#define HALO_CORE_EN 0x00000001
-
-/*
- * HALO_CORE_SOFT_RESET
- */
-#define HALO_CORE_SOFT_RESET_MASK 0x00000001
-
-/*
- * HALO_WDT_CONTROL
- */
-#define HALO_WDT_EN_MASK 0x00000001
-
-/*
- * HALO_MPU_?M_VIO_STATUS
- */
-#define HALO_MPU_VIO_STS_MASK 0x007e0000
-#define HALO_MPU_VIO_STS_SHIFT 17
-#define HALO_MPU_VIO_ERR_WR_MASK 0x00008000
-#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff
-#define HALO_MPU_VIO_ERR_SRC_SHIFT 0
-
-static const struct wm_adsp_ops wm_adsp1_ops;
-static const struct wm_adsp_ops wm_adsp2_ops[];
-static const struct wm_adsp_ops wm_halo_ops;
-
-struct wm_adsp_buf {
- struct list_head list;
- void *buf;
-};
-
-static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
- struct list_head *list)
-{
- struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
-
- if (buf == NULL)
- return NULL;
-
- buf->buf = vmalloc(len);
- if (!buf->buf) {
- kfree(buf);
- return NULL;
- }
- memcpy(buf->buf, src, len);
-
- if (list)
- list_add_tail(&buf->list, list);
-
- return buf;
-}
-
-static void wm_adsp_buf_free(struct list_head *list)
-{
- while (!list_empty(list)) {
- struct wm_adsp_buf *buf = list_first_entry(list,
- struct wm_adsp_buf,
- list);
- list_del(&buf->list);
- vfree(buf->buf);
- kfree(buf);
- }
-}
+static const struct cs_dsp_client_ops wm_adsp1_client_ops;
+static const struct cs_dsp_client_ops wm_adsp2_client_ops;
#define WM_ADSP_FW_MBC_VSS 0
#define WM_ADSP_FW_HIFI 1
@@ -470,12 +178,10 @@ struct wm_adsp_compr {
const char *name;
};
-#define WM_ADSP_DATA_WORD_SIZE 3
-
#define WM_ADSP_MIN_FRAGMENTS 1
#define WM_ADSP_MAX_FRAGMENTS 256
-#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
-#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
+#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * CS_DSP_DATA_WORD_SIZE)
+#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE)
#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
@@ -598,183 +304,11 @@ static const struct {
struct wm_coeff_ctl {
const char *name;
- const char *fw_name;
- /* Subname is needed to match with firmware */
- const char *subname;
- unsigned int subname_len;
- struct wm_adsp_alg_region alg_region;
- struct wm_adsp *dsp;
- unsigned int enabled:1;
- struct list_head list;
- void *cache;
- unsigned int offset;
- size_t len;
- unsigned int set:1;
+ struct cs_dsp_coeff_ctl *cs_ctl;
struct soc_bytes_ext bytes_ext;
- unsigned int flags;
- snd_ctl_elem_type_t type;
-};
-
-static const char *wm_adsp_mem_region_name(unsigned int type)
-{
- switch (type) {
- case WMFW_ADSP1_PM:
- return "PM";
- case WMFW_HALO_PM_PACKED:
- return "PM_PACKED";
- case WMFW_ADSP1_DM:
- return "DM";
- case WMFW_ADSP2_XM:
- return "XM";
- case WMFW_HALO_XM_PACKED:
- return "XM_PACKED";
- case WMFW_ADSP2_YM:
- return "YM";
- case WMFW_HALO_YM_PACKED:
- return "YM_PACKED";
- case WMFW_ADSP1_ZM:
- return "ZM";
- default:
- return NULL;
- }
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
-{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
- kfree(dsp->wmfw_file_name);
- dsp->wmfw_file_name = tmp;
-}
-
-static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
-{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
- kfree(dsp->bin_file_name);
- dsp->bin_file_name = tmp;
-}
-
-static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
-{
- kfree(dsp->wmfw_file_name);
- kfree(dsp->bin_file_name);
- dsp->wmfw_file_name = NULL;
- dsp->bin_file_name = NULL;
-}
-
-static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct wm_adsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->wmfw_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->wmfw_file_name,
- strlen(dsp->wmfw_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
-}
-
-static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct wm_adsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->bin_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->bin_file_name,
- strlen(dsp->bin_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
-}
-
-static const struct {
- const char *name;
- const struct file_operations fops;
-} wm_adsp_debugfs_fops[] = {
- {
- .name = "wmfw_file_name",
- .fops = {
- .open = simple_open,
- .read = wm_adsp_debugfs_wmfw_read,
- },
- },
- {
- .name = "bin_file_name",
- .fops = {
- .open = simple_open,
- .read = wm_adsp_debugfs_bin_read,
- },
- },
+ struct work_struct work;
};
-static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
- struct snd_soc_component *component)
-{
- struct dentry *root = NULL;
- int i;
-
- root = debugfs_create_dir(dsp->name, component->debugfs_root);
-
- debugfs_create_bool("booted", 0444, root, &dsp->booted);
- debugfs_create_bool("running", 0444, root, &dsp->running);
- debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id);
- debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version);
-
- for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i)
- debugfs_create_file(wm_adsp_debugfs_fops[i].name, 0444, root,
- dsp, &wm_adsp_debugfs_fops[i].fops);
-
- dsp->debugfs_root = root;
-}
-
-static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
-{
- wm_adsp_debugfs_clear(dsp);
- debugfs_remove_recursive(dsp->debugfs_root);
- dsp->debugfs_root = NULL;
-}
-#else
-static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
- struct snd_soc_component *component)
-{
-}
-
-static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
-{
-}
-
-static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
- const char *s)
-{
-}
-
-static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
- const char *s)
-{
-}
-
-static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
-{
-}
-#endif
-
int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -802,14 +336,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
- mutex_lock(&dsp[e->shift_l].pwr_lock);
+ mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock);
- if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list))
+ if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list))
ret = -EBUSY;
else
dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
- mutex_unlock(&dsp[e->shift_l].pwr_lock);
+ mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock);
return ret;
}
@@ -826,270 +360,49 @@ const struct soc_enum wm_adsp_fw_enum[] = {
};
EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
-static const struct wm_adsp_region *wm_adsp_find_region(struct wm_adsp *dsp,
- int type)
-{
- int i;
-
- for (i = 0; i < dsp->num_mems; i++)
- if (dsp->mem[i].type == type)
- return &dsp->mem[i];
-
- return NULL;
-}
-
-static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
- unsigned int offset)
-{
- switch (mem->type) {
- case WMFW_ADSP1_PM:
- return mem->base + (offset * 3);
- case WMFW_ADSP1_DM:
- case WMFW_ADSP2_XM:
- case WMFW_ADSP2_YM:
- case WMFW_ADSP1_ZM:
- return mem->base + (offset * 2);
- default:
- WARN(1, "Unknown memory region type");
- return offset;
- }
-}
-
-static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem,
- unsigned int offset)
-{
- switch (mem->type) {
- case WMFW_ADSP2_XM:
- case WMFW_ADSP2_YM:
- return mem->base + (offset * 4);
- case WMFW_HALO_XM_PACKED:
- case WMFW_HALO_YM_PACKED:
- return (mem->base + (offset * 3)) & ~0x3;
- case WMFW_HALO_PM_PACKED:
- return mem->base + (offset * 5);
- default:
- WARN(1, "Unknown memory region type");
- return offset;
- }
-}
-
-static void wm_adsp_read_fw_status(struct wm_adsp *dsp,
- int noffs, unsigned int *offs)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < noffs; ++i) {
- ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]);
- if (ret) {
- adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);
- return;
- }
- }
-}
-
-static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
-{
- unsigned int offs[] = {
- ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3,
- };
-
- wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
-
- adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- offs[0], offs[1], offs[2], offs[3]);
-}
-
-static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
-{
- unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 };
-
- wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
-
- adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- offs[0] & 0xFFFF, offs[0] >> 16,
- offs[1] & 0xFFFF, offs[1] >> 16);
-}
-
-static void wm_halo_show_fw_status(struct wm_adsp *dsp)
-{
- unsigned int offs[] = {
- HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4,
- };
-
- wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
-
- adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- offs[0], offs[1], offs[2], offs[3]);
-}
-
static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
{
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
}
-static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
-{
- const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- struct wm_adsp *dsp = ctl->dsp;
- const struct wm_adsp_region *mem;
-
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset);
-
- return 0;
-}
-
static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- switch (ctl->type) {
+ switch (cs_ctl->type) {
case WMFW_CTL_TYPE_ACKED:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
- uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
+ uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE;
+ uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE;
uinfo->value.integer.step = 1;
uinfo->count = 1;
break;
default:
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = ctl->len;
+ uinfo->count = cs_ctl->len;
break;
}
return 0;
}
-static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
- unsigned int event_id)
-{
- struct wm_adsp *dsp = ctl->dsp;
- __be32 val = cpu_to_be32(event_id);
- unsigned int reg;
- int i, ret;
-
- ret = wm_coeff_base_reg(ctl, &reg);
- if (ret)
- return ret;
-
- adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
- event_id, ctl->alg_region.alg,
- wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
-
- ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
- if (ret) {
- adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
- return ret;
- }
-
- /*
- * Poll for ack, we initially poll at ~1ms intervals for firmwares
- * that respond quickly, then go to ~10ms polls. A firmware is unlikely
- * to ack instantly so we do the first 1ms delay before reading the
- * control to avoid a pointless bus transaction
- */
- for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
- switch (i) {
- case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
- usleep_range(1000, 2000);
- i++;
- break;
- default:
- usleep_range(10000, 20000);
- i += 10;
- break;
- }
-
- ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
- if (ret) {
- adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
- return ret;
- }
-
- if (val == 0) {
- adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
- return 0;
- }
- }
-
- adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
- reg, ctl->alg_region.alg,
- wm_adsp_mem_region_name(ctl->alg_region.type),
- ctl->offset);
-
- return -ETIMEDOUT;
-}
-
-static int wm_coeff_write_ctrl_raw(struct wm_coeff_ctl *ctl,
- const void *buf, size_t len)
-{
- struct wm_adsp *dsp = ctl->dsp;
- void *scratch;
- int ret;
- unsigned int reg;
-
- ret = wm_coeff_base_reg(ctl, &reg);
- if (ret)
- return ret;
-
- scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
- if (!scratch)
- return -ENOMEM;
-
- ret = regmap_raw_write(dsp->regmap, reg, scratch,
- len);
- if (ret) {
- adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
- len, reg, ret);
- kfree(scratch);
- return ret;
- }
- adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
-
- kfree(scratch);
-
- return 0;
-}
-
-static int wm_coeff_write_ctrl(struct wm_coeff_ctl *ctl,
- const void *buf, size_t len)
-{
- int ret = 0;
-
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
- ret = -EPERM;
- else if (buf != ctl->cache)
- memcpy(ctl->cache, buf, len);
-
- ctl->set = 1;
- if (ctl->enabled && ctl->dsp->running)
- ret = wm_coeff_write_ctrl_raw(ctl, buf, len);
-
- return ret;
-}
-
static int wm_coeff_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
char *p = ucontrol->value.bytes.data;
int ret = 0;
- mutex_lock(&ctl->dsp->pwr_lock);
- ret = wm_coeff_write_ctrl(ctl, p, ctl->len);
- mutex_unlock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, p, cs_ctl->len);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
@@ -1100,16 +413,17 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
int ret = 0;
- mutex_lock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
- if (copy_from_user(ctl->cache, bytes, size))
+ if (copy_from_user(cs_ctl->cache, bytes, size))
ret = -EFAULT;
else
- ret = wm_coeff_write_ctrl(ctl, ctl->cache, size);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, cs_ctl->cache, size);
- mutex_unlock(&ctl->dsp->pwr_lock);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
@@ -1120,71 +434,21 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
unsigned int val = ucontrol->value.integer.value[0];
int ret;
if (val == 0)
return 0; /* 0 means no event */
- mutex_lock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
- if (ctl->enabled && ctl->dsp->running)
- ret = wm_coeff_write_acked_control(ctl, val);
+ if (cs_ctl->enabled)
+ ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);
else
ret = -EPERM;
- mutex_unlock(&ctl->dsp->pwr_lock);
-
- return ret;
-}
-
-static int wm_coeff_read_ctrl_raw(struct wm_coeff_ctl *ctl,
- void *buf, size_t len)
-{
- struct wm_adsp *dsp = ctl->dsp;
- void *scratch;
- int ret;
- unsigned int reg;
-
- ret = wm_coeff_base_reg(ctl, &reg);
- if (ret)
- return ret;
-
- scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
- if (!scratch)
- return -ENOMEM;
-
- ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
- if (ret) {
- adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
- len, reg, ret);
- kfree(scratch);
- return ret;
- }
- adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
-
- memcpy(buf, scratch, len);
- kfree(scratch);
-
- return 0;
-}
-
-static int wm_coeff_read_ctrl(struct wm_coeff_ctl *ctl, void *buf, size_t len)
-{
- int ret = 0;
-
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
- if (ctl->enabled && ctl->dsp->running)
- return wm_coeff_read_ctrl_raw(ctl, buf, len);
- else
- return -EPERM;
- } else {
- if (!ctl->flags && ctl->enabled && ctl->dsp->running)
- ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len);
-
- if (buf != ctl->cache)
- memcpy(buf, ctl->cache, len);
- }
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
@@ -1195,12 +459,13 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
char *p = ucontrol->value.bytes.data;
int ret;
- mutex_lock(&ctl->dsp->pwr_lock);
- ret = wm_coeff_read_ctrl(ctl, p, ctl->len);
- mutex_unlock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, p, cs_ctl->len);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
@@ -1211,16 +476,17 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
int ret = 0;
- mutex_lock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = wm_coeff_read_ctrl(ctl, ctl->cache, size);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, cs_ctl->cache, size);
- if (!ret && copy_to_user(bytes, ctl->cache, size))
+ if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
ret = -EFAULT;
- mutex_unlock(&ctl->dsp->pwr_lock);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
@@ -1240,12 +506,6 @@ static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
return 0;
}
-struct wmfw_ctl_work {
- struct wm_adsp *dsp;
- struct wm_coeff_ctl *ctl;
- struct work_struct work;
-};
-
static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
{
unsigned int out, rd, wr, vol;
@@ -1279,12 +539,10 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
{
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
struct snd_kcontrol_new *kcontrol;
int ret;
- if (!ctl || !ctl->name)
- return -EINVAL;
-
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
if (!kcontrol)
return -ENOMEM;
@@ -1294,16 +552,16 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
- kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
+ kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
- switch (ctl->type) {
+ switch (cs_ctl->type) {
case WMFW_CTL_TYPE_ACKED:
kcontrol->get = wm_coeff_get_acked;
kcontrol->put = wm_coeff_put_acked;
break;
default:
if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- ctl->bytes_ext.max = ctl->len;
+ ctl->bytes_ext.max = cs_ctl->len;
ctl->bytes_ext.get = wm_coeff_tlv_get;
ctl->bytes_ext.put = wm_coeff_tlv_put;
} else {
@@ -1326,128 +584,55 @@ err_kcontrol:
return ret;
}
-static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
-{
- struct wm_coeff_ctl *ctl;
- int ret;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!ctl->enabled || ctl->set)
- continue;
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
- continue;
-
- /*
- * For readable controls populate the cache from the DSP memory.
- * For non-readable controls the cache was zero-filled when
- * created so we don't need to do anything.
- */
- if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) {
- ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len);
- if (ret < 0)
- return ret;
- }
- }
-
- return 0;
-}
-
-static int wm_coeff_sync_controls(struct wm_adsp *dsp)
-{
- struct wm_coeff_ctl *ctl;
- int ret;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!ctl->enabled)
- continue;
- if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
- ret = wm_coeff_write_ctrl_raw(ctl, ctl->cache,
- ctl->len);
- if (ret < 0)
- return ret;
- }
- }
-
- return 0;
-}
-
-static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
- unsigned int event)
-{
- struct wm_coeff_ctl *ctl;
- int ret;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
- continue;
-
- if (!ctl->enabled)
- continue;
-
- ret = wm_coeff_write_acked_control(ctl, event);
- if (ret)
- adsp_warn(dsp,
- "Failed to send 0x%x event to alg 0x%x (%d)\n",
- event, ctl->alg_region.alg, ret);
- }
-}
-
static void wm_adsp_ctl_work(struct work_struct *work)
{
- struct wmfw_ctl_work *ctl_work = container_of(work,
- struct wmfw_ctl_work,
- work);
-
- wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
- kfree(ctl_work);
-}
+ struct wm_coeff_ctl *ctl = container_of(work,
+ struct wm_coeff_ctl,
+ work);
+ struct wm_adsp *dsp = container_of(ctl->cs_ctl->dsp,
+ struct wm_adsp,
+ cs_dsp);
-static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
-{
- kfree(ctl->cache);
- kfree(ctl->name);
- kfree(ctl->subname);
- kfree(ctl);
+ wmfw_add_ctl(dsp, ctl);
}
-static int wm_adsp_create_control(struct wm_adsp *dsp,
- const struct wm_adsp_alg_region *alg_region,
- unsigned int offset, unsigned int len,
- const char *subname, unsigned int subname_len,
- unsigned int flags, snd_ctl_elem_type_t type)
+static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
{
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
+ struct cs_dsp *cs_dsp = &dsp->cs_dsp;
struct wm_coeff_ctl *ctl;
- struct wmfw_ctl_work *ctl_work;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
const char *region_name;
int ret;
- region_name = wm_adsp_mem_region_name(alg_region->type);
+ if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
+ return 0;
+
+ region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
if (!region_name) {
- adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
+ adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);
return -EINVAL;
}
- switch (dsp->fw_ver) {
+ switch (cs_dsp->fw_ver) {
case 0:
case 1:
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x",
- dsp->name, region_name, alg_region->alg);
- subname = NULL; /* don't append subname */
+ cs_dsp->name, region_name, cs_ctl->alg_region.alg);
break;
case 2:
ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
- "%s%c %.12s %x", dsp->name, *region_name,
- wm_adsp_fw_text[dsp->fw], alg_region->alg);
+ "%s%c %.12s %x", cs_dsp->name, *region_name,
+ wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
break;
default:
ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
- "%s %.12s %x", dsp->name,
- wm_adsp_fw_text[dsp->fw], alg_region->alg);
+ "%s %.12s %x", cs_dsp->name,
+ wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
break;
}
- if (subname) {
+ if (cs_ctl->subname) {
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
int skip = 0;
@@ -1455,613 +640,70 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
avail -= strlen(dsp->component->name_prefix) + 1;
/* Truncate the subname from the start if it is too long */
- if (subname_len > avail)
- skip = subname_len - avail;
+ if (cs_ctl->subname_len > avail)
+ skip = cs_ctl->subname_len - avail;
snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
- " %.*s", subname_len - skip, subname + skip);
- }
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!strcmp(ctl->name, name)) {
- if (!ctl->enabled)
- ctl->enabled = 1;
- return 0;
- }
+ " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
if (!ctl)
return -ENOMEM;
- ctl->fw_name = wm_adsp_fw_text[dsp->fw];
- ctl->alg_region = *alg_region;
+ ctl->cs_ctl = cs_ctl;
+
ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
if (!ctl->name) {
ret = -ENOMEM;
goto err_ctl;
}
- if (subname) {
- ctl->subname_len = subname_len;
- ctl->subname = kmemdup(subname,
- strlen(subname) + 1, GFP_KERNEL);
- if (!ctl->subname) {
- ret = -ENOMEM;
- goto err_ctl_name;
- }
- }
- ctl->enabled = 1;
- ctl->set = 0;
- ctl->dsp = dsp;
-
- ctl->flags = flags;
- ctl->type = type;
- ctl->offset = offset;
- ctl->len = len;
- ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
- if (!ctl->cache) {
- ret = -ENOMEM;
- goto err_ctl_subname;
- }
-
- list_add(&ctl->list, &dsp->ctl_list);
- if (flags & WMFW_CTL_FLAG_SYS)
- return 0;
-
- ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
- if (!ctl_work) {
- ret = -ENOMEM;
- goto err_list_del;
- }
+ cs_ctl->priv = ctl;
- ctl_work->dsp = dsp;
- ctl_work->ctl = ctl;
- INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
- schedule_work(&ctl_work->work);
+ INIT_WORK(&ctl->work, wm_adsp_ctl_work);
+ schedule_work(&ctl->work);
return 0;
-err_list_del:
- list_del(&ctl->list);
- kfree(ctl->cache);
-err_ctl_subname:
- kfree(ctl->subname);
-err_ctl_name:
- kfree(ctl->name);
err_ctl:
kfree(ctl);
return ret;
}
-struct wm_coeff_parsed_alg {
- int id;
- const u8 *name;
- int name_len;
- int ncoeff;
-};
-
-struct wm_coeff_parsed_coeff {
- int offset;
- int mem_type;
- const u8 *name;
- int name_len;
- snd_ctl_elem_type_t ctl_type;
- int flags;
- int len;
-};
-
-static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
-{
- int length;
-
- switch (bytes) {
- case 1:
- length = **pos;
- break;
- case 2:
- length = le16_to_cpu(*((__le16 *)*pos));
- break;
- default:
- return 0;
- }
-
- if (str)
- *str = *pos + bytes;
-
- *pos += ((length + bytes) + 3) & ~0x03;
-
- return length;
-}
-
-static int wm_coeff_parse_int(int bytes, const u8 **pos)
-{
- int val = 0;
-
- switch (bytes) {
- case 2:
- val = le16_to_cpu(*((__le16 *)*pos));
- break;
- case 4:
- val = le32_to_cpu(*((__le32 *)*pos));
- break;
- default:
- break;
- }
-
- *pos += bytes;
-
- return val;
-}
-
-static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
- struct wm_coeff_parsed_alg *blk)
-{
- const struct wmfw_adsp_alg_data *raw;
-
- switch (dsp->fw_ver) {
- case 0:
- case 1:
- raw = (const struct wmfw_adsp_alg_data *)*data;
- *data = raw->data;
-
- blk->id = le32_to_cpu(raw->id);
- blk->name = raw->name;
- blk->name_len = strlen(raw->name);
- blk->ncoeff = le32_to_cpu(raw->ncoeff);
- break;
- default:
- blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
- blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
- &blk->name);
- wm_coeff_parse_string(sizeof(u16), data, NULL);
- blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
- break;
- }
-
- adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
- adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
- adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
-}
-
-static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
- struct wm_coeff_parsed_coeff *blk)
-{
- const struct wmfw_adsp_coeff_data *raw;
- const u8 *tmp;
- int length;
-
- switch (dsp->fw_ver) {
- case 0:
- case 1:
- raw = (const struct wmfw_adsp_coeff_data *)*data;
- *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
-
- blk->offset = le16_to_cpu(raw->hdr.offset);
- blk->mem_type = le16_to_cpu(raw->hdr.type);
- blk->name = raw->name;
- blk->name_len = strlen(raw->name);
- blk->ctl_type = (__force snd_ctl_elem_type_t)le16_to_cpu(raw->ctl_type);
- blk->flags = le16_to_cpu(raw->flags);
- blk->len = le32_to_cpu(raw->len);
- break;
- default:
- tmp = *data;
- blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
- blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
- length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
- blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
- &blk->name);
- wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
- wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
- blk->ctl_type =
- (__force snd_ctl_elem_type_t)wm_coeff_parse_int(sizeof(raw->ctl_type),
- &tmp);
- blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
- blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
-
- *data = *data + sizeof(raw->hdr) + length;
- break;
- }
-
- adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
- adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
- adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
- adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
- adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
- adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
-}
-
-static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
- const struct wm_coeff_parsed_coeff *coeff_blk,
- unsigned int f_required,
- unsigned int f_illegal)
-{
- if ((coeff_blk->flags & f_illegal) ||
- ((coeff_blk->flags & f_required) != f_required)) {
- adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
- coeff_blk->flags, coeff_blk->ctl_type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
- const struct wmfw_region *region)
-{
- struct wm_adsp_alg_region alg_region = {};
- struct wm_coeff_parsed_alg alg_blk;
- struct wm_coeff_parsed_coeff coeff_blk;
- const u8 *data = region->data;
- int i, ret;
-
- wm_coeff_parse_alg(dsp, &data, &alg_blk);
- for (i = 0; i < alg_blk.ncoeff; i++) {
- wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
-
- switch (coeff_blk.ctl_type) {
- case SNDRV_CTL_ELEM_TYPE_BYTES:
- break;
- case WMFW_CTL_TYPE_ACKED:
- if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
- continue; /* ignore */
-
- ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
- WMFW_CTL_FLAG_VOLATILE |
- WMFW_CTL_FLAG_WRITEABLE |
- WMFW_CTL_FLAG_READABLE,
- 0);
- if (ret)
- return -EINVAL;
- break;
- case WMFW_CTL_TYPE_HOSTEVENT:
- ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
- WMFW_CTL_FLAG_SYS |
- WMFW_CTL_FLAG_VOLATILE |
- WMFW_CTL_FLAG_WRITEABLE |
- WMFW_CTL_FLAG_READABLE,
- 0);
- if (ret)
- return -EINVAL;
- break;
- case WMFW_CTL_TYPE_HOST_BUFFER:
- ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
- WMFW_CTL_FLAG_SYS |
- WMFW_CTL_FLAG_VOLATILE |
- WMFW_CTL_FLAG_READABLE,
- 0);
- if (ret)
- return -EINVAL;
- break;
- default:
- adsp_err(dsp, "Unknown control type: %d\n",
- coeff_blk.ctl_type);
- return -EINVAL;
- }
-
- alg_region.type = coeff_blk.mem_type;
- alg_region.alg = alg_blk.id;
-
- ret = wm_adsp_create_control(dsp, &alg_region,
- coeff_blk.offset,
- coeff_blk.len,
- coeff_blk.name,
- coeff_blk.name_len,
- coeff_blk.flags,
- coeff_blk.ctl_type);
- if (ret < 0)
- adsp_err(dsp, "Failed to create control: %.*s, %d\n",
- coeff_blk.name_len, coeff_blk.name, ret);
- }
-
- return 0;
-}
-
-static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp,
- const char * const file,
- unsigned int pos,
- const struct firmware *firmware)
-{
- const struct wmfw_adsp1_sizes *adsp1_sizes;
-
- adsp1_sizes = (void *)&firmware->data[pos];
-
- adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,
- le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm),
- le32_to_cpu(adsp1_sizes->zm));
-
- return pos + sizeof(*adsp1_sizes);
-}
-
-static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp,
- const char * const file,
- unsigned int pos,
- const struct firmware *firmware)
-{
- const struct wmfw_adsp2_sizes *adsp2_sizes;
-
- adsp2_sizes = (void *)&firmware->data[pos];
-
- adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,
- le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym),
- le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm));
-
- return pos + sizeof(*adsp2_sizes);
-}
-
-static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version)
-{
- switch (version) {
- case 0:
- adsp_warn(dsp, "Deprecated file format %d\n", version);
- return true;
- case 1:
- case 2:
- return true;
- default:
- return false;
- }
-}
-
-static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version)
-{
- switch (version) {
- case 3:
- return true;
- default:
- return false;
- }
-}
-
-static int wm_adsp_load(struct wm_adsp *dsp)
-{
- LIST_HEAD(buf_list);
- const struct firmware *firmware;
- struct regmap *regmap = dsp->regmap;
- unsigned int pos = 0;
- const struct wmfw_header *header;
- const struct wmfw_adsp1_sizes *adsp1_sizes;
- const struct wmfw_footer *footer;
- const struct wmfw_region *region;
- const struct wm_adsp_region *mem;
- const char *region_name;
- char *file, *text = NULL;
- struct wm_adsp_buf *buf;
- unsigned int reg;
- int regions = 0;
- int ret, offset, type;
-
- file = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (file == NULL)
- return -ENOMEM;
-
- snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name,
- wm_adsp_fw[dsp->fw].file);
- file[PAGE_SIZE - 1] = '\0';
-
- ret = request_firmware(&firmware, file, dsp->dev);
- if (ret != 0) {
- adsp_err(dsp, "Failed to request '%s'\n", file);
- goto out;
- }
- ret = -EINVAL;
-
- pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
- if (pos >= firmware->size) {
- adsp_err(dsp, "%s: file too short, %zu bytes\n",
- file, firmware->size);
- goto out_fw;
- }
-
- header = (void *)&firmware->data[0];
-
- if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
- adsp_err(dsp, "%s: invalid magic\n", file);
- goto out_fw;
- }
-
- if (!dsp->ops->validate_version(dsp, header->ver)) {
- adsp_err(dsp, "%s: unknown file format %d\n",
- file, header->ver);
- goto out_fw;
- }
-
- adsp_info(dsp, "Firmware version: %d\n", header->ver);
- dsp->fw_ver = header->ver;
-
- if (header->core != dsp->type) {
- adsp_err(dsp, "%s: invalid core %d != %d\n",
- file, header->core, dsp->type);
- goto out_fw;
- }
-
- pos = sizeof(*header);
- pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
-
- footer = (void *)&firmware->data[pos];
- pos += sizeof(*footer);
-
- if (le32_to_cpu(header->len) != pos) {
- adsp_err(dsp, "%s: unexpected header length %d\n",
- file, le32_to_cpu(header->len));
- goto out_fw;
- }
-
- adsp_dbg(dsp, "%s: timestamp %llu\n", file,
- le64_to_cpu(footer->timestamp));
-
- while (pos < firmware->size &&
- sizeof(*region) < firmware->size - pos) {
- region = (void *)&(firmware->data[pos]);
- region_name = "Unknown";
- reg = 0;
- text = NULL;
- offset = le32_to_cpu(region->offset) & 0xffffff;
- type = be32_to_cpu(region->type) & 0xff;
-
- switch (type) {
- case WMFW_NAME_TEXT:
- region_name = "Firmware name";
- text = kzalloc(le32_to_cpu(region->len) + 1,
- GFP_KERNEL);
- break;
- case WMFW_ALGORITHM_DATA:
- region_name = "Algorithm";
- ret = wm_adsp_parse_coeff(dsp, region);
- if (ret != 0)
- goto out_fw;
- break;
- case WMFW_INFO_TEXT:
- region_name = "Information";
- text = kzalloc(le32_to_cpu(region->len) + 1,
- GFP_KERNEL);
- break;
- case WMFW_ABSOLUTE:
- region_name = "Absolute";
- reg = offset;
- break;
- case WMFW_ADSP1_PM:
- case WMFW_ADSP1_DM:
- case WMFW_ADSP2_XM:
- case WMFW_ADSP2_YM:
- case WMFW_ADSP1_ZM:
- case WMFW_HALO_PM_PACKED:
- case WMFW_HALO_XM_PACKED:
- case WMFW_HALO_YM_PACKED:
- mem = wm_adsp_find_region(dsp, type);
- if (!mem) {
- adsp_err(dsp, "No region of type: %x\n", type);
- ret = -EINVAL;
- goto out_fw;
- }
-
- region_name = wm_adsp_mem_region_name(type);
- reg = dsp->ops->region_to_reg(mem, offset);
- break;
- default:
- adsp_warn(dsp,
- "%s.%d: Unknown region type %x at %d(%x)\n",
- file, regions, type, pos, pos);
- break;
- }
-
- adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
- regions, le32_to_cpu(region->len), offset,
- region_name);
-
- if (le32_to_cpu(region->len) >
- firmware->size - pos - sizeof(*region)) {
- adsp_err(dsp,
- "%s.%d: %s region len %d bytes exceeds file length %zu\n",
- file, regions, region_name,
- le32_to_cpu(region->len), firmware->size);
- ret = -EINVAL;
- goto out_fw;
- }
-
- if (text) {
- memcpy(text, region->data, le32_to_cpu(region->len));
- adsp_info(dsp, "%s: %s\n", file, text);
- kfree(text);
- text = NULL;
- }
-
- if (reg) {
- buf = wm_adsp_buf_alloc(region->data,
- le32_to_cpu(region->len),
- &buf_list);
- if (!buf) {
- adsp_err(dsp, "Out of memory\n");
- ret = -ENOMEM;
- goto out_fw;
- }
-
- ret = regmap_raw_write_async(regmap, reg, buf->buf,
- le32_to_cpu(region->len));
- if (ret != 0) {
- adsp_err(dsp,
- "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
- file, regions,
- le32_to_cpu(region->len), offset,
- region_name, ret);
- goto out_fw;
- }
- }
-
- pos += le32_to_cpu(region->len) + sizeof(*region);
- regions++;
- }
-
- ret = regmap_async_complete(regmap);
- if (ret != 0) {
- adsp_err(dsp, "Failed to complete async write: %d\n", ret);
- goto out_fw;
- }
-
- if (pos > firmware->size)
- adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
- file, regions, pos - firmware->size);
-
- wm_adsp_debugfs_save_wmfwname(dsp, file);
-
-out_fw:
- regmap_async_complete(regmap);
- wm_adsp_buf_free(&buf_list);
- release_firmware(firmware);
- kfree(text);
-out:
- kfree(file);
-
- return ret;
-}
-
-/*
- * Find wm_coeff_ctl with input name as its subname
- * If not found, return NULL
- */
-static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp,
- const char *name, int type,
- unsigned int alg)
+static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
{
- struct wm_coeff_ctl *pos, *rslt = NULL;
- const char *fw_txt = wm_adsp_fw_text[dsp->fw];
+ struct wm_coeff_ctl *ctl = cs_ctl->priv;
- list_for_each_entry(pos, &dsp->ctl_list, list) {
- if (!pos->subname)
- continue;
- if (strncmp(pos->subname, name, pos->subname_len) == 0 &&
- pos->fw_name == fw_txt &&
- pos->alg_region.alg == alg &&
- pos->alg_region.type == type) {
- rslt = pos;
- break;
- }
- }
+ cancel_work_sync(&ctl->work);
- return rslt;
+ kfree(ctl->name);
+ kfree(ctl);
}
int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len)
{
+ struct cs_dsp_coeff_ctl *cs_ctl;
struct wm_coeff_ctl *ctl;
struct snd_kcontrol *kcontrol;
char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int ret;
- ctl = wm_adsp_get_ctl(dsp, name, type, alg);
- if (!ctl)
+ cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
+ if (!cs_ctl)
return -EINVAL;
- if (len > ctl->len)
+ ctl = cs_ctl->priv;
+
+ if (len > cs_ctl->len)
return -EINVAL;
- ret = wm_coeff_write_ctrl(ctl, buf, len);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, buf, len);
if (ret)
return ret;
- if (ctl->flags & WMFW_CTL_FLAG_SYS)
+ if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
return 0;
if (dsp->component->name_prefix)
@@ -2087,683 +729,83 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len)
{
- struct wm_coeff_ctl *ctl;
+ struct cs_dsp_coeff_ctl *cs_ctl;
- ctl = wm_adsp_get_ctl(dsp, name, type, alg);
- if (!ctl)
+ cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
+ if (!cs_ctl)
return -EINVAL;
- if (len > ctl->len)
+ if (len > cs_ctl->len)
return -EINVAL;
- return wm_coeff_read_ctrl(ctl, buf, len);
+ return cs_dsp_coeff_read_ctrl(cs_ctl, buf, len);
}
EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
-static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
- const struct wm_adsp_alg_region *alg_region)
-{
- struct wm_coeff_ctl *ctl;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
- alg_region->alg == ctl->alg_region.alg &&
- alg_region->type == ctl->alg_region.type) {
- ctl->alg_region.base = alg_region->base;
- }
- }
-}
-
-static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
- const struct wm_adsp_region *mem,
- unsigned int pos, unsigned int len)
-{
- void *alg;
- unsigned int reg;
- int ret;
- __be32 val;
-
- if (n_algs == 0) {
- adsp_err(dsp, "No algorithms\n");
- return ERR_PTR(-EINVAL);
- }
-
- if (n_algs > 1024) {
- adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
- return ERR_PTR(-EINVAL);
- }
-
- /* Read the terminator first to validate the length */
- reg = dsp->ops->region_to_reg(mem, pos + len);
-
- ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list end: %d\n",
- ret);
- return ERR_PTR(ret);
- }
-
- if (be32_to_cpu(val) != 0xbedead)
- adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
- reg, be32_to_cpu(val));
-
- /* Convert length from DSP words to bytes */
- len *= sizeof(u32);
-
- alg = kzalloc(len, GFP_KERNEL | GFP_DMA);
- if (!alg)
- return ERR_PTR(-ENOMEM);
-
- reg = dsp->ops->region_to_reg(mem, pos);
-
- ret = regmap_raw_read(dsp->regmap, reg, alg, len);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
- kfree(alg);
- return ERR_PTR(ret);
- }
-
- return alg;
-}
-
-static struct wm_adsp_alg_region *
- wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
-{
- struct wm_adsp_alg_region *alg_region;
-
- list_for_each_entry(alg_region, &dsp->alg_regions, list) {
- if (id == alg_region->alg && type == alg_region->type)
- return alg_region;
- }
-
- return NULL;
-}
-
-static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
- int type, __be32 id,
- __be32 base)
-{
- struct wm_adsp_alg_region *alg_region;
-
- alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
- if (!alg_region)
- return ERR_PTR(-ENOMEM);
-
- alg_region->type = type;
- alg_region->alg = be32_to_cpu(id);
- alg_region->base = be32_to_cpu(base);
-
- list_add_tail(&alg_region->list, &dsp->alg_regions);
-
- if (dsp->fw_ver > 0)
- wm_adsp_ctl_fixup_base(dsp, alg_region);
-
- return alg_region;
-}
-
-static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
-{
- struct wm_adsp_alg_region *alg_region;
-
- while (!list_empty(&dsp->alg_regions)) {
- alg_region = list_first_entry(&dsp->alg_regions,
- struct wm_adsp_alg_region,
- list);
- list_del(&alg_region->list);
- kfree(alg_region);
- }
-}
-
-static void wmfw_parse_id_header(struct wm_adsp *dsp,
- struct wmfw_id_hdr *fw, int nalgs)
-{
- dsp->fw_id = be32_to_cpu(fw->id);
- dsp->fw_id_version = be32_to_cpu(fw->ver);
-
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n",
- dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16,
- (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,
- nalgs);
-}
-
-static void wmfw_v3_parse_id_header(struct wm_adsp *dsp,
- struct wmfw_v3_id_hdr *fw, int nalgs)
-{
- dsp->fw_id = be32_to_cpu(fw->id);
- dsp->fw_id_version = be32_to_cpu(fw->ver);
- dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id);
-
- adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n",
- dsp->fw_id, dsp->fw_vendor_id,
- (dsp->fw_id_version & 0xff0000) >> 16,
- (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,
- nalgs);
-}
-
-static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions,
- const int *type, __be32 *base)
+static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
+ const struct firmware *wmfw_firmware,
+ char *wmfw_filename,
+ const struct firmware *coeff_firmware,
+ char *coeff_filename)
{
- struct wm_adsp_alg_region *alg_region;
- int i;
-
- for (i = 0; i < nregions; i++) {
- alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
- }
+ if (wmfw_firmware)
+ release_firmware(wmfw_firmware);
+ kfree(wmfw_filename);
- return 0;
+ if (coeff_firmware)
+ release_firmware(coeff_firmware);
+ kfree(coeff_filename);
}
-static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
+static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
+ const struct firmware **firmware,
+ char **filename,
+ char *suffix)
{
- struct wmfw_adsp1_id_hdr adsp1_id;
- struct wmfw_adsp1_alg_hdr *adsp1_alg;
- struct wm_adsp_alg_region *alg_region;
- const struct wm_adsp_region *mem;
- unsigned int pos, len;
- size_t n_algs;
- int i, ret;
-
- mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
- if (WARN_ON(!mem))
- return -EINVAL;
-
- ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
- sizeof(adsp1_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
-
- n_algs = be32_to_cpu(adsp1_id.n_algs);
-
- wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
- adsp1_id.fw.id, adsp1_id.zm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
- adsp1_id.fw.id, adsp1_id.dm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- /* Calculate offset and length in DSP words */
- pos = sizeof(adsp1_id) / sizeof(u32);
- len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);
-
- adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
- if (IS_ERR(adsp1_alg))
- return PTR_ERR(adsp1_alg);
-
- for (i = 0; i < n_algs; i++) {
- adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
- i, be32_to_cpu(adsp1_alg[i].alg.id),
- (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
- be32_to_cpu(adsp1_alg[i].dm),
- be32_to_cpu(adsp1_alg[i].zm));
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
- adsp1_alg[i].alg.id,
- adsp1_alg[i].dm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp1_alg[i + 1].dm);
- len -= be32_to_cpu(adsp1_alg[i].dm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0,
- SNDRV_CTL_ELEM_TYPE_BYTES);
- } else {
- adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
- be32_to_cpu(adsp1_alg[i].alg.id));
- }
- }
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
- adsp1_alg[i].alg.id,
- adsp1_alg[i].zm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp1_alg[i + 1].zm);
- len -= be32_to_cpu(adsp1_alg[i].zm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0,
- SNDRV_CTL_ELEM_TYPE_BYTES);
- } else {
- adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
- be32_to_cpu(adsp1_alg[i].alg.id));
- }
- }
- }
-
-out:
- kfree(adsp1_alg);
- return ret;
-}
-
-static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
-{
- struct wmfw_adsp2_id_hdr adsp2_id;
- struct wmfw_adsp2_alg_hdr *adsp2_alg;
- struct wm_adsp_alg_region *alg_region;
- const struct wm_adsp_region *mem;
- unsigned int pos, len;
- size_t n_algs;
- int i, ret;
+ struct cs_dsp *cs_dsp = &dsp->cs_dsp;
+ int ret = 0;
- mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
- if (WARN_ON(!mem))
- return -EINVAL;
+ *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", dsp->part, dsp->fwf_name,
+ wm_adsp_fw[dsp->fw].file, suffix);
+ if (*filename == NULL)
+ return -ENOMEM;
- ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
- sizeof(adsp2_id));
+ ret = request_firmware(firmware, *filename, cs_dsp->dev);
if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
+ adsp_err(dsp, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
}
- n_algs = be32_to_cpu(adsp2_id.n_algs);
-
- wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
- adsp2_id.fw.id, adsp2_id.xm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
- adsp2_id.fw.id, adsp2_id.ym);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
- adsp2_id.fw.id, adsp2_id.zm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- /* Calculate offset and length in DSP words */
- pos = sizeof(adsp2_id) / sizeof(u32);
- len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);
-
- adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
- if (IS_ERR(adsp2_alg))
- return PTR_ERR(adsp2_alg);
-
- for (i = 0; i < n_algs; i++) {
- adsp_info(dsp,
- "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
- i, be32_to_cpu(adsp2_alg[i].alg.id),
- (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
- be32_to_cpu(adsp2_alg[i].xm),
- be32_to_cpu(adsp2_alg[i].ym),
- be32_to_cpu(adsp2_alg[i].zm));
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].xm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].xm);
- len -= be32_to_cpu(adsp2_alg[i].xm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0,
- SNDRV_CTL_ELEM_TYPE_BYTES);
- } else {
- adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].ym);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].ym);
- len -= be32_to_cpu(adsp2_alg[i].ym);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0,
- SNDRV_CTL_ELEM_TYPE_BYTES);
- } else {
- adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].zm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].zm);
- len -= be32_to_cpu(adsp2_alg[i].zm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0,
- SNDRV_CTL_ELEM_TYPE_BYTES);
- } else {
- adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
- }
-
-out:
- kfree(adsp2_alg);
return ret;
}
-static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id,
- __be32 xm_base, __be32 ym_base)
-{
- static const int types[] = {
- WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED,
- WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED
- };
- __be32 bases[] = { xm_base, xm_base, ym_base, ym_base };
-
- return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases);
-}
-
-static int wm_halo_setup_algs(struct wm_adsp *dsp)
+static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
{
- struct wmfw_halo_id_hdr halo_id;
- struct wmfw_halo_alg_hdr *halo_alg;
- const struct wm_adsp_region *mem;
- unsigned int pos, len;
- size_t n_algs;
- int i, ret;
-
- mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
- if (WARN_ON(!mem))
- return -EINVAL;
-
- ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id,
- sizeof(halo_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
-
- n_algs = be32_to_cpu(halo_id.n_algs);
-
- wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs);
-
- ret = wm_halo_create_regions(dsp, halo_id.fw.id,
- halo_id.xm_base, halo_id.ym_base);
- if (ret)
- return ret;
-
- /* Calculate offset and length in DSP words */
- pos = sizeof(halo_id) / sizeof(u32);
- len = (sizeof(*halo_alg) * n_algs) / sizeof(u32);
-
- halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
- if (IS_ERR(halo_alg))
- return PTR_ERR(halo_alg);
-
- for (i = 0; i < n_algs; i++) {
- adsp_info(dsp,
- "%d: ID %x v%d.%d.%d XM@%x YM@%x\n",
- i, be32_to_cpu(halo_alg[i].alg.id),
- (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16,
- (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8,
- be32_to_cpu(halo_alg[i].alg.ver) & 0xff,
- be32_to_cpu(halo_alg[i].xm_base),
- be32_to_cpu(halo_alg[i].ym_base));
-
- ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id,
- halo_alg[i].xm_base,
- halo_alg[i].ym_base);
- if (ret)
- goto out;
- }
-
-out:
- kfree(halo_alg);
- return ret;
-}
-
-static int wm_adsp_load_coeff(struct wm_adsp *dsp)
-{
- LIST_HEAD(buf_list);
- struct regmap *regmap = dsp->regmap;
- struct wmfw_coeff_hdr *hdr;
- struct wmfw_coeff_item *blk;
- const struct firmware *firmware;
- const struct wm_adsp_region *mem;
- struct wm_adsp_alg_region *alg_region;
- const char *region_name;
- int ret, pos, blocks, type, offset, reg;
- char *file;
- struct wm_adsp_buf *buf;
-
- file = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (file == NULL)
- return -ENOMEM;
-
- snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name,
- wm_adsp_fw[dsp->fw].file);
- file[PAGE_SIZE - 1] = '\0';
-
- ret = request_firmware(&firmware, file, dsp->dev);
- if (ret != 0) {
- adsp_warn(dsp, "Failed to request '%s'\n", file);
- ret = 0;
- goto out;
- }
- ret = -EINVAL;
-
- if (sizeof(*hdr) >= firmware->size) {
- adsp_err(dsp, "%s: file too short, %zu bytes\n",
- file, firmware->size);
- goto out_fw;
- }
-
- hdr = (void *)&firmware->data[0];
- if (memcmp(hdr->magic, "WMDR", 4) != 0) {
- adsp_err(dsp, "%s: invalid magic\n", file);
- goto out_fw;
- }
-
- switch (be32_to_cpu(hdr->rev) & 0xff) {
- case 1:
- break;
- default:
- adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
- file, be32_to_cpu(hdr->rev) & 0xff);
- ret = -EINVAL;
- goto out_fw;
- }
-
- adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
- (le32_to_cpu(hdr->ver) >> 16) & 0xff,
- (le32_to_cpu(hdr->ver) >> 8) & 0xff,
- le32_to_cpu(hdr->ver) & 0xff);
-
- pos = le32_to_cpu(hdr->len);
-
- blocks = 0;
- while (pos < firmware->size &&
- sizeof(*blk) < firmware->size - pos) {
- blk = (void *)(&firmware->data[pos]);
-
- type = le16_to_cpu(blk->type);
- offset = le16_to_cpu(blk->offset);
-
- adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
- file, blocks, le32_to_cpu(blk->id),
- (le32_to_cpu(blk->ver) >> 16) & 0xff,
- (le32_to_cpu(blk->ver) >> 8) & 0xff,
- le32_to_cpu(blk->ver) & 0xff);
- adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
- file, blocks, le32_to_cpu(blk->len), offset, type);
-
- reg = 0;
- region_name = "Unknown";
- switch (type) {
- case (WMFW_NAME_TEXT << 8):
- case (WMFW_INFO_TEXT << 8):
- case (WMFW_METADATA << 8):
- break;
- case (WMFW_ABSOLUTE << 8):
- /*
- * Old files may use this for global
- * coefficients.
- */
- if (le32_to_cpu(blk->id) == dsp->fw_id &&
- offset == 0) {
- region_name = "global coefficients";
- mem = wm_adsp_find_region(dsp, type);
- if (!mem) {
- adsp_err(dsp, "No ZM\n");
- break;
- }
- reg = dsp->ops->region_to_reg(mem, 0);
-
- } else {
- region_name = "register";
- reg = offset;
- }
- break;
-
- case WMFW_ADSP1_DM:
- case WMFW_ADSP1_ZM:
- case WMFW_ADSP2_XM:
- case WMFW_ADSP2_YM:
- case WMFW_HALO_XM_PACKED:
- case WMFW_HALO_YM_PACKED:
- case WMFW_HALO_PM_PACKED:
- adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
- file, blocks, le32_to_cpu(blk->len),
- type, le32_to_cpu(blk->id));
-
- mem = wm_adsp_find_region(dsp, type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n", type);
- break;
- }
-
- alg_region = wm_adsp_find_alg_region(dsp, type,
- le32_to_cpu(blk->id));
- if (alg_region) {
- reg = alg_region->base;
- reg = dsp->ops->region_to_reg(mem, reg);
- reg += offset;
- } else {
- adsp_err(dsp, "No %x for algorithm %x\n",
- type, le32_to_cpu(blk->id));
- }
- break;
-
- default:
- adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
- file, blocks, type, pos);
- break;
- }
-
- if (reg) {
- if (le32_to_cpu(blk->len) >
- firmware->size - pos - sizeof(*blk)) {
- adsp_err(dsp,
- "%s.%d: %s region len %d bytes exceeds file length %zu\n",
- file, blocks, region_name,
- le32_to_cpu(blk->len),
- firmware->size);
- ret = -EINVAL;
- goto out_fw;
- }
-
- buf = wm_adsp_buf_alloc(blk->data,
- le32_to_cpu(blk->len),
- &buf_list);
- if (!buf) {
- adsp_err(dsp, "Out of memory\n");
- ret = -ENOMEM;
- goto out_fw;
- }
-
- adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
- file, blocks, le32_to_cpu(blk->len),
- reg);
- ret = regmap_raw_write_async(regmap, reg, buf->buf,
- le32_to_cpu(blk->len));
- if (ret != 0) {
- adsp_err(dsp,
- "%s.%d: Failed to write to %x in %s: %d\n",
- file, blocks, reg, region_name, ret);
- }
- }
-
- pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
- blocks++;
- }
+ int ret = 0;
- ret = regmap_async_complete(regmap);
+ ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "wmfw");
if (ret != 0)
- adsp_err(dsp, "Failed to complete async write: %d\n", ret);
-
- if (pos > firmware->size)
- adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
- file, blocks, pos - firmware->size);
+ return ret;
- wm_adsp_debugfs_save_binname(dsp, file);
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, "bin");
-out_fw:
- regmap_async_complete(regmap);
- release_firmware(firmware);
- wm_adsp_buf_free(&buf_list);
-out:
- kfree(file);
- return ret;
+ return 0;
}
-static int wm_adsp_create_name(struct wm_adsp *dsp)
+static int wm_adsp_common_init(struct wm_adsp *dsp)
{
char *p;
- if (!dsp->name) {
- dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d",
- dsp->num);
- if (!dsp->name)
- return -ENOMEM;
- }
+ INIT_LIST_HEAD(&dsp->compr_list);
+ INIT_LIST_HEAD(&dsp->buffer_list);
if (!dsp->fwf_name) {
- p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL);
+ p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -2775,28 +817,16 @@ static int wm_adsp_create_name(struct wm_adsp *dsp)
return 0;
}
-static int wm_adsp_common_init(struct wm_adsp *dsp)
+int wm_adsp1_init(struct wm_adsp *dsp)
{
int ret;
- ret = wm_adsp_create_name(dsp);
+ dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;
+
+ ret = cs_dsp_adsp1_init(&dsp->cs_dsp);
if (ret)
return ret;
- INIT_LIST_HEAD(&dsp->alg_regions);
- INIT_LIST_HEAD(&dsp->ctl_list);
- INIT_LIST_HEAD(&dsp->compr_list);
- INIT_LIST_HEAD(&dsp->buffer_list);
-
- mutex_init(&dsp->pwr_lock);
-
- return 0;
-}
-
-int wm_adsp1_init(struct wm_adsp *dsp)
-{
- dsp->ops = &wm_adsp1_ops;
-
return wm_adsp_common_init(dsp);
}
EXPORT_SYMBOL_GPL(wm_adsp1_init);
@@ -2808,314 +838,49 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- struct wm_coeff_ctl *ctl;
- int ret;
- unsigned int val;
+ int ret = 0;
+ char *wmfw_filename = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ const struct firmware *coeff_firmware = NULL;
dsp->component = component;
- mutex_lock(&dsp->pwr_lock);
-
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_SYS_ENA, ADSP1_SYS_ENA);
-
- /*
- * For simplicity set the DSP clock rate to be the
- * SYSCLK rate rather than making it configurable.
- */
- if (dsp->sysclk_reg) {
- ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
- ret);
- goto err_mutex;
- }
-
- val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
-
- ret = regmap_update_bits(dsp->regmap,
- dsp->base + ADSP1_CONTROL_31,
- ADSP1_CLK_SEL_MASK, val);
- if (ret != 0) {
- adsp_err(dsp, "Failed to set clock rate: %d\n",
- ret);
- goto err_mutex;
- }
- }
-
- ret = wm_adsp_load(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp1_setup_algs(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp_load_coeff(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Sync set controls */
- ret = wm_coeff_sync_controls(dsp);
- if (ret != 0)
- goto err_ena;
-
- dsp->booted = true;
+ ret = wm_adsp_request_firmware_files(dsp,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret)
+ break;
- /* Start the core running */
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_CORE_ENA | ADSP1_START,
- ADSP1_CORE_ENA | ADSP1_START);
+ ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename,
+ wm_adsp_fw_text[dsp->fw]);
- dsp->running = true;
+ wm_adsp_release_firmware_files(dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
break;
-
case SND_SOC_DAPM_PRE_PMD:
- dsp->running = false;
- dsp->booted = false;
-
- /* Halt the core */
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_CORE_ENA | ADSP1_START, 0);
-
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
- ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
-
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_SYS_ENA, 0);
-
- list_for_each_entry(ctl, &dsp->ctl_list, list)
- ctl->enabled = 0;
-
-
- wm_adsp_free_alg_regions(dsp);
+ cs_dsp_adsp1_power_down(&dsp->cs_dsp);
break;
-
default:
break;
}
- mutex_unlock(&dsp->pwr_lock);
-
- return 0;
-
-err_ena:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_SYS_ENA, 0);
-err_mutex:
- mutex_unlock(&dsp->pwr_lock);
-
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp1_event);
-static int wm_adsp2v2_enable_core(struct wm_adsp *dsp)
-{
- unsigned int val;
- int ret, count;
-
- /* Wait for the RAM to start, should be near instantaneous */
- for (count = 0; count < 10; ++count) {
- ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
- if (ret != 0)
- return ret;
-
- if (val & ADSP2_RAM_RDY)
- break;
-
- usleep_range(250, 500);
- }
-
- if (!(val & ADSP2_RAM_RDY)) {
- adsp_err(dsp, "Failed to start DSP RAM\n");
- return -EBUSY;
- }
-
- adsp_dbg(dsp, "RAM ready after %d polls\n", count);
-
- return 0;
-}
-
-static int wm_adsp2_enable_core(struct wm_adsp *dsp)
-{
- int ret;
-
- ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, ADSP2_SYS_ENA);
- if (ret != 0)
- return ret;
-
- return wm_adsp2v2_enable_core(dsp);
-}
-
-static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
-{
- struct regmap *regmap = dsp->regmap;
- unsigned int code0, code1, lock_reg;
-
- if (!(lock_regions & WM_ADSP2_REGION_ALL))
- return 0;
-
- lock_regions &= WM_ADSP2_REGION_ALL;
- lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
-
- while (lock_regions) {
- code0 = code1 = 0;
- if (lock_regions & BIT(0)) {
- code0 = ADSP2_LOCK_CODE_0;
- code1 = ADSP2_LOCK_CODE_1;
- }
- if (lock_regions & BIT(1)) {
- code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
- code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
- }
- regmap_write(regmap, lock_reg, code0);
- regmap_write(regmap, lock_reg, code1);
- lock_regions >>= 2;
- lock_reg += 2;
- }
-
- return 0;
-}
-
-static int wm_adsp2_enable_memory(struct wm_adsp *dsp)
-{
- return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, ADSP2_MEM_ENA);
-}
-
-static void wm_adsp2_disable_memory(struct wm_adsp *dsp)
-{
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
-}
-
-static void wm_adsp2_disable_core(struct wm_adsp *dsp)
-{
- regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
-
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
-}
-
-static void wm_adsp2v2_disable_core(struct wm_adsp *dsp)
-{
- regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
-}
-
-static void wm_adsp_boot_work(struct work_struct *work)
-{
- struct wm_adsp *dsp = container_of(work,
- struct wm_adsp,
- boot_work);
- int ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- if (dsp->ops->enable_memory) {
- ret = dsp->ops->enable_memory(dsp);
- if (ret != 0)
- goto err_mutex;
- }
-
- if (dsp->ops->enable_core) {
- ret = dsp->ops->enable_core(dsp);
- if (ret != 0)
- goto err_mem;
- }
-
- ret = wm_adsp_load(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = dsp->ops->setup_algs(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp_load_coeff(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp);
- if (ret != 0)
- goto err_ena;
-
- if (dsp->ops->disable_core)
- dsp->ops->disable_core(dsp);
-
- dsp->booted = true;
-
- mutex_unlock(&dsp->pwr_lock);
-
- return;
-
-err_ena:
- if (dsp->ops->disable_core)
- dsp->ops->disable_core(dsp);
-err_mem:
- if (dsp->ops->disable_memory)
- dsp->ops->disable_memory(dsp);
-err_mutex:
- mutex_unlock(&dsp->pwr_lock);
-}
-
-static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions)
-{
- struct reg_sequence config[] = {
- { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 },
- { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA },
- { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions },
- { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions },
- { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions },
- { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions },
- { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions },
- { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions },
- { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions },
- { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions },
- { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions },
- { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF },
- { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions },
- { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions },
- { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions },
- { dsp->base + HALO_MPU_LOCK_CONFIG, 0 },
- };
-
- return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config));
-}
-
int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- int ret;
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK,
- freq << ADSP2_CLK_SEL_SHIFT);
- if (ret)
- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
-
- return ret;
+ return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);
}
EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
@@ -3145,7 +910,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
struct wm_adsp *dsp = &dsps[mc->shift - 1];
char preload[32];
- snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
dsp->preloaded = ucontrol->value.integer.value[0];
@@ -3162,16 +927,31 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
-static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
+static void wm_adsp_boot_work(struct work_struct *work)
{
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
- ADSP2_WDT_ENA_MASK, 0);
-}
+ 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;
+ char *coeff_filename = NULL;
+ const struct firmware *coeff_firmware = NULL;
+
+ ret = wm_adsp_request_firmware_files(dsp,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret)
+ return;
-static void wm_halo_stop_watchdog(struct wm_adsp *dsp)
-{
- regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL,
- HALO_WDT_EN_MASK, 0);
+ 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);
}
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
@@ -3180,33 +960,13 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- struct wm_coeff_ctl *ctl;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
queue_work(system_unbound_wq, &dsp->boot_work);
break;
case SND_SOC_DAPM_PRE_PMD:
- mutex_lock(&dsp->pwr_lock);
-
- wm_adsp_debugfs_clear(dsp);
-
- dsp->fw_id = 0;
- dsp->fw_id_version = 0;
-
- dsp->booted = false;
-
- if (dsp->ops->disable_memory)
- dsp->ops->disable_memory(dsp);
-
- list_for_each_entry(ctl, &dsp->ctl_list, list)
- ctl->enabled = 0;
-
- wm_adsp_free_alg_regions(dsp);
-
- mutex_unlock(&dsp->pwr_lock);
-
- adsp_dbg(dsp, "Shutdown complete\n");
+ cs_dsp_power_down(&dsp->cs_dsp);
break;
default:
break;
@@ -3216,17 +976,24 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(wm_adsp_early_event);
-static int wm_adsp2_start_core(struct wm_adsp *dsp)
+static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)
{
- return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START,
- ADSP2_CORE_ENA | ADSP2_START);
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
+
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ return wm_adsp_buffer_init(dsp);
+
+ return 0;
}
-static void wm_adsp2_stop_core(struct wm_adsp *dsp)
+static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
{
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START, 0);
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
+
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ wm_adsp_buffer_free(dsp);
+
+ dsp->fatal_error = false;
}
int wm_adsp_event(struct snd_soc_dapm_widget *w,
@@ -3235,127 +1002,32 @@ int wm_adsp_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- int ret;
+ int ret = 0;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
flush_work(&dsp->boot_work);
-
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->booted) {
- ret = -EIO;
- goto err;
- }
-
- if (dsp->ops->enable_core) {
- ret = dsp->ops->enable_core(dsp);
- if (ret != 0)
- goto err;
- }
-
- /* Sync set controls */
- ret = wm_coeff_sync_controls(dsp);
- if (ret != 0)
- goto err;
-
- if (dsp->ops->lock_memory) {
- ret = dsp->ops->lock_memory(dsp, dsp->lock_regions);
- if (ret != 0) {
- adsp_err(dsp, "Error configuring MPU: %d\n",
- ret);
- goto err;
- }
- }
-
- if (dsp->ops->start_core) {
- ret = dsp->ops->start_core(dsp);
- if (ret != 0)
- goto err;
- }
-
- if (wm_adsp_fw[dsp->fw].num_caps != 0) {
- ret = wm_adsp_buffer_init(dsp);
- if (ret < 0)
- goto err;
- }
-
- dsp->running = true;
-
- mutex_unlock(&dsp->pwr_lock);
+ ret = cs_dsp_run(&dsp->cs_dsp);
break;
-
case SND_SOC_DAPM_PRE_PMD:
- /* Tell the firmware to cleanup */
- wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
-
- if (dsp->ops->stop_watchdog)
- dsp->ops->stop_watchdog(dsp);
-
- /* Log firmware state, it can be useful for analysis */
- if (dsp->ops->show_fw_status)
- dsp->ops->show_fw_status(dsp);
-
- mutex_lock(&dsp->pwr_lock);
-
- dsp->running = false;
-
- if (dsp->ops->stop_core)
- dsp->ops->stop_core(dsp);
- if (dsp->ops->disable_core)
- dsp->ops->disable_core(dsp);
-
- if (wm_adsp_fw[dsp->fw].num_caps != 0)
- wm_adsp_buffer_free(dsp);
-
- dsp->fatal_error = false;
-
- mutex_unlock(&dsp->pwr_lock);
-
- adsp_dbg(dsp, "Execution stopped\n");
+ cs_dsp_stop(&dsp->cs_dsp);
break;
-
default:
break;
}
- return 0;
-err:
- if (dsp->ops->stop_core)
- dsp->ops->stop_core(dsp);
- if (dsp->ops->disable_core)
- dsp->ops->disable_core(dsp);
- mutex_unlock(&dsp->pwr_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp_event);
-static int wm_halo_start_core(struct wm_adsp *dsp)
-{
- return regmap_update_bits(dsp->regmap,
- dsp->base + HALO_CCM_CORE_CONTROL,
- HALO_CORE_RESET | HALO_CORE_EN,
- HALO_CORE_RESET | HALO_CORE_EN);
-}
-
-static void wm_halo_stop_core(struct wm_adsp *dsp)
-{
- regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,
- HALO_CORE_EN, 0);
-
- /* reset halo core with CORE_SOFT_RESET */
- regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET,
- HALO_CORE_SOFT_RESET_MASK, 1);
-}
-
int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
{
char preload[32];
- snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
snd_soc_component_disable_pin(component, preload);
- wm_adsp2_init_debugfs(dsp, component);
+ cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
dsp->component = component;
@@ -3365,7 +1037,7 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
{
- wm_adsp2_cleanup_debugfs(dsp);
+ cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
return 0;
}
@@ -3375,37 +1047,16 @@ int wm_adsp2_init(struct wm_adsp *dsp)
{
int ret;
- ret = wm_adsp_common_init(dsp);
- if (ret)
- return ret;
-
- switch (dsp->rev) {
- case 0:
- /*
- * Disable the DSP memory by default when in reset for a small
- * power saving.
- */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
- if (ret) {
- adsp_err(dsp,
- "Failed to clear memory retention: %d\n", ret);
- return ret;
- }
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
- dsp->ops = &wm_adsp2_ops[0];
- break;
- case 1:
- dsp->ops = &wm_adsp2_ops[1];
- break;
- default:
- dsp->ops = &wm_adsp2_ops[2];
- break;
- }
+ dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);
+ dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
- INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
+ ret = cs_dsp_adsp2_init(&dsp->cs_dsp);
+ if (ret)
+ return ret;
- return 0;
+ return wm_adsp_common_init(dsp);
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
@@ -3413,28 +1064,22 @@ int wm_halo_init(struct wm_adsp *dsp)
{
int ret;
- ret = wm_adsp_common_init(dsp);
- if (ret)
- return ret;
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
- dsp->ops = &wm_halo_ops;
+ dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);
+ dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
- INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
+ ret = cs_dsp_halo_init(&dsp->cs_dsp);
+ if (ret)
+ return ret;
- return 0;
+ return wm_adsp_common_init(dsp);
}
EXPORT_SYMBOL_GPL(wm_halo_init);
void wm_adsp2_remove(struct wm_adsp *dsp)
{
- struct wm_coeff_ctl *ctl;
-
- while (!list_empty(&dsp->ctl_list)) {
- ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
- list);
- list_del(&ctl->list);
- wm_adsp_free_ctl_blk(ctl);
- }
+ cs_dsp_remove(&dsp->cs_dsp);
}
EXPORT_SYMBOL_GPL(wm_adsp2_remove);
@@ -3487,7 +1132,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
struct snd_soc_pcm_runtime *rtd = stream->private_data;
int ret = 0;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
if (wm_adsp_fw[dsp->fw].num_caps == 0) {
adsp_err(dsp, "%s: Firmware does not support compressed API\n",
@@ -3527,7 +1172,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
stream->runtime->private_data = compr;
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -3539,7 +1184,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component,
struct wm_adsp_compr *compr = stream->runtime->private_data;
struct wm_adsp *dsp = compr->dsp;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
wm_adsp_compr_detach(compr);
list_del(&compr->list);
@@ -3547,7 +1192,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component,
kfree(compr->raw_buf);
kfree(compr);
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return 0;
}
@@ -3566,7 +1211,7 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
- params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
+ params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {
compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
params->buffer.fragment_size,
params->buffer.fragments);
@@ -3605,7 +1250,7 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
{
- return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
+ return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;
}
int wm_adsp_compr_set_params(struct snd_soc_component *component,
@@ -3661,88 +1306,19 @@ int wm_adsp_compr_get_caps(struct snd_soc_component *component,
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
-static int wm_adsp_read_raw_data_block(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr,
- unsigned int num_words, __be32 *data)
-{
- struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
- unsigned int reg;
- int ret;
-
- if (!mem)
- return -EINVAL;
-
- reg = dsp->ops->region_to_reg(mem, mem_addr);
-
- ret = regmap_raw_read(dsp->regmap, reg, data,
- sizeof(*data) * num_words);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr, u32 *data)
-{
- __be32 raw;
- int ret;
-
- ret = wm_adsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw);
- if (ret < 0)
- return ret;
-
- *data = be32_to_cpu(raw) & 0x00ffffffu;
-
- return 0;
-}
-
-static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr, u32 data)
-{
- struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
- __be32 val = cpu_to_be32(data & 0x00ffffffu);
- unsigned int reg;
-
- if (!mem)
- return -EINVAL;
-
- reg = dsp->ops->region_to_reg(mem, mem_addr);
-
- return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
-}
-
static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
unsigned int field_offset, u32 *data)
{
- return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type,
- buf->host_buf_ptr + field_offset, data);
+ return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
+ buf->host_buf_ptr + field_offset, data);
}
static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
unsigned int field_offset, u32 data)
{
- return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type,
- buf->host_buf_ptr + field_offset, data);
-}
-
-static void wm_adsp_remove_padding(u32 *buf, int nwords)
-{
- const __be32 *pack_in = (__be32 *)buf;
- u8 *pack_out = (u8 *)buf;
- int i;
-
- /*
- * DSP words from the register map have pad bytes and the data bytes
- * are in swapped order. This swaps back to the original little-endian
- * order and strips the pad bytes.
- */
- for (i = 0; i < nwords; i++) {
- u32 word = be32_to_cpu(*pack_in++);
- *pack_out++ = (u8)word;
- *pack_out++ = (u8)(word >> 8);
- *pack_out++ = (u8)(word >> 16);
- }
+ return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
+ buf->host_buf_ptr + field_offset,
+ data);
}
static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
@@ -3810,12 +1386,12 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
{
- struct wm_adsp_alg_region *alg_region;
+ struct cs_dsp_alg_region *alg_region;
struct wm_adsp_compr_buf *buf;
u32 xmalg, addr, magic;
int i, ret;
- alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
+ alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);
if (!alg_region) {
adsp_err(dsp, "No algorithm region found\n");
return -EINVAL;
@@ -3825,10 +1401,10 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
if (!buf)
return -ENOMEM;
- xmalg = dsp->ops->sys_config_size / sizeof(__be32);
+ xmalg = dsp->sys_config_size / sizeof(__be32);
addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
- ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
+ ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);
if (ret < 0)
return ret;
@@ -3837,8 +1413,8 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
for (i = 0; i < 5; ++i) {
- ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
- &buf->host_buf_ptr);
+ ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,
+ &buf->host_buf_ptr);
if (ret < 0)
return ret;
@@ -3862,40 +1438,36 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
return 0;
}
-static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
+static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
{
struct wm_adsp_host_buf_coeff_v1 coeff_v1;
struct wm_adsp_compr_buf *buf;
- unsigned int reg, version;
- __be32 bufp;
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
+ unsigned int version;
int ret, i;
- ret = wm_coeff_base_reg(ctl, &reg);
- if (ret)
- return ret;
-
for (i = 0; i < 5; ++i) {
- ret = regmap_raw_read(ctl->dsp->regmap, reg, &bufp, sizeof(bufp));
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, &coeff_v1, sizeof(coeff_v1));
if (ret < 0)
return ret;
- if (bufp)
+ if (coeff_v1.host_buf_ptr)
break;
usleep_range(1000, 2000);
}
- if (!bufp) {
- adsp_err(ctl->dsp, "Failed to acquire host buffer\n");
+ if (!coeff_v1.host_buf_ptr) {
+ adsp_err(dsp, "Failed to acquire host buffer\n");
return -EIO;
}
- buf = wm_adsp_buffer_alloc(ctl->dsp);
+ buf = wm_adsp_buffer_alloc(dsp);
if (!buf)
return -ENOMEM;
- buf->host_buf_mem_type = ctl->alg_region.type;
- buf->host_buf_ptr = be32_to_cpu(bufp);
+ buf->host_buf_mem_type = cs_ctl->alg_region.type;
+ buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);
ret = wm_adsp_buffer_populate(buf);
if (ret < 0)
@@ -3905,29 +1477,24 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
* v0 host_buffer coefficients didn't have versioning, so if the
* control is one word, assume version 0.
*/
- if (ctl->len == 4) {
+ if (cs_ctl->len == 4) {
compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr);
return 0;
}
- ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1,
- sizeof(coeff_v1));
- if (ret < 0)
- return ret;
-
version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;
version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
- adsp_err(ctl->dsp,
+ adsp_err(dsp,
"Host buffer coeff ver %u > supported version %u\n",
version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
return -EINVAL;
}
- wm_adsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
+ cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
- buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part,
+ buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,
(char *)&coeff_v1.name);
compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
@@ -3938,17 +1505,17 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
static int wm_adsp_buffer_init(struct wm_adsp *dsp)
{
- struct wm_coeff_ctl *ctl;
+ struct cs_dsp_coeff_ctl *cs_ctl;
int ret;
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
+ list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {
+ if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
continue;
- if (!ctl->enabled)
+ if (!cs_ctl->enabled)
continue;
- ret = wm_adsp_buffer_parse_coeff(ctl);
+ ret = wm_adsp_buffer_parse_coeff(cs_ctl);
if (ret < 0) {
adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
goto error;
@@ -4016,7 +1583,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component,
compr_dbg(compr, "Trigger: %d\n", cmd);
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -4052,7 +1619,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component,
break;
}
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -4101,7 +1668,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
avail += wm_adsp_buffer_size(buf);
compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
- buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
+ buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);
buf->avail = avail;
@@ -4114,7 +1681,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
struct wm_adsp_compr *compr;
int ret = 0;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
if (list_empty(&dsp->buffer_list)) {
ret = -ENODEV;
@@ -4152,7 +1719,7 @@ out_notify:
}
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -4182,7 +1749,7 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component,
compr_dbg(compr, "Pointer request\n");
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
buf = compr->buf;
@@ -4222,11 +1789,11 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component,
}
tstamp->copied_total = compr->copied_total;
- tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
+ tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;
tstamp->sampling_rate = compr->sample_rate;
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -4264,12 +1831,12 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
return 0;
/* Read data from DSP */
- ret = wm_adsp_read_raw_data_block(buf->dsp, mem_type, adsp_addr,
- nwords, (__be32 *)compr->raw_buf);
+ ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,
+ nwords, (__be32 *)compr->raw_buf);
if (ret < 0)
return ret;
- wm_adsp_remove_padding(compr->raw_buf, nwords);
+ cs_dsp_remove_padding(compr->raw_buf, nwords);
/* update read index to account for words read */
buf->read_index += nwords;
@@ -4301,7 +1868,7 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
return -EIO;
}
- count /= WM_ADSP_DATA_WORD_SIZE;
+ count /= CS_DSP_DATA_WORD_SIZE;
do {
nwords = wm_adsp_buffer_capture_block(compr, count);
@@ -4311,7 +1878,7 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
return nwords;
}
- nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
+ nbytes = nwords * CS_DSP_DATA_WORD_SIZE;
compr_dbg(compr, "Read %d bytes\n", nbytes);
@@ -4338,21 +1905,22 @@ int wm_adsp_compr_copy(struct snd_soc_component *component,
struct wm_adsp *dsp = compr->dsp;
int ret;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
if (stream->direction == SND_COMPRESS_CAPTURE)
ret = wm_adsp_compr_read(compr, buf, count);
else
ret = -ENOTSUPP;
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
-static void wm_adsp_fatal_error(struct wm_adsp *dsp)
+static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)
{
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
struct wm_adsp_compr *compr;
dsp->fatal_error = true;
@@ -4366,64 +1934,8 @@ static void wm_adsp_fatal_error(struct wm_adsp *dsp)
irqreturn_t wm_adsp2_bus_error(int irq, void *data)
{
struct wm_adsp *dsp = (struct wm_adsp *)data;
- unsigned int val;
- struct regmap *regmap = dsp->regmap;
- int ret = 0;
-
- mutex_lock(&dsp->pwr_lock);
-
- ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
- if (ret) {
- adsp_err(dsp,
- "Failed to read Region Lock Ctrl register: %d\n", ret);
- goto error;
- }
-
- if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
- adsp_err(dsp, "watchdog timeout error\n");
- dsp->ops->stop_watchdog(dsp);
- wm_adsp_fatal_error(dsp);
- }
-
- if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
- if (val & ADSP2_ADDR_ERR_MASK)
- adsp_err(dsp, "bus error: address error\n");
- else
- adsp_err(dsp, "bus error: region lock error\n");
-
- ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
- if (ret) {
- adsp_err(dsp,
- "Failed to read Bus Err Addr register: %d\n",
- ret);
- goto error;
- }
-
- adsp_err(dsp, "bus error address = 0x%x\n",
- val & ADSP2_BUS_ERR_ADDR_MASK);
- ret = regmap_read(regmap,
- dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
- &val);
- if (ret) {
- adsp_err(dsp,
- "Failed to read Pmem Xmem Err Addr register: %d\n",
- ret);
- goto error;
- }
-
- adsp_err(dsp, "xmem error address = 0x%x\n",
- val & ADSP2_XMEM_ERR_ADDR_MASK);
- adsp_err(dsp, "pmem error address = 0x%x\n",
- (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
- ADSP2_PMEM_ERR_ADDR_SHIFT);
- }
-
- regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
- ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
-
-error:
- mutex_unlock(&dsp->pwr_lock);
+ cs_dsp_adsp2_bus_error(&dsp->cs_dsp);
return IRQ_HANDLED;
}
@@ -4432,55 +1944,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
irqreturn_t wm_halo_bus_error(int irq, void *data)
{
struct wm_adsp *dsp = (struct wm_adsp *)data;
- struct regmap *regmap = dsp->regmap;
- unsigned int fault[6];
- struct reg_sequence clear[] = {
- { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 },
- { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 },
- { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 },
- };
- int ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1,
- fault);
- if (ret) {
- adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret);
- goto exit_unlock;
- }
- adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n",
- *fault & HALO_AHBM_FLAGS_ERR_MASK,
- (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >>
- HALO_AHBM_CORE_ERR_ADDR_SHIFT);
-
- ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0,
- fault);
- if (ret) {
- adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret);
- goto exit_unlock;
- }
-
- adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault);
-
- ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR,
- fault, ARRAY_SIZE(fault));
- if (ret) {
- adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret);
- goto exit_unlock;
- }
-
- adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]);
- adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]);
- adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]);
-
- ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear));
- if (ret)
- adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret);
-
-exit_unlock:
- mutex_unlock(&dsp->pwr_lock);
+ cs_dsp_halo_bus_error(&dsp->cs_dsp);
return IRQ_HANDLED;
}
@@ -4490,99 +1955,23 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data)
{
struct wm_adsp *dsp = data;
- mutex_lock(&dsp->pwr_lock);
-
- adsp_warn(dsp, "WDT Expiry Fault\n");
- dsp->ops->stop_watchdog(dsp);
- wm_adsp_fatal_error(dsp);
-
- mutex_unlock(&dsp->pwr_lock);
+ cs_dsp_halo_wdt_expire(&dsp->cs_dsp);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
-static const struct wm_adsp_ops wm_adsp1_ops = {
- .validate_version = wm_adsp_validate_version,
- .parse_sizes = wm_adsp1_parse_sizes,
- .region_to_reg = wm_adsp_region_to_reg,
+static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
+ .control_add = wm_adsp_control_add,
+ .control_remove = wm_adsp_control_remove,
};
-static const struct wm_adsp_ops wm_adsp2_ops[] = {
- {
- .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
- .parse_sizes = wm_adsp2_parse_sizes,
- .validate_version = wm_adsp_validate_version,
- .setup_algs = wm_adsp2_setup_algs,
- .region_to_reg = wm_adsp_region_to_reg,
-
- .show_fw_status = wm_adsp2_show_fw_status,
-
- .enable_memory = wm_adsp2_enable_memory,
- .disable_memory = wm_adsp2_disable_memory,
-
- .enable_core = wm_adsp2_enable_core,
- .disable_core = wm_adsp2_disable_core,
-
- .start_core = wm_adsp2_start_core,
- .stop_core = wm_adsp2_stop_core,
-
- },
- {
- .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
- .parse_sizes = wm_adsp2_parse_sizes,
- .validate_version = wm_adsp_validate_version,
- .setup_algs = wm_adsp2_setup_algs,
- .region_to_reg = wm_adsp_region_to_reg,
-
- .show_fw_status = wm_adsp2v2_show_fw_status,
-
- .enable_memory = wm_adsp2_enable_memory,
- .disable_memory = wm_adsp2_disable_memory,
- .lock_memory = wm_adsp2_lock,
-
- .enable_core = wm_adsp2v2_enable_core,
- .disable_core = wm_adsp2v2_disable_core,
-
- .start_core = wm_adsp2_start_core,
- .stop_core = wm_adsp2_stop_core,
- },
- {
- .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
- .parse_sizes = wm_adsp2_parse_sizes,
- .validate_version = wm_adsp_validate_version,
- .setup_algs = wm_adsp2_setup_algs,
- .region_to_reg = wm_adsp_region_to_reg,
-
- .show_fw_status = wm_adsp2v2_show_fw_status,
- .stop_watchdog = wm_adsp_stop_watchdog,
-
- .enable_memory = wm_adsp2_enable_memory,
- .disable_memory = wm_adsp2_disable_memory,
- .lock_memory = wm_adsp2_lock,
-
- .enable_core = wm_adsp2v2_enable_core,
- .disable_core = wm_adsp2v2_disable_core,
-
- .start_core = wm_adsp2_start_core,
- .stop_core = wm_adsp2_stop_core,
- },
-};
-
-static const struct wm_adsp_ops wm_halo_ops = {
- .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr),
- .parse_sizes = wm_adsp2_parse_sizes,
- .validate_version = wm_halo_validate_version,
- .setup_algs = wm_halo_setup_algs,
- .region_to_reg = wm_halo_region_to_reg,
-
- .show_fw_status = wm_halo_show_fw_status,
- .stop_watchdog = wm_halo_stop_watchdog,
-
- .lock_memory = wm_halo_configure_mpu,
-
- .start_core = wm_halo_start_core,
- .stop_core = wm_halo_stop_core,
+static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
+ .control_add = wm_adsp_control_add,
+ .control_remove = wm_adsp_control_remove,
+ .post_run = wm_adsp_event_post_run,
+ .post_stop = wm_adsp_event_post_stop,
+ .watchdog_expired = wm_adsp_fatal_error,
};
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index f22131d9cc29..0e2f113bd342 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -10,128 +10,37 @@
#ifndef __WM_ADSP_H
#define __WM_ADSP_H
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/compress_driver.h>
-#include "wmfw.h"
-
/* Return values for wm_adsp_compr_handle_irq */
#define WM_ADSP_COMPR_OK 0
#define WM_ADSP_COMPR_VOICE_TRIGGER 1
-#define WM_ADSP2_REGION_0 BIT(0)
-#define WM_ADSP2_REGION_1 BIT(1)
-#define WM_ADSP2_REGION_2 BIT(2)
-#define WM_ADSP2_REGION_3 BIT(3)
-#define WM_ADSP2_REGION_4 BIT(4)
-#define WM_ADSP2_REGION_5 BIT(5)
-#define WM_ADSP2_REGION_6 BIT(6)
-#define WM_ADSP2_REGION_7 BIT(7)
-#define WM_ADSP2_REGION_8 BIT(8)
-#define WM_ADSP2_REGION_9 BIT(9)
-#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \
- WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \
- WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \
- WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \
- WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9)
-#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9)
-
-struct wm_adsp_region {
- int type;
- unsigned int base;
-};
-
-struct wm_adsp_alg_region {
- struct list_head list;
- unsigned int alg;
- int type;
- unsigned int base;
-};
-
struct wm_adsp_compr;
struct wm_adsp_compr_buf;
-struct wm_adsp_ops;
struct wm_adsp {
+ struct cs_dsp cs_dsp;
const char *part;
- const char *name;
const char *fwf_name;
- int rev;
- int num;
- int type;
- struct device *dev;
- struct regmap *regmap;
struct snd_soc_component *component;
- const struct wm_adsp_ops *ops;
-
- unsigned int base;
- unsigned int base_sysinfo;
- unsigned int sysclk_reg;
- unsigned int sysclk_mask;
- unsigned int sysclk_shift;
-
- struct list_head alg_regions;
-
- unsigned int fw_id;
- unsigned int fw_id_version;
- unsigned int fw_vendor_id;
-
- const struct wm_adsp_region *mem;
- int num_mems;
+ unsigned int sys_config_size;
int fw;
- int fw_ver;
+
+ struct work_struct boot_work;
bool preloaded;
- bool booted;
- bool running;
bool fatal_error;
- struct list_head ctl_list;
-
- struct work_struct boot_work;
-
struct list_head compr_list;
struct list_head buffer_list;
-
- struct mutex pwr_lock;
-
- unsigned int lock_regions;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_root;
- char *wmfw_file_name;
- char *bin_file_name;
-#endif
-
-};
-
-struct wm_adsp_ops {
- unsigned int sys_config_size;
-
- bool (*validate_version)(struct wm_adsp *dsp, unsigned int version);
- unsigned int (*parse_sizes)(struct wm_adsp *dsp,
- const char * const file,
- unsigned int pos,
- const struct firmware *firmware);
- int (*setup_algs)(struct wm_adsp *dsp);
- unsigned int (*region_to_reg)(struct wm_adsp_region const *mem,
- unsigned int offset);
-
- void (*show_fw_status)(struct wm_adsp *dsp);
- void (*stop_watchdog)(struct wm_adsp *dsp);
-
- int (*enable_memory)(struct wm_adsp *dsp);
- void (*disable_memory)(struct wm_adsp *dsp);
- int (*lock_memory)(struct wm_adsp *dsp, unsigned int lock_regions);
-
- int (*enable_core)(struct wm_adsp *dsp);
- void (*disable_core)(struct wm_adsp *dsp);
-
- int (*start_core)(struct wm_adsp *dsp);
- void (*stop_core)(struct wm_adsp *dsp);
};
#define WM_ADSP1(wname, num) \
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
deleted file mode 100644
index f3d51602f85c..000000000000
--- a/sound/soc/codecs/wmfw.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * wmfw.h - Wolfson firmware format information
- *
- * Copyright 2012 Wolfson Microelectronics plc
- *
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- */
-
-#ifndef __WMFW_H
-#define __WMFW_H
-
-#include <linux/types.h>
-
-#define WMFW_MAX_ALG_NAME 256
-#define WMFW_MAX_ALG_DESCR_NAME 256
-
-#define WMFW_MAX_COEFF_NAME 256
-#define WMFW_MAX_COEFF_DESCR_NAME 256
-
-#define WMFW_CTL_FLAG_SYS 0x8000
-#define WMFW_CTL_FLAG_VOLATILE 0x0004
-#define WMFW_CTL_FLAG_WRITEABLE 0x0002
-#define WMFW_CTL_FLAG_READABLE 0x0001
-
-/* Non-ALSA coefficient types start at 0x1000 */
-#define WMFW_CTL_TYPE_ACKED ((__force snd_ctl_elem_type_t)0x1000) /* acked control */
-#define WMFW_CTL_TYPE_HOSTEVENT ((__force snd_ctl_elem_type_t)0x1001) /* event control */
-#define WMFW_CTL_TYPE_HOST_BUFFER ((__force snd_ctl_elem_type_t)0x1002) /* host buffer pointer */
-
-struct wmfw_header {
- char magic[4];
- __le32 len;
- __le16 rev;
- u8 core;
- u8 ver;
-} __packed;
-
-struct wmfw_footer {
- __le64 timestamp;
- __le32 checksum;
-} __packed;
-
-struct wmfw_adsp1_sizes {
- __le32 dm;
- __le32 pm;
- __le32 zm;
-} __packed;
-
-struct wmfw_adsp2_sizes {
- __le32 xm;
- __le32 ym;
- __le32 pm;
- __le32 zm;
-} __packed;
-
-struct wmfw_region {
- union {
- __be32 type;
- __le32 offset;
- };
- __le32 len;
- u8 data[];
-} __packed;
-
-struct wmfw_id_hdr {
- __be32 core_id;
- __be32 core_rev;
- __be32 id;
- __be32 ver;
-} __packed;
-
-struct wmfw_v3_id_hdr {
- __be32 core_id;
- __be32 block_rev;
- __be32 vendor_id;
- __be32 id;
- __be32 ver;
-} __packed;
-
-struct wmfw_adsp1_id_hdr {
- struct wmfw_id_hdr fw;
- __be32 zm;
- __be32 dm;
- __be32 n_algs;
-} __packed;
-
-struct wmfw_adsp2_id_hdr {
- struct wmfw_id_hdr fw;
- __be32 zm;
- __be32 xm;
- __be32 ym;
- __be32 n_algs;
-} __packed;
-
-struct wmfw_halo_id_hdr {
- struct wmfw_v3_id_hdr fw;
- __be32 xm_base;
- __be32 xm_size;
- __be32 ym_base;
- __be32 ym_size;
- __be32 n_algs;
-} __packed;
-
-struct wmfw_alg_hdr {
- __be32 id;
- __be32 ver;
-} __packed;
-
-struct wmfw_adsp1_alg_hdr {
- struct wmfw_alg_hdr alg;
- __be32 zm;
- __be32 dm;
-} __packed;
-
-struct wmfw_adsp2_alg_hdr {
- struct wmfw_alg_hdr alg;
- __be32 zm;
- __be32 xm;
- __be32 ym;
-} __packed;
-
-struct wmfw_halo_alg_hdr {
- struct wmfw_alg_hdr alg;
- __be32 xm_base;
- __be32 xm_size;
- __be32 ym_base;
- __be32 ym_size;
-} __packed;
-
-struct wmfw_adsp_alg_data {
- __le32 id;
- u8 name[WMFW_MAX_ALG_NAME];
- u8 descr[WMFW_MAX_ALG_DESCR_NAME];
- __le32 ncoeff;
- u8 data[];
-} __packed;
-
-struct wmfw_adsp_coeff_data {
- struct {
- __le16 offset;
- __le16 type;
- __le32 size;
- } hdr;
- u8 name[WMFW_MAX_COEFF_NAME];
- u8 descr[WMFW_MAX_COEFF_DESCR_NAME];
- __le16 ctl_type;
- __le16 flags;
- __le32 len;
- u8 data[];
-} __packed;
-
-struct wmfw_coeff_hdr {
- u8 magic[4];
- __le32 len;
- union {
- __be32 rev;
- __le32 ver;
- };
- union {
- __be32 core;
- __le32 core_ver;
- };
- u8 data[];
-} __packed;
-
-struct wmfw_coeff_item {
- __le16 offset;
- __le16 type;
- __le32 id;
- __le32 ver;
- __le32 sr;
- __le32 len;
- u8 data[];
-} __packed;
-
-#define WMFW_ADSP1 1
-#define WMFW_ADSP2 2
-#define WMFW_HALO 4
-
-#define WMFW_ABSOLUTE 0xf0
-#define WMFW_ALGORITHM_DATA 0xf2
-#define WMFW_METADATA 0xfc
-#define WMFW_NAME_TEXT 0xfe
-#define WMFW_INFO_TEXT 0xff
-
-#define WMFW_ADSP1_PM 2
-#define WMFW_ADSP1_DM 3
-#define WMFW_ADSP1_ZM 4
-
-#define WMFW_ADSP2_PM 2
-#define WMFW_ADSP2_ZM 4
-#define WMFW_ADSP2_XM 5
-#define WMFW_ADSP2_YM 6
-
-#define WMFW_HALO_PM_PACKED 0x10
-#define WMFW_HALO_XM_PACKED 0x11
-#define WMFW_HALO_YM_PACKED 0x12
-
-#endif
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
index d21a72314d37..d20ec1571010 100644
--- a/sound/soc/codecs/zl38060.c
+++ b/sound/soc/codecs/zl38060.c
@@ -250,8 +250,8 @@ static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
/* always 32 bits per frame (= 16 bits/channel, 2 channels) */
err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
CFG_CLK_MASTER | CFG_CLK_PCLK_MASK,
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
index 33ce257ae198..5cb58929090d 100644
--- a/sound/soc/dwc/dwc-i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
@@ -356,25 +356,25 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
int ret = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
if (dev->capability & DW_I2S_SLAVE)
ret = 0;
else
ret = -EINVAL;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (dev->capability & DW_I2S_MASTER)
ret = 0;
else
ret = -EINVAL;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ case SND_SOC_DAIFMT_CBC_CFP:
ret = -EINVAL;
break;
default:
- dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+ dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
ret = -EINVAL;
break;
}
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index e13271ea84de..8b61582753c8 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -70,7 +70,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = {
.name = "tlv320aic23",
.stream_name = "TLV320AIC23",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &eukrea_tlv320_snd_ops,
SND_SOC_DAILINK_REG(hifi),
};
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 06107ae46e20..6e6494f9f399 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -356,8 +356,8 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
- switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
@@ -367,7 +367,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
@@ -377,7 +377,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
@@ -387,7 +387,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
@@ -533,8 +533,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
struct device_node *cpu_np, *codec_np, *asrc_np;
struct device_node *np = pdev->dev.of_node;
struct platform_device *asrc_pdev = NULL;
- struct device_node *bitclkmaster = NULL;
- struct device_node *framemaster = NULL;
+ struct device_node *bitclkprovider = NULL;
+ struct device_node *frameprovider = NULL;
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct device *codec_dev = NULL;
@@ -617,29 +617,29 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.slot_width = 32;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
} else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
codec_dai_name = "cs4271-hifi";
priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
codec_dai_name = "sgtl5000";
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) {
codec_dai_name = "tlv320aic32x4-hifi";
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
codec_dai_name = "wm8962";
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
priv->codec_priv.pll_id = WM8962_FLL;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
codec_dai_name = "wm8960-hifi";
priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
codec_dai_name = "ac97-hifi";
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
@@ -648,7 +648,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
} else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) {
codec_dai_name = "fsl-mqs-dai";
priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_CBS_CFS |
+ SND_SOC_DAIFMT_CBC_CFC |
SND_SOC_DAIFMT_NB_NF;
priv->dai_link[1].dpcm_capture = 0;
priv->dai_link[2].dpcm_capture = 0;
@@ -656,7 +656,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) {
codec_dai_name = "wm8524-hifi";
- priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
priv->dai_link[1].dpcm_capture = 0;
priv->dai_link[2].dpcm_capture = 0;
priv->cpu_priv.slot_width = 32;
@@ -664,12 +664,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) {
codec_dai_name = "si476x-codec";
- priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
priv->card.dapm_routes = audio_map_rx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) {
codec_dai_name = "wm8994-aif1";
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1;
priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1;
priv->codec_priv.pll_id = WM8994_FLL1;
@@ -683,29 +683,29 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
}
/* Format info from DT is optional. */
- snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkmaster, &framemaster);
- if (bitclkmaster || framemaster) {
+ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider);
+ if (bitclkprovider || frameprovider) {
unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL);
- if (codec_np == bitclkmaster)
- daifmt |= (codec_np == framemaster) ?
- SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+ if (codec_np == bitclkprovider)
+ daifmt |= (codec_np == frameprovider) ?
+ SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC;
else
- daifmt |= (codec_np == framemaster) ?
- SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+ daifmt |= (codec_np == frameprovider) ?
+ SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC;
/* Override dai_fmt with value from DT */
priv->dai_fmt = daifmt;
}
/* Change direction according to format */
- if (priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) {
+ if (priv->dai_fmt & SND_SOC_DAIFMT_CBP_CFP) {
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_IN;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_IN;
}
- of_node_put(bitclkmaster);
- of_node_put(framemaster);
+ of_node_put(bitclkprovider);
+ of_node_put(frameprovider);
if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
dev_dbg(&pdev->dev, "failed to find codec device\n");
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index f931288e256c..6dbb8c99f626 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -257,10 +257,10 @@ static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- /* For playback the AUDMIX is slave, and for record is master */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBS_CFS:
+ /* For playback the AUDMIX is consumer, and for record is provider */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index bda66b30e063..3a9e2df4e16f 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -52,7 +52,7 @@ struct fsl_esai_soc_data {
* @sck_rate: clock rate of desired SCKx clock
* @hck_dir: the direction of HCKx pads
* @sck_div: if using PSR/PM dividers for SCKx clock
- * @slave_mode: if fully using DAI slave mode
+ * @consumer_mode: if fully using DAI clock consumer mode
* @synchronous: if using tx/rx synchronous mode
* @name: driver name
*/
@@ -78,7 +78,7 @@ struct fsl_esai {
u32 sck_rate[2];
bool hck_dir[2];
bool sck_div[2];
- bool slave_mode;
+ bool consumer_mode;
bool synchronous;
char name[32];
};
@@ -366,8 +366,8 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
u32 sub, ratio = hck_rate / freq;
int ret;
- /* Don't apply for fully slave mode or unchanged bclk */
- if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
+ /* Don't apply for fully consumer mode or unchanged bclk */
+ if (esai_priv->consumer_mode || esai_priv->sck_rate[tx] == freq)
return 0;
if (ratio * freq > hck_rate)
@@ -476,20 +476,20 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- esai_priv->slave_mode = false;
+ esai_priv->consumer_mode = false;
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- esai_priv->slave_mode = true;
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ esai_priv->consumer_mode = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
xccr |= ESAI_xCCR_xCKD;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
xccr |= ESAI_xCCR_xFSD;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
break;
default:
@@ -1016,8 +1016,8 @@ static int fsl_esai_probe(struct platform_device *pdev)
/* Set a default slot number */
esai_priv->slots = 2;
- /* Set a default master/slave state */
- esai_priv->slave_mode = true;
+ /* Set a default clock provider state */
+ esai_priv->consumer_mode = true;
/* Determine the FIFO depth */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index 69aeb0e71844..27b4536dce44 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -102,8 +102,8 @@ static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
index d60f4dac6c1b..8508bc7f239d 100644
--- a/sound/soc/fsl/fsl_rpmsg.c
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -138,11 +138,43 @@ static const struct snd_soc_component_driver fsl_component = {
.name = "fsl-rpmsg",
};
+static const struct fsl_rpmsg_soc_data imx7ulp_data = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mm_data = {
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U8 |
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mn_data = {
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mp_data = {
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
static const struct of_device_id fsl_rpmsg_ids[] = {
- { .compatible = "fsl,imx7ulp-rpmsg-audio"},
- { .compatible = "fsl,imx8mm-rpmsg-audio"},
- { .compatible = "fsl,imx8mn-rpmsg-audio"},
- { .compatible = "fsl,imx8mp-rpmsg-audio"},
+ { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
+ { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
+ { .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data},
+ { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
+ { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
@@ -157,6 +189,13 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
if (!rpmsg)
return -ENOMEM;
+ rpmsg->soc_data = of_device_get_match_data(&pdev->dev);
+
+ fsl_rpmsg_dai.playback.rates = rpmsg->soc_data->rates;
+ fsl_rpmsg_dai.capture.rates = rpmsg->soc_data->rates;
+ fsl_rpmsg_dai.playback.formats = rpmsg->soc_data->formats;
+ fsl_rpmsg_dai.capture.formats = rpmsg->soc_data->formats;
+
if (of_property_read_bool(np, "fsl,enable-lpa")) {
rpmsg->enable_lpa = 1;
rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h
index 4f5b49eb18d8..b04086fbf828 100644
--- a/sound/soc/fsl/fsl_rpmsg.h
+++ b/sound/soc/fsl/fsl_rpmsg.h
@@ -7,6 +7,16 @@
#define __FSL_RPMSG_H
/*
+ * struct fsl_rpmsg_soc_data
+ * @rates: supported rates
+ * @formats: supported formats
+ */
+struct fsl_rpmsg_soc_data {
+ int rates;
+ u64 formats;
+};
+
+/*
* struct fsl_rpmsg - rpmsg private data
*
* @ipg: ipg clock for cpu dai (SAI)
@@ -15,6 +25,7 @@
* @pll8k: parent clock for multiple of 8kHz frequency
* @pll11k: parent clock for multiple of 11kHz frequency
* @card_pdev: Platform_device pointer to register a sound card
+ * @soc_data: soc specific data
* @mclk_streams: Active streams that are using baudclk
* @force_lpa: force enable low power audio routine if condition satisfy
* @enable_lpa: enable low power audio routine according to dts setting
@@ -27,6 +38,7 @@ struct fsl_rpmsg {
struct clk *pll8k;
struct clk *pll11k;
struct platform_device *card_pdev;
+ const struct fsl_rpmsg_soc_data *soc_data;
unsigned int mclk_streams;
int force_lpa;
int enable_lpa;
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 38f6362099d5..10544fa27dc0 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -297,23 +297,23 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
- sai->is_slave_mode = false;
+ sai->is_consumer_mode = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- sai->is_slave_mode = true;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ sai->is_consumer_mode = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
- sai->is_slave_mode = false;
+ sai->is_consumer_mode = false;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
- sai->is_slave_mode = true;
+ sai->is_consumer_mode = true;
break;
default:
return -EINVAL;
@@ -356,8 +356,8 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
u32 id;
int ret = 0;
- /* Don't apply to slave mode */
- if (sai->is_slave_mode)
+ /* Don't apply to consumer mode */
+ if (sai->is_consumer_mode)
return 0;
/*
@@ -462,7 +462,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
pins = DIV_ROUND_UP(channels, slots);
- if (!sai->is_slave_mode) {
+ if (!sai->is_consumer_mode) {
if (sai->bclk_ratio)
ret = fsl_sai_set_bclk(cpu_dai, tx,
sai->bclk_ratio *
@@ -502,12 +502,12 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
val_cr4 |= FSL_SAI_CR4_CHMOD;
/*
- * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
+ * For SAI provider mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
* generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
* RCR5(TCR5) for playback(capture), or there will be sync error.
*/
- if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) {
+ if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
FSL_SAI_CR4_CHMOD_MASK,
@@ -543,7 +543,7 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
FSL_SAI_CR3_TRCE_MASK, 0);
- if (!sai->is_slave_mode &&
+ if (!sai->is_consumer_mode &&
sai->mclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
sai->mclk_streams &= ~BIT(substream->stream);
@@ -577,7 +577,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
* This is a hardware bug, and will be fix in the
* next sai version.
*/
- if (!sai->is_slave_mode) {
+ if (!sai->is_consumer_mode) {
/* Software Reset */
regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index bc60030967dd..9aaf231bc024 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -259,7 +259,7 @@ struct fsl_sai {
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
- bool is_slave_mode;
+ bool is_consumer_mode;
bool is_lsb_first;
bool is_dsp_mode;
bool synchronous[2];
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 1c53719bb61e..d178b479c8bd 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -111,6 +111,7 @@ struct spdif_mixer_control {
* @dma_params_tx: DMA parameters for transmit channel
* @dma_params_rx: DMA parameters for receive channel
* @regcache_srpc: regcache for SRPC
+ * @bypass: status of bypass input to output
*/
struct fsl_spdif_priv {
const struct fsl_spdif_soc_data *soc;
@@ -133,6 +134,7 @@ struct fsl_spdif_priv {
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* regcache for SRPC */
u32 regcache_srpc;
+ bool bypass;
};
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
@@ -186,6 +188,16 @@ static struct fsl_spdif_soc_data fsl_spdif_imx8mm = {
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
};
+static struct fsl_spdif_soc_data fsl_spdif_imx8ulp = {
+ .imx = true,
+ .shared_root_clock = true,
+ .raw_capture_mode = false,
+ .interrupts = 1,
+ .tx_burst = 2, /* Applied for EDMA */
+ .rx_burst = 2, /* Applied for EDMA */
+ .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */
+};
+
/* Check if clk is a root clock that does not share clock source with others */
static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk)
{
@@ -895,6 +907,69 @@ static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int fsl_spdif_bypass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ ucontrol->value.integer.value[0] = priv->bypass ? 1 : 0;
+
+ return 0;
+}
+
+static int fsl_spdif_bypass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_card *card = dai->component->card;
+ bool set = (ucontrol->value.integer.value[0] != 0);
+ struct regmap *regmap = priv->regmap;
+ struct snd_soc_pcm_runtime *rtd;
+ u32 scr, mask;
+ int stream;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link);
+
+ if (priv->bypass == set)
+ return 0; /* nothing to do */
+
+ if (snd_soc_dai_active(dai)) {
+ dev_err(dai->dev, "Cannot change BYPASS mode while stream is running.\n");
+ return -EBUSY;
+ }
+
+ pm_runtime_get_sync(dai->dev);
+
+ if (set) {
+ /* Disable interrupts */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+
+ /* Configure BYPASS mode */
+ scr = SCR_TXSEL_RX | SCR_RXFIFO_OFF;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK |
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK | SCR_TXSEL_MASK;
+ /* Power up SPDIF module */
+ mask |= SCR_LOW_POWER;
+ } else {
+ /* Power down SPDIF module, disable TX */
+ scr = SCR_LOW_POWER | SCR_TXSEL_OFF;
+ mask = SCR_LOW_POWER | SCR_TXSEL_MASK;
+ }
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Disable playback & capture if BYPASS mode is enabled, enable otherwise */
+ for_each_pcm_streams(stream)
+ rtd->pcm->streams[stream].substream_count = (set ? 0 : 1);
+
+ priv->bypass = set;
+ pm_runtime_put_sync(dai->dev);
+
+ return 0;
+}
+
/* DPLL lock information */
static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1065,6 +1140,15 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
.info = fsl_spdif_rxrate_info,
.get = fsl_spdif_rxrate_get,
},
+ /* RX bypass controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Bypass Mode",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_bypass_get,
+ .put = fsl_spdif_bypass_put,
+ },
/* User bit sync mode set/get controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1560,6 +1644,7 @@ static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, },
{ .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
{ .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
+ { .compatible = "fsl,imx8ulp-spdif", .data = &fsl_spdif_imx8ulp, },
{}
};
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index ecbc1c365d5b..1169d1104b9e 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -350,16 +350,16 @@ static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi)
SND_SOC_DAIFMT_AC97;
}
-static bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi)
+static bool fsl_ssi_is_i2s_clock_provider(struct fsl_ssi *ssi)
{
- return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
- SND_SOC_DAIFMT_CBS_CFS;
+ return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_CBC_CFC;
}
-static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi)
+static bool fsl_ssi_is_i2s_cbp_cfc(struct fsl_ssi *ssi)
{
- return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
- SND_SOC_DAIFMT_CBM_CFS;
+ return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_CBP_CFC;
}
/**
@@ -808,7 +808,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
u32 wl = SSI_SxCCR_WL(sample_size);
int ret;
- if (fsl_ssi_is_i2s_master(ssi)) {
+ if (fsl_ssi_is_i2s_clock_provider(ssi)) {
ret = fsl_ssi_set_bclk(substream, dai, hw_params);
if (ret)
return ret;
@@ -841,7 +841,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
u8 i2s_net = ssi->i2s_net;
/* Normal + Network mode to send 16-bit data in 32-bit frames */
- if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16)
+ if (fsl_ssi_is_i2s_cbp_cfc(ssi) && sample_size == 16)
i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
/* Use Normal mode to send mono data at 1st slot of 2 slots */
@@ -865,7 +865,7 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
- if (fsl_ssi_is_i2s_master(ssi) &&
+ if (fsl_ssi_is_i2s_clock_provider(ssi) &&
ssi->baudclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(ssi->baudclk);
ssi->baudclk_streams &= ~BIT(substream->stream);
@@ -891,18 +891,18 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
ssi->i2s_net = SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
if (IS_ERR(ssi->baudclk)) {
dev_err(ssi->dev,
"missing baudclk for master mode\n");
return -EINVAL;
}
fallthrough;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE;
break;
default:
@@ -962,17 +962,17 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
return -EINVAL;
}
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
/* Output bit and frame sync clocks */
strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
scr |= SSI_SCR_SYS_CLK_EN;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* Input bit or frame sync clocks */
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
/* Input bit clock but output frame sync clock */
strcr |= SSI_STCR_TFDIR;
break;
@@ -1341,7 +1341,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
}
}
- /* Do not error out for slave cases that live without a baud clock */
+ /* Do not error out for consumer cases that live without a baud clock */
ssi->baudclk = devm_clk_get(dev, "baud");
if (IS_ERR(ssi->baudclk))
dev_dbg(dev, "failed to get baud clock: %ld\n",
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
index a364e2415de0..502fe1b522ab 100644
--- a/sound/soc/fsl/imx-audmix.c
+++ b/sound/soc/fsl/imx-audmix.c
@@ -80,8 +80,8 @@ static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream,
u32 channels = params_channels(params);
int ret, dir;
- /* For playback the AUDMIX is slave, and for record is master */
- fmt |= tx ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM;
+ /* For playback the AUDMIX is consumer, and for record is provider */
+ fmt |= tx ? SND_SOC_DAIFMT_CBC_CFC : SND_SOC_DAIFMT_CBP_CFP;
dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN;
/* set DAI configuration */
@@ -121,8 +121,8 @@ static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
if (!tx)
return 0;
- /* For playback the AUDMIX is slave */
- fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ /* For playback the AUDMIX is consumer */
+ fmt |= SND_SOC_DAIFMT_CBP_CFP;
/* set AUDMIX DAI configuration */
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt);
@@ -132,12 +132,12 @@ static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops imx_audmix_fe_ops = {
+static const struct snd_soc_ops imx_audmix_fe_ops = {
.startup = imx_audmix_fe_startup,
.hw_params = imx_audmix_fe_hw_params,
};
-static struct snd_soc_ops imx_audmix_be_ops = {
+static const struct snd_soc_ops imx_audmix_be_ops = {
.hw_params = imx_audmix_be_hw_params,
};
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index 58fd0639a069..6f06afd23b16 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -442,12 +442,12 @@ static int imx_aif_startup(struct snd_pcm_substream *substream)
return ret;
}
-static struct snd_soc_ops imx_aif_ops = {
+static const struct snd_soc_ops imx_aif_ops = {
.hw_params = imx_aif_hw_params,
.startup = imx_aif_startup,
};
-static struct snd_soc_ops imx_aif_ops_be = {
+static const struct snd_soc_ops imx_aif_ops_be = {
.hw_params = imx_aif_hw_params,
};
@@ -652,7 +652,7 @@ static int imx_card_parse_of(struct imx_card_data *data)
NULL, &link->dai_fmt);
if (ret)
link->dai_fmt = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS |
+ SND_SOC_DAIFMT_CBC_CFC |
SND_SOC_DAIFMT_I2S;
/* Get tdm slot */
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
index 1981dcd7e930..09c674ee79f1 100644
--- a/sound/soc/fsl/imx-es8328.c
+++ b/sound/soc/fsl/imx-es8328.c
@@ -174,7 +174,7 @@ static int imx_es8328_probe(struct platform_device *pdev)
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_CBM_CFM;
+ SND_SOC_DAIFMT_CBP_CFP;
data->card.dev = dev;
data->card.dapm_widgets = imx_es8328_dapm_widgets;
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index 34a0dceae621..f10359a28800 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -59,7 +59,7 @@ static int imx_hdmi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops imx_hdmi_ops = {
+static const struct snd_soc_ops imx_hdmi_ops = {
.hw_params = imx_hdmi_hw_params,
};
@@ -171,7 +171,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
data->dai.codecs->name = "hdmi-audio-codec.1";
data->dai.dai_fmt = data->dai_fmt |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_CBC_CFC;
}
if (hdmi_in) {
@@ -181,7 +181,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
data->dai.codecs->name = "hdmi-audio-codec.2";
data->dai.dai_fmt = data->dai_fmt |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_CBP_CFP;
}
data->card.dapm_widgets = imx_hdmi_widgets;
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index f96fe4ff8425..2e117311e582 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -64,7 +64,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->dai.stream_name = "rpmsg hifi";
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_CBC_CFC;
/* Optional codec node */
ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args);
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index f45cb4bbb6c4..2f1acd011042 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -153,7 +153,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
data->dai.platforms->of_node = ssi_np;
data->dai.init = &imx_sgtl5000_dai_init;
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_CBP_CFP;
data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model");
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 58b9ca3c4da0..e71a992fbf93 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -264,7 +264,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
if (strcasecmp(sprop, "i2s-slave") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
@@ -282,37 +282,37 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
machine_data->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "lj-slave") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "lj-master") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "rj-slave") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "rj-master") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "ac97-slave") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "ac97-master") == 0) {
machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else {
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 317c767b0099..b45742931b0d 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -275,7 +275,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
if (strcasecmp(sprop, "i2s-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
@@ -293,37 +293,37 @@ static int p1022_ds_probe(struct platform_device *pdev)
mdata->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "lj-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "lj-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "rj-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "rj-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "ac97-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "ac97-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else {
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
index 714515b8081f..b395adabe823 100644
--- a/sound/soc/fsl/p1022_rdk.c
+++ b/sound/soc/fsl/p1022_rdk.c
@@ -265,7 +265,7 @@ static int p1022_rdk_probe(struct platform_device *pdev)
* only one way to configure the SSI.
*/
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index 4cafcf0e2bbf..b6df4e26bc4a 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -17,3 +17,23 @@ config SND_AUDIO_GRAPH_CARD
This option enables generic simple sound card support
with OF-graph DT bindings.
It also support DPCM of multi CPU single Codec ststem.
+
+config SND_AUDIO_GRAPH_CARD2
+ tristate "ASoC Audio Graph sound card2 support"
+ depends on OF
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables generic simple sound card2 support
+ with OF-graph DT bindings.
+
+config SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
+ tristate "ASoC Audio Graph Card2 base custom sample support"
+ depends on SND_AUDIO_GRAPH_CARD2
+ help
+ This option enables Audio Graph Card2 base custom sample
+
+config SND_TEST_COMPONENT
+ tristate "ASoC Test component sound support"
+ depends on OF
+ help
+ This option enables test component sound driver support.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index 21c29e5e0671..084862156506 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -2,7 +2,13 @@
snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o
snd-soc-audio-graph-card-objs := audio-graph-card.o
+snd-soc-audio-graph-card2-objs := audio-graph-card2.o
+snd-soc-audio-graph-card2-custom-sample-objs := audio-graph-card2-custom-sample.o
+snd-soc-test-component-objs := test-component.o
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
+obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o
+obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE) += snd-soc-audio-graph-card2-custom-sample.o
+obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 546f6fd0609e..7eb027238327 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -310,8 +310,10 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
* For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
* there are 'n' BE components in the path.
*/
- if (card->component_chaining && !soc_component_is_pcm(cpus))
+ if (card->component_chaining && !soc_component_is_pcm(cpus)) {
dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
+ }
asoc_simple_canonicalize_cpu(cpus, is_single_links);
asoc_simple_canonicalize_platform(platforms, cpus);
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.c b/sound/soc/generic/audio-graph-card2-custom-sample.c
new file mode 100644
index 000000000000..4a2c743e286c
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// audio-graph-card2-custom-sample.c
+//
+// Copyright (C) 2020 Renesas Electronics Corp.
+// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <sound/graph_card.h>
+
+/*
+ * Custom driver can have own priv
+ * which includes asoc_simple_priv.
+ */
+struct custom_priv {
+ struct asoc_simple_priv simple_priv;
+
+ /* custom driver's own params */
+ int custom_params;
+};
+
+/* You can get custom_priv from simple_priv */
+#define simple_to_custom(simple) container_of((simple), struct custom_priv, simple_priv)
+
+static int custom_card_probe(struct snd_soc_card *card)
+{
+ struct asoc_simple_priv *simple_priv = snd_soc_card_get_drvdata(card);
+ struct custom_priv *custom_priv = simple_to_custom(simple_priv);
+ struct device *dev = simple_priv_to_dev(simple_priv);
+
+ dev_info(dev, "custom probe\n");
+
+ custom_priv->custom_params = 1;
+
+ /* you can use generic probe function */
+ return asoc_graph_card_probe(card);
+}
+
+static int custom_hook_pre(struct asoc_simple_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /* You can custom before parsing */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return 0;
+}
+
+static int custom_hook_post(struct asoc_simple_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_card *card;
+
+ /* You can custom after parsing */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ /* overwrite .probe sample */
+ card = simple_priv_to_card(priv);
+ card->probe = custom_card_probe;
+
+ return 0;
+}
+
+static int custom_normal(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom Normal parsing
+ * before/affter audio_graph2_link_normal()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_normal(priv, lnk, li);
+}
+
+static int custom_dpcm(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom DPCM parsing
+ * before/affter audio_graph2_link_dpcm()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_dpcm(priv, lnk, li);
+}
+
+static int custom_c2c(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom Codec2Codec parsing
+ * before/affter audio_graph2_link_c2c()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_c2c(priv, lnk, li);
+}
+
+/*
+ * audio-graph-card2 has many hooks for your customizing.
+ */
+static struct graph2_custom_hooks custom_hooks = {
+ .hook_pre = custom_hook_pre,
+ .hook_post = custom_hook_post,
+ .custom_normal = custom_normal,
+ .custom_dpcm = custom_dpcm,
+ .custom_c2c = custom_c2c,
+};
+
+static int custom_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct device *dev = simple_priv_to_dev(priv);
+
+ dev_info(dev, "custom startup\n");
+
+ return asoc_simple_startup(substream);
+}
+
+/* You can use custom ops */
+static const struct snd_soc_ops custom_ops = {
+ .startup = custom_startup,
+ .shutdown = asoc_simple_shutdown,
+ .hw_params = asoc_simple_hw_params,
+};
+
+static int custom_probe(struct platform_device *pdev)
+{
+ struct custom_priv *custom_priv;
+ struct asoc_simple_priv *simple_priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ custom_priv = devm_kzalloc(dev, sizeof(*custom_priv), GFP_KERNEL);
+ if (!custom_priv)
+ return -ENOMEM;
+
+ simple_priv = &custom_priv->simple_priv;
+ simple_priv->ops = &custom_ops; /* customize dai_link ops */
+
+ /* use audio-graph-card2 parsing with own custom hooks */
+ ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks);
+ if (ret < 0)
+ return ret;
+
+ /* customize more if needed */
+
+ return 0;
+}
+
+static const struct of_device_id custom_of_match[] = {
+ { .compatible = "audio-graph-card2-custom-sample", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, custom_of_match);
+
+static struct platform_driver custom_card = {
+ .driver = {
+ .name = "audio-graph-card2-custom-sample",
+ .of_match_table = custom_of_match,
+ },
+ .probe = custom_probe,
+ .remove = asoc_simple_remove,
+};
+module_platform_driver(custom_card);
+
+MODULE_ALIAS("platform:asoc-audio-graph-card2-custom-sample");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Card2 Custom Sample");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
new file mode 100644
index 000000000000..8eee7b821ff7
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * audio-graph-card2-custom-sample.dtsi
+ *
+ * Copyright (C) 2020 Renesas Electronics Corp.
+ * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This sample indicates how to use audio-graph-card2 and its
+ * custom driver. "audio-graph-card2-custom-sample" is the custome driver
+ * which is using audio-graph-card2.
+ *
+ * You can easily use this sample by adding below line on your DT file,
+ * and add new CONFIG to your .config.
+ *
+ * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi"
+ *
+ * CONFIG_SND_AUDIO_GRAPH_CARD2
+ * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
+ * CONFIG_SND_TEST_COMPONENT
+ */
+/ {
+ /*
+ * @ : used at links
+ *
+ * [Normal]
+ * cpu0 <-@-----------------> codec0
+ *
+ * [Multi-CPU/Codec]
+ * +-+ +-+
+ * cpu1 <--| |<-@--------->| |-> codec1
+ * cpu2 <--| | | |-> codec2
+ * +-+ +-+
+ *
+ * [DPCM]
+ * FE BE
+ * ****
+ * cpu3 <-@--* *--@-> codec3
+ * cpu4 <-@--* *
+ * ****
+ *
+ * [DPCM-Multi]
+ *
+ * --NOTE--
+ * Multi-FE is not supported by ASoC.
+ *
+ * FE BE
+ * **** +-+
+ * cpu5 <-@--* *--@-> | | -> codec4
+ * cpu6 <-@--* * | | -> codec5
+ * **** +-+
+ *
+ * [Codec2Codec]
+ * +-@-> codec6
+ * |
+ * +---> codec7
+ *
+ * [Codec2Codec-Multi]
+ *
+ * --NOTE--
+ * Multi connect N:M is not supported by ASoC.
+ *
+ * +-+
+ * +-@->| |-> codec8
+ * | | |-> codec9
+ * | +-+
+ * | +-+
+ * +--->| |-> codec10
+ * | |-> codec11
+ * +-+
+ */
+ audio-graph-card2-custom-sample {
+ /*
+ * You can use audio-graph-card2 directly by using
+ *
+ * compatible = "audio-graph-card2";
+ */
+ compatible = "audio-graph-card2-custom-sample";
+
+ /* for [DPCM] */
+ /* BE FE */
+ routing = "TC DAI3 Playback", "DAI3 Playback",
+ "TC DAI3 Playback", "DAI4 Playback",
+ "DAI3 Capture", "TC DAI3 Capture",
+ "DAI4 Capture", "TC DAI3 Capture",
+ /* for [DPCM-Multi] */
+ /* BE FE */
+ "TC DAI4 Playback", "DAI5 Playback",
+ "TC DAI5 Playback", "DAI5 Playback",
+ "TC DAI4 Playback", "DAI6 Playback",
+ "TC DAI5 Playback", "DAI6 Playback",
+ "DAI5 Capture", "TC DAI4 Capture",
+ "DAI5 Capture", "TC DAI5 Capture",
+ "DAI6 Capture", "TC DAI4 Capture",
+ "DAI6 Capture", "TC DAI5 Capture",
+ /* for [Codec2Codec] */
+ "TC OUT", "TC DAI7 Playback",
+ "TC DAI6 Capture", "TC IN",
+ /* for [Codec2Codec-Multi] */
+ "TC OUT", "TC DAI10 Playback",
+ "TC DAI8 Capture", "TC IN",
+ "TC OUT", "TC DAI11 Playback",
+ "TC DAI9 Capture", "TC IN";
+
+ links = <&cpu0 /* normal: cpu side only */
+ &mcpu0 /* multi: cpu side only */
+ &fe00 &fe01 &be0 /* dpcm: both FE / BE */
+ &fe10 &fe11 &be1 /* dpcm-m: both FE / BE */
+ &c2c /* c2c: cpu side only */
+ &c2c_m /* c2c: cpu side only */
+ >;
+
+ multi {
+ ports@0 {
+ mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
+ port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
+ port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
+ };
+ ports@1 {
+ port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
+ port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+ port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
+ };
+ ports@2 {
+ port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
+ port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
+ port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
+ };
+ ports@3 {
+ port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
+ port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
+ port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
+ };
+ ports@4 {
+ port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
+ port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
+ port@2 { mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; };
+ };
+ };
+
+ dpcm {
+ /* FE */
+ ports@0 {
+ fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
+ fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
+ fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
+ fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
+ };
+ /* BE */
+ ports@1 {
+ be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
+ be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
+ };
+ };
+
+ codec2codec {
+ ports@0 {
+ rate = <48000>;
+ c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; };
+ port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
+ };
+ ports@1 {
+ rate = <48000>;
+ c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; };
+ port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; };
+ };
+ };
+ };
+
+ test_cpu {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ */
+ compatible = "test-cpu";
+ ports {
+ bitclock-master;
+ frame-master;
+ cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+ port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
+ port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+ port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
+ port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; };
+ port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; };
+ port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; };
+ };
+ };
+
+ test_codec {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ */
+ compatible = "test-codec";
+ ports {
+ /*
+ * prefix can be added to *component*,
+ * see audio-graph-card2::routing
+ */
+ prefix = "TC";
+
+ port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
+ port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
+ port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+ port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; };
+ port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; };
+ port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; };
+ port@6 { bitclock-master;
+ frame-master;
+ codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
+ port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+ port@8 { bitclock-master;
+ frame-master;
+ codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; };
+ port@9 { codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; };
+ port@10 { codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; };
+ port@11 { codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; };
+ };
+ };
+};
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
new file mode 100644
index 000000000000..b6049bcfb771
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -0,0 +1,1281 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC Audio Graph Card2 support
+//
+// Copyright (C) 2020 Renesas Electronics Corp.
+// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// based on ${LINUX}/sound/soc/generic/audio-graph-card.c
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/graph_card.h>
+
+/************************************
+ daifmt
+ ************************************
+ ports {
+ format = "left_j";
+ port@0 {
+ bitclock-master;
+ sample0: endpoint@0 {
+ frame-master;
+ };
+ sample1: endpoint@1 {
+ format = "i2s";
+ };
+ };
+ ...
+ };
+
+ You can set daifmt at ports/port/endpoint.
+ It uses *latest* format, and *share* master settings.
+ In above case,
+ sample0: left_j, bitclock-master, frame-master
+ sample1: i2s, bitclock-master
+
+ If there was no settings, *Codec* will be
+ bitclock/frame provider as default.
+ see
+ graph_parse_daifmt().
+
+ ************************************
+ Normal Audio-Graph
+ ************************************
+
+ CPU <---> Codec
+
+ sound {
+ compatible = "audio-graph-card2";
+ links = <&cpu>;
+ };
+
+ CPU {
+ cpu: port {
+ bitclock-master;
+ frame-master;
+ cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; };
+ };
+
+ Codec {
+ port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
+ };
+
+ ************************************
+ Multi-CPU/Codec
+ ************************************
+
+It has connection part (= X) and list part (= y).
+links indicates connection part of CPU side (= A).
+
+ +-+ (A) +-+
+ CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1
+ CPU2 --(y) | | | | (y)-- Codec2
+ +-+ +-+
+
+ sound {
+ compatible = "audio-graph-card2";
+
+(A) links = <&mcpu>;
+
+ multi {
+ ports@0 {
+(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
+(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
+(y) port@1 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
+ };
+ ports@1 {
+(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
+(y) port@0 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+(y) port@1 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
+ };
+ };
+ };
+
+ CPU {
+ ports {
+ bitclock-master;
+ frame-master;
+ port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
+ port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
+ port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+ };
+ };
+
+ ************************************
+ DPCM
+ ************************************
+
+ DSP
+ ************
+ PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset
+ PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers
+ PCM2 <--> * fe2 be2 * <--> DAI2: MODEM
+ PCM3 <--> * fe3 be3 * <--> DAI3: BT
+ * be4 * <--> DAI4: DMIC
+ * be5 * <--> DAI5: FM
+ ************
+
+ sound {
+ compatible = "audio-graph-card2";
+
+ // indicate routing
+ routing = "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback";
+
+ // indicate all Front-End, Back-End
+ links = <&fe0, &fe1, ...,
+ &be0, &be1, ...>;
+
+ dpcm {
+ // Front-End
+ ports@0 {
+ fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };
+ fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };
+ ...
+ };
+ // Back-End
+ ports@1 {
+ be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };
+ be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };
+ ...
+ };
+ };
+ };
+
+ CPU {
+ ports {
+ bitclock-master;
+ frame-master;
+ port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; };
+ port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; };
+ ...
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; };
+ port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; };
+ ...
+ };
+ };
+
+ ************************************
+ Codec to Codec
+ ************************************
+
+ +--+
+ | |<-- Codec0 <- IN
+ | |--> Codec1 -> OUT
+ +--+
+
+ sound {
+ compatible = "audio-graph-card2";
+
+ routing = "OUT" ,"DAI1 Playback",
+ "DAI0 Capture", "IN";
+
+ links = <&c2c>;
+
+ codec2codec {
+ ports {
+ rate = <48000>;
+ c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+ port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 {
+ bitclock-master;
+ frame-master;
+ codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
+ port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+ };
+ };
+
+*/
+
+enum graph_type {
+ GRAPH_NORMAL,
+ GRAPH_DPCM,
+ GRAPH_C2C,
+
+ GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */
+};
+
+#define GRAPH_NODENAME_MULTI "multi"
+#define GRAPH_NODENAME_DPCM "dpcm"
+#define GRAPH_NODENAME_C2C "codec2codec"
+
+#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
+
+static enum graph_type __graph_get_type(struct device_node *lnk)
+{
+ struct device_node *np;
+
+ /*
+ * target {
+ * ports {
+ * => lnk: port@0 { ... };
+ * port@1 { ... };
+ * };
+ * };
+ */
+ np = of_get_parent(lnk);
+ if (of_node_name_eq(np, "ports"))
+ np = of_get_parent(np);
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_MULTI))
+ return GRAPH_MULTI;
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_DPCM))
+ return GRAPH_DPCM;
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_C2C))
+ return GRAPH_C2C;
+
+ return GRAPH_NORMAL;
+}
+
+static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
+ struct device_node *lnk)
+{
+ enum graph_type type = __graph_get_type(lnk);
+
+ /* GRAPH_MULTI here means GRAPH_NORMAL */
+ if (type == GRAPH_MULTI)
+ type = GRAPH_NORMAL;
+
+#ifdef DEBUG
+ {
+ struct device *dev = simple_priv_to_dev(priv);
+ const char *str = "Normal";
+
+ switch (type) {
+ case GRAPH_DPCM:
+ if (asoc_graph_is_ports0(lnk))
+ str = "DPCM Front-End";
+ else
+ str = "DPCM Back-End";
+ break;
+ case GRAPH_C2C:
+ str = "Codec2Codec";
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%pOF (%s)", lnk, str);
+ }
+#endif
+ return type;
+}
+
+static int graph_lnk_is_multi(struct device_node *lnk)
+{
+ return __graph_get_type(lnk) == GRAPH_MULTI;
+}
+
+static struct device_node *graph_get_next_multi_ep(struct device_node **port)
+{
+ struct device_node *ports = of_get_parent(*port);
+ struct device_node *ep = NULL;
+ struct device_node *rep = NULL;
+
+ /*
+ * multi {
+ * ports {
+ * => lnk: port@0 { ... };
+ * port@1 { ep { ... = rep0 } };
+ * port@2 { ep { ... = rep1 } };
+ * ...
+ * };
+ * };
+ *
+ * xxx {
+ * port@0 { rep0 };
+ * port@1 { rep1 };
+ * };
+ */
+ do {
+ *port = of_get_next_child(ports, *port);
+ if (!*port)
+ break;
+ } while (!of_node_name_eq(*port, "port"));
+
+ if (*port) {
+ ep = port_to_endpoint(*port);
+ rep = of_graph_get_remote_endpoint(ep);
+ }
+
+ of_node_put(ep);
+ of_node_put(ports);
+
+ return rep;
+}
+
+static const struct snd_soc_ops graph_ops = {
+ .startup = asoc_simple_startup,
+ .shutdown = asoc_simple_shutdown,
+ .hw_params = asoc_simple_hw_params,
+};
+
+static int graph_get_dai_id(struct device_node *ep)
+{
+ struct device_node *node;
+ struct device_node *endpoint;
+ struct of_endpoint info;
+ int i, id;
+ const u32 *reg;
+ int ret;
+
+ /* use driver specified DAI ID if exist */
+ ret = snd_soc_get_dai_id(ep);
+ if (ret != -ENOTSUPP)
+ return ret;
+
+ /* use endpoint/port reg if exist */
+ ret = of_graph_parse_endpoint(ep, &info);
+ if (ret == 0) {
+ /*
+ * Because it will count port/endpoint if it doesn't have "reg".
+ * But, we can't judge whether it has "no reg", or "reg = <0>"
+ * only of_graph_parse_endpoint().
+ * We need to check "reg" property
+ */
+ if (of_get_property(ep, "reg", NULL))
+ return info.id;
+
+ node = of_get_parent(ep);
+ reg = of_get_property(node, "reg", NULL);
+ of_node_put(node);
+ if (reg)
+ return info.port;
+ }
+ node = of_graph_get_port_parent(ep);
+
+ /*
+ * Non HDMI sound case, counting port/endpoint on its DT
+ * is enough. Let's count it.
+ */
+ i = 0;
+ id = -1;
+ for_each_endpoint_of_node(node, endpoint) {
+ if (endpoint == ep)
+ id = i;
+ i++;
+ }
+
+ of_node_put(node);
+
+ if (id < 0)
+ return -ENODEV;
+
+ return id;
+}
+
+static int asoc_simple_parse_dai(struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc,
+ int *is_single_link)
+{
+ struct device_node *node;
+ struct of_phandle_args args;
+ int ret;
+
+ if (!ep)
+ return 0;
+
+ node = of_graph_get_port_parent(ep);
+
+ /* Get dai->name */
+ args.np = node;
+ args.args[0] = graph_get_dai_id(ep);
+ args.args_count = (of_graph_get_endpoint_count(node) > 1);
+
+ /*
+ * FIXME
+ *
+ * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
+ * If user unbinded CPU or Codec driver, but not for Sound Card,
+ * dlc->dai_name is keeping unbinded CPU or Codec
+ * driver's pointer.
+ *
+ * If user re-bind CPU or Codec driver again, ALSA SoC will try
+ * to rebind Card via snd_soc_try_rebind_card(), but because of
+ * above reason, it might can't bind Sound Card.
+ * Because Sound Card is pointing to released dai_name pointer.
+ *
+ * To avoid this rebind Card issue,
+ * 1) It needs to alloc memory to keep dai_name eventhough
+ * CPU or Codec driver was unbinded, or
+ * 2) user need to rebind Sound Card everytime
+ * if he unbinded CPU or Codec.
+ */
+ ret = snd_soc_get_dai_name(&args, &dlc->dai_name);
+ if (ret < 0)
+ return ret;
+
+ dlc->of_node = node;
+
+ if (is_single_link)
+ *is_single_link = of_graph_get_endpoint_count(node) == 1;
+
+ return 0;
+}
+
+static void graph_parse_convert(struct device_node *ep,
+ struct simple_dai_props *props)
+{
+ struct device_node *port = of_get_parent(ep);
+ struct device_node *ports = of_get_parent(port);
+ struct asoc_simple_data *adata = &props->adata;
+
+ if (of_node_name_eq(ports, "ports"))
+ asoc_simple_parse_convert(ports, NULL, adata);
+ asoc_simple_parse_convert(port, NULL, adata);
+ asoc_simple_parse_convert(ep, NULL, adata);
+
+ of_node_put(port);
+ of_node_put(ports);
+}
+
+static void graph_parse_mclk_fs(struct device_node *ep,
+ struct simple_dai_props *props)
+{
+ struct device_node *port = of_get_parent(ep);
+ struct device_node *ports = of_get_parent(port);
+
+ if (of_node_name_eq(ports, "ports"))
+ of_property_read_u32(ports, "mclk-fs", &props->mclk_fs);
+ of_property_read_u32(port, "mclk-fs", &props->mclk_fs);
+ of_property_read_u32(ep, "mclk-fs", &props->mclk_fs);
+
+ of_node_put(port);
+ of_node_put(ports);
+}
+
+static int __graph_parse_node(struct asoc_simple_priv *priv,
+ enum graph_type gtype,
+ struct device_node *ep,
+ struct link_info *li,
+ int is_cpu, int idx)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_dai_link_component *dlc;
+ struct asoc_simple_dai *dai;
+ int ret, is_single_links = 0;
+
+ if (is_cpu) {
+ dlc = asoc_link_to_cpu(dai_link, idx);
+ dai = simple_props_to_dai_cpu(dai_props, idx);
+ } else {
+ dlc = asoc_link_to_codec(dai_link, idx);
+ dai = simple_props_to_dai_codec(dai_props, idx);
+ }
+
+ graph_parse_mclk_fs(ep, dai_props);
+
+ ret = asoc_simple_parse_dai(ep, dlc, &is_single_links);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_parse_tdm(ep, dai);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_parse_clk(dev, ep, dai, dlc);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * set DAI Name
+ */
+ if (!dai_link->name) {
+ struct snd_soc_dai_link_component *cpus = dlc;
+ struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx);
+ char *cpu_multi = "";
+ char *codec_multi = "";
+
+ if (dai_link->num_cpus > 1)
+ cpu_multi = "_multi";
+ if (dai_link->num_codecs > 1)
+ codec_multi = "_multi";
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ /* run is_cpu only. see audio_graph2_link_normal() */
+ if (is_cpu)
+ asoc_simple_set_dailink_name(dev, dai_link, "%s%s-%s%s",
+ cpus->dai_name, cpu_multi,
+ codecs->dai_name, codec_multi);
+ break;
+ case GRAPH_DPCM:
+ if (is_cpu)
+ asoc_simple_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s",
+ cpus->of_node, cpus->dai_name, cpu_multi);
+ else
+ asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s",
+ codecs->of_node, codecs->dai_name, codec_multi);
+ break;
+ case GRAPH_C2C:
+ /* run is_cpu only. see audio_graph2_link_c2c() */
+ if (is_cpu)
+ asoc_simple_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s",
+ cpus->dai_name, cpu_multi,
+ codecs->dai_name, codec_multi);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Check "prefix" from top node
+ * if DPCM-BE case
+ */
+ if (!is_cpu && gtype == GRAPH_DPCM) {
+ struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx);
+ struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx);
+ struct device_node *rport = of_get_parent(ep);
+ struct device_node *rports = of_get_parent(rport);
+
+ if (of_node_name_eq(rports, "ports"))
+ snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix");
+ snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix");
+
+ of_node_put(rport);
+ of_node_put(rports);
+ }
+
+ if (is_cpu) {
+ struct snd_soc_dai_link_component *cpus = dlc;
+ struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx);
+
+ asoc_simple_canonicalize_cpu(cpus, is_single_links);
+ asoc_simple_canonicalize_platform(platforms, cpus);
+ }
+
+ return 0;
+}
+
+static int graph_parse_node(struct asoc_simple_priv *priv,
+ enum graph_type gtype,
+ struct device_node *port,
+ struct link_info *li, int is_cpu)
+{
+ struct device_node *ep;
+ int ret = 0;
+
+ if (graph_lnk_is_multi(port)) {
+ int idx;
+
+ of_node_get(port);
+
+ for (idx = 0;; idx++) {
+ ep = graph_get_next_multi_ep(&port);
+ if (!ep)
+ break;
+
+ ret = __graph_parse_node(priv, gtype, ep,
+ li, is_cpu, idx);
+ of_node_put(ep);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ /* Single CPU / Codec */
+ ep = port_to_endpoint(port);
+ ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
+ of_node_put(ep);
+ }
+
+ return ret;
+}
+
+static void graph_parse_daifmt(struct device_node *node,
+ unsigned int *daifmt, unsigned int *bit_frame)
+{
+ unsigned int fmt;
+
+ /*
+ * see also above "daifmt" explanation
+ * and samples.
+ */
+
+ /*
+ * ports {
+ * (A)
+ * port {
+ * (B)
+ * endpoint {
+ * (C)
+ * };
+ * };
+ * };
+ * };
+ */
+
+ /*
+ * clock_provider:
+ *
+ * It can be judged it is provider
+ * if (A) or (B) or (C) has bitclock-master / frame-master flag.
+ *
+ * use "or"
+ */
+ *bit_frame |= snd_soc_daifmt_parse_clock_provider_as_bitmap(node, NULL);
+
+#define update_daifmt(name) \
+ if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \
+ (fmt & SND_SOC_DAIFMT_##name##_MASK)) \
+ *daifmt |= fmt & SND_SOC_DAIFMT_##name##_MASK
+
+ /*
+ * format
+ *
+ * This function is called by (C) -> (B) -> (A) order.
+ * Set if applicable part was not yet set.
+ */
+ fmt = snd_soc_daifmt_parse_format(node, NULL);
+ update_daifmt(FORMAT);
+ update_daifmt(CLOCK);
+ update_daifmt(INV);
+}
+
+static void graph_link_init(struct asoc_simple_priv *priv,
+ struct device_node *port,
+ struct link_info *li,
+ int is_cpu_node)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct device_node *ep;
+ struct device_node *ports;
+ unsigned int daifmt = 0, daiclk = 0;
+ unsigned int bit_frame = 0;
+
+ if (graph_lnk_is_multi(port)) {
+ of_node_get(port);
+ ep = graph_get_next_multi_ep(&port);
+ port = of_get_parent(ep);
+ } else {
+ ep = port_to_endpoint(port);
+ }
+
+ ports = of_get_parent(port);
+
+ /*
+ * ports {
+ * (A)
+ * port {
+ * (B)
+ * endpoint {
+ * (C)
+ * };
+ * };
+ * };
+ * };
+ */
+ graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */
+ graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */
+ if (of_node_name_eq(ports, "ports"))
+ graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */
+
+ /*
+ * convert bit_frame
+ * We need to flip clock_provider if it was CPU node,
+ * because it is Codec base.
+ */
+ daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame);
+ if (is_cpu_node)
+ daiclk = snd_soc_daifmt_clock_provider_fliped(daiclk);
+
+ dai_link->dai_fmt = daifmt | daiclk;
+ dai_link->init = asoc_simple_dai_init;
+ dai_link->ops = &graph_ops;
+ if (priv->ops)
+ dai_link->ops = priv->ops;
+}
+
+int audio_graph2_link_normal(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *cpu_port = lnk;
+ struct device_node *cpu_ep = port_to_endpoint(cpu_port);
+ struct device_node *codec_port = of_graph_get_remote_port(cpu_ep);
+ int ret;
+
+ /*
+ * call Codec first.
+ * see
+ * __graph_parse_node() :: DAI Naming
+ */
+ ret = graph_parse_node(priv, GRAPH_NORMAL, codec_port, li, 0);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * call CPU, and set DAI Name
+ */
+ ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_port, li, 1);
+ if (ret < 0)
+ goto err;
+
+ graph_link_init(priv, cpu_port, li, 1);
+err:
+ of_node_put(codec_port);
+ of_node_put(cpu_ep);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_normal);
+
+int audio_graph2_link_dpcm(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ep = port_to_endpoint(lnk);
+ struct device_node *rep = of_graph_get_remote_endpoint(ep);
+ struct device_node *rport = of_graph_get_remote_port(ep);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ int is_cpu = asoc_graph_is_ports0(lnk);
+ int ret;
+
+ if (is_cpu) {
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { ep: { ... = rep }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * ...
+ * };
+ * };
+ *
+ * CPU {
+ * rports: ports {
+ * rport: port@0 { rep: { ... = ep } };
+ * }
+ * }
+ */
+ /*
+ * setup CPU here, Codec is already set as dummy.
+ * see
+ * asoc_simple_init_priv()
+ */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
+
+ ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1);
+ if (ret)
+ goto err;
+ } else {
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * => lnk: port@0 { ep: { ... = rep; }; };
+ * ...
+ * };
+ * };
+ *
+ * Codec {
+ * rports: ports {
+ * rport: port@0 { rep: { ... = ep; }; };
+ * }
+ * }
+ */
+ /*
+ * setup Codec here, CPU is already set as dummy.
+ * see
+ * asoc_simple_init_priv()
+ */
+
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
+
+ ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ graph_parse_convert(rep, dai_props);
+
+ snd_soc_dai_link_set_capabilities(dai_link);
+
+ graph_link_init(priv, rport, li, is_cpu);
+err:
+ of_node_put(ep);
+ of_node_put(rep);
+ of_node_put(rport);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
+
+int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_pcm_stream *c2c_conf = dai_props->c2c_conf;
+ struct device_node *port0, *port1, *ports;
+ struct device_node *codec0_port, *codec1_port;
+ struct device_node *ep0, *ep1;
+ u32 val;
+ int ret = -EINVAL;
+
+ /*
+ * codec2codec {
+ * ports {
+ * rate = <48000>;
+ * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; };
+ * port@1 { c2c1_ep: { ... = codec1_ep; }; };
+ * };
+ * };
+ *
+ * Codec {
+ * ports {
+ * port@0 { codec0_ep: ... }; };
+ * port@1 { codec1_ep: ... }; };
+ * };
+ * };
+ */
+ of_node_get(lnk);
+ port0 = lnk;
+ ports = of_get_parent(port0);
+ port1 = of_get_next_child(ports, lnk);
+
+ if (!of_get_property(ports, "rate", &val)) {
+ struct device *dev = simple_priv_to_dev(priv);
+
+ dev_err(dev, "Codec2Codec needs rate settings\n");
+ goto err1;
+ }
+
+ c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
+ c2c_conf->rate_min =
+ c2c_conf->rate_max = val;
+ c2c_conf->channels_min =
+ c2c_conf->channels_max = 2; /* update ME */
+ dai_link->params = c2c_conf;
+
+ ep0 = port_to_endpoint(port0);
+ ep1 = port_to_endpoint(port1);
+
+ codec0_port = of_graph_get_remote_port(ep0);
+ codec1_port = of_graph_get_remote_port(ep1);
+
+ /*
+ * call Codec first.
+ * see
+ * __graph_parse_node() :: DAI Naming
+ */
+ ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0);
+ if (ret < 0)
+ goto err2;
+
+ /*
+ * call CPU, and set DAI Name
+ */
+ ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1);
+ if (ret < 0)
+ goto err2;
+
+ graph_link_init(priv, codec0_port, li, 1);
+err2:
+ of_node_put(ep0);
+ of_node_put(ep1);
+ of_node_put(codec0_port);
+ of_node_put(codec1_port);
+err1:
+ of_node_put(ports);
+ of_node_put(port0);
+ of_node_put(port1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);
+
+static int graph_link(struct asoc_simple_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ GRAPH2_CUSTOM func = NULL;
+ int ret = -EINVAL;
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ if (hooks && hooks->custom_normal)
+ func = hooks->custom_normal;
+ else
+ func = audio_graph2_link_normal;
+ break;
+ case GRAPH_DPCM:
+ if (hooks && hooks->custom_dpcm)
+ func = hooks->custom_dpcm;
+ else
+ func = audio_graph2_link_dpcm;
+ break;
+ case GRAPH_C2C:
+ if (hooks && hooks->custom_c2c)
+ func = hooks->custom_c2c;
+ else
+ func = audio_graph2_link_c2c;
+ break;
+ default:
+ break;
+ }
+
+ if (!func) {
+ dev_err(dev, "non supported gtype (%d)\n", gtype);
+ goto err;
+ }
+
+ ret = func(priv, lnk, li);
+ if (ret < 0)
+ goto err;
+
+ li->link++;
+err:
+ return ret;
+}
+
+static int graph_counter(struct device_node *lnk)
+{
+ /*
+ * Multi CPU / Codec
+ *
+ * multi {
+ * ports {
+ * => lnk: port@0 { ... };
+ * port@1 { ... };
+ * port@2 { ... };
+ * ...
+ * };
+ * };
+ *
+ * ignore first lnk part
+ */
+ if (graph_lnk_is_multi(lnk))
+ return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1;
+ /*
+ * Single CPU / Codec
+ */
+ else
+ return 1;
+}
+
+static int graph_count_normal(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *cpu_port = lnk;
+ struct device_node *cpu_ep = port_to_endpoint(cpu_port);
+ struct device_node *codec_port = of_graph_get_remote_port(cpu_ep);
+
+ /*
+ * CPU {
+ * => lnk: port { endpoint { .. }; };
+ * };
+ */
+ 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);
+ of_node_put(codec_port);
+
+ return 0;
+}
+
+static int graph_count_dpcm(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ep = port_to_endpoint(lnk);
+ struct device_node *rport = of_graph_get_remote_port(ep);
+
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@1 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * };
+ */
+
+ if (asoc_graph_is_ports0(lnk)) {
+ li->num[li->link].cpus = graph_counter(rport); /* FE */
+ li->num[li->link].platforms = graph_counter(rport);
+ } else {
+ li->num[li->link].codecs = graph_counter(rport); /* BE */
+ }
+
+ of_node_put(ep);
+ of_node_put(rport);
+
+ return 0;
+}
+
+static int graph_count_c2c(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ports = of_get_parent(lnk);
+ struct device_node *port0 = lnk;
+ struct device_node *port1 = of_get_next_child(ports, lnk);
+ struct device_node *ep0 = port_to_endpoint(port0);
+ struct device_node *ep1 = port_to_endpoint(port1);
+ struct device_node *codec0 = of_graph_get_remote_port(ep0);
+ struct device_node *codec1 = of_graph_get_remote_port(ep1);
+
+ of_node_get(lnk);
+
+ /*
+ * codec2codec {
+ * ports {
+ * => lnk: port@0 { endpoint { ... }; };
+ * port@1 { endpoint { ... }; };
+ * };
+ * };
+ */
+ li->num[li->link].cpus =
+ li->num[li->link].platforms = graph_counter(codec0);
+ li->num[li->link].codecs = graph_counter(codec1);
+ li->num[li->link].c2c = 1;
+
+ of_node_put(ports);
+ of_node_put(port1);
+ of_node_put(ep0);
+ of_node_put(ep1);
+ of_node_put(codec0);
+ of_node_put(codec1);
+
+ return 0;
+}
+
+static int graph_count(struct asoc_simple_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ GRAPH2_CUSTOM func = NULL;
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS) {
+ dev_err(dev, "too many links\n");
+ return ret;
+ }
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ func = graph_count_normal;
+ break;
+ case GRAPH_DPCM:
+ func = graph_count_dpcm;
+ break;
+ case GRAPH_C2C:
+ func = graph_count_c2c;
+ break;
+ default:
+ break;
+ }
+
+ if (!func) {
+ dev_err(dev, "non supported gtype (%d)\n", gtype);
+ goto err;
+ }
+
+ ret = func(priv, lnk, li);
+ if (ret < 0)
+ goto err;
+
+ li->link++;
+err:
+ return ret;
+}
+
+static int graph_for_each_link(struct asoc_simple_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ struct link_info *li,
+ int (*func)(struct asoc_simple_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li))
+{
+ struct of_phandle_iterator it;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *node = dev->of_node;
+ struct device_node *lnk;
+ enum graph_type gtype;
+ int rc, ret;
+
+ /* loop for all listed CPU port */
+ of_for_each_phandle(&it, rc, node, "links", NULL, 0) {
+ lnk = it.node;
+
+ gtype = graph_get_type(priv, lnk);
+
+ ret = func(priv, hooks, gtype, lnk, li);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
+ struct graph2_custom_hooks *hooks)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct link_info *li;
+ int ret;
+
+ dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n");
+
+ li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
+ if (!li)
+ return -ENOMEM;
+
+ card->probe = asoc_graph_card_probe;
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+
+ if ((hooks) && (hooks)->hook_pre) {
+ ret = (hooks)->hook_pre(priv);
+ if (ret < 0)
+ goto err;
+ }
+
+ ret = graph_for_each_link(priv, hooks, li, graph_count);
+ if (!li->link)
+ ret = -EINVAL;
+ if (ret < 0)
+ goto err;
+
+ ret = asoc_simple_init_priv(priv, li);
+ if (ret < 0)
+ goto err;
+
+ priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pa_gpio)) {
+ ret = PTR_ERR(priv->pa_gpio);
+ dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
+ goto err;
+ }
+
+ ret = asoc_simple_parse_widgets(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ ret = asoc_simple_parse_routing(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ memset(li, 0, sizeof(*li));
+ ret = graph_for_each_link(priv, hooks, li, graph_link);
+ if (ret < 0)
+ goto err;
+
+ ret = asoc_simple_parse_card_name(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ if ((hooks) && (hooks)->hook_post) {
+ ret = (hooks)->hook_post(priv);
+ if (ret < 0)
+ goto err;
+ }
+
+ asoc_simple_debug_info(priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+err:
+ devm_kfree(dev, li);
+
+ if ((ret < 0) && (ret != -EPROBE_DEFER))
+ dev_err(dev, "parse error %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_parse_of);
+
+static int graph_probe(struct platform_device *pdev)
+{
+ struct asoc_simple_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ /* Allocate the private data and the DAI link array */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ return audio_graph2_parse_of(priv, dev, NULL);
+}
+
+static const struct of_device_id graph_of_match[] = {
+ { .compatible = "audio-graph-card2", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, graph_of_match);
+
+static struct platform_driver graph_card = {
+ .driver = {
+ .name = "asoc-audio-graph-card2",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = graph_of_match,
+ },
+ .probe = graph_probe,
+ .remove = asoc_simple_remove,
+};
+module_platform_driver(graph_card);
+
+MODULE_ALIAS("platform:asoc-audio-graph-card2");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Card2");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 10c63b73900c..850e968677f1 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -355,9 +355,9 @@ static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hardware hw;
int i, ret, stream;
- /* Only codecs should have non_legacy_dai_naming set. */
+ /* Only Codecs */
for_each_rtd_components(rtd, i, component) {
- if (!component->driver->non_legacy_dai_naming)
+ if (!snd_soc_component_is_codec(component))
return 0;
}
@@ -619,7 +619,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
struct asoc_simple_dai *dais;
struct snd_soc_dai_link_component *dlcs;
struct snd_soc_codec_conf *cconf = NULL;
- int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
+ struct snd_soc_pcm_stream *c2c_conf = NULL;
+ int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0;
dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
@@ -638,6 +639,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
if (!li->num[i].cpus)
cnf_num += li->num[i].codecs;
+
+ c2c_num += li->num[i].c2c;
}
dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
@@ -651,6 +654,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
return -ENOMEM;
}
+ if (c2c_num) {
+ c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL);
+ if (!c2c_conf)
+ return -ENOMEM;
+ }
+
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
li->link, dai_num, cnf_num);
@@ -664,6 +673,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
priv->dais = dais;
priv->dlcs = dlcs;
priv->codec_conf = cconf;
+ priv->c2c_conf = c2c_conf;
card->dai_link = priv->dai_link;
card->num_links = li->link;
@@ -681,6 +691,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dlcs += li->num[i].cpus;
dais += li->num[i].cpus;
+
+ if (li->num[i].c2c) {
+ /* Codec2Codec */
+ dai_props[i].c2c_conf = c2c_conf;
+ c2c_conf += li->num[i].c2c;
+ }
} else {
/* DPCM Be's CPU = dummy */
dai_props[i].cpus =
@@ -759,6 +775,34 @@ int asoc_graph_card_probe(struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
+int asoc_graph_is_ports0(struct device_node *np)
+{
+ struct device_node *port, *ports, *ports0, *top;
+ int ret;
+
+ /* np is "endpoint" or "port" */
+ if (of_node_name_eq(np, "endpoint")) {
+ port = of_get_parent(np);
+ } else {
+ port = np;
+ of_node_get(port);
+ }
+
+ ports = of_get_parent(port);
+ top = of_get_parent(ports);
+ ports0 = of_get_child_by_name(top, "ports");
+
+ ret = ports0 == ports;
+
+ of_node_put(port);
+ of_node_put(ports);
+ of_node_put(ports0);
+ of_node_put(top);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
+
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
new file mode 100644
index 000000000000..85385a771d80
--- /dev/null
+++ b/sound/soc/generic/test-component.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// test-component.c -- Test Audio Component driver
+//
+// Copyright (C) 2020 Renesas Electronics Corporation
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define TEST_NAME_LEN 32
+struct test_dai_name {
+ char name[TEST_NAME_LEN];
+ char name_playback[TEST_NAME_LEN];
+ char name_capture[TEST_NAME_LEN];
+};
+
+struct test_priv {
+ struct device *dev;
+ struct snd_pcm_substream *substream;
+ struct delayed_work dwork;
+ struct snd_soc_component_driver *component_driver;
+ struct snd_soc_dai_driver *dai_driver;
+ struct test_dai_name *name;
+};
+
+struct test_adata {
+ u32 is_cpu:1;
+ u32 cmp_v:1;
+ u32 dai_v:1;
+};
+
+#define mile_stone(d) dev_info((d)->dev, "%s() : %s", __func__, (d)->driver->name)
+#define mile_stone_x(dev) dev_info(dev, "%s()", __func__)
+
+static int test_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ unsigned int clock = fmt & SND_SOC_DAIFMT_CLOCK_MASK;
+ unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+ unsigned int master = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ char *str;
+
+ dev_info(dai->dev, "name : %s", dai->name);
+
+ str = "unknown";
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ str = "i2s";
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ str = "right_j";
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ str = "left_j";
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ str = "dsp_a";
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ str = "dsp_b";
+ break;
+ case SND_SOC_DAIFMT_AC97:
+ str = "ac97";
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ str = "pdm";
+ break;
+ }
+ dev_info(dai->dev, "format : %s", str);
+
+ if (clock == SND_SOC_DAIFMT_CONT)
+ str = "continuous";
+ else
+ str = "gated";
+ dev_info(dai->dev, "clock : %s", str);
+
+ str = "unknown";
+ switch (master) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ str = "clk provider, frame provider";
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ str = "clk consumer, frame provider";
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ str = "clk provider, frame consumer";
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ str = "clk consumer, frame consumer";
+ break;
+ }
+ dev_info(dai->dev, "clock : codec is %s", str);
+
+ str = "unknown";
+ switch (inv) {
+ case SND_SOC_DAIFMT_NB_NF:
+ str = "normal bit, normal frame";
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ str = "normal bit, invert frame";
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ str = "invert bit, normal frame";
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ str = "invert bit, invert frame";
+ break;
+ }
+ dev_info(dai->dev, "signal : %s", str);
+
+ return 0;
+}
+
+static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+}
+
+static int test_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static u64 test_dai_formats =
+ /*
+ * Select below from Sound Card, not auto
+ * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP
+ * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP
+ * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC
+ * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_AC97 |
+ SND_SOC_POSSIBLE_DAIFMT_PDM |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const struct snd_soc_dai_ops test_ops = {
+ .set_fmt = test_dai_set_fmt,
+ .startup = test_dai_startup,
+ .shutdown = test_dai_shutdown,
+ .auto_selectable_formats = &test_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops test_verbose_ops = {
+ .set_sysclk = test_dai_set_sysclk,
+ .set_pll = test_dai_set_pll,
+ .set_clkdiv = test_dai_set_clkdiv,
+ .set_fmt = test_dai_set_fmt,
+ .mute_stream = test_dai_mute_stream,
+ .startup = test_dai_startup,
+ .shutdown = test_dai_shutdown,
+ .hw_params = test_dai_hw_params,
+ .hw_free = test_dai_hw_free,
+ .trigger = test_dai_trigger,
+ .bespoke_trigger = test_dai_bespoke_trigger,
+ .auto_selectable_formats = &test_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_384000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static int test_component_probe(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_remove(struct snd_soc_component *component)
+{
+ mile_stone(component);
+}
+
+static int test_component_suspend(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_resume(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+#define PREALLOC_BUFFER (32 * 1024)
+static int test_component_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ mile_stone(component);
+
+ snd_pcm_set_managed_buffer_all(
+ rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER);
+
+ return 0;
+}
+
+static void test_component_pcm_destruct(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ mile_stone(component);
+}
+
+static int test_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_seq_notifier(struct snd_soc_component *component,
+ enum snd_soc_dapm_type type, int subseq)
+{
+ mile_stone(component);
+}
+
+static int test_component_stream_event(struct snd_soc_component *component, int event)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware test_component_hardware = {
+ /* Random values to keep userspace happy when checking constraints */
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .buffer_bytes_max = 32 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 128,
+ .fifo_size = 256,
+};
+
+static int test_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ mile_stone(component);
+
+ /* BE's dont need dummy params */
+ if (!rtd->dai_link->no_pcm)
+ snd_soc_set_runtime_hwparams(substream, &test_component_hardware);
+
+ return 0;
+}
+
+static int test_component_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_ioctl(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_timer_stop(struct test_priv *priv)
+{
+ cancel_delayed_work(&priv->dwork);
+}
+
+static void test_component_timer_start(struct test_priv *priv)
+{
+ schedule_delayed_work(&priv->dwork, msecs_to_jiffies(10));
+}
+
+static void test_component_dwork(struct work_struct *work)
+{
+ struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
+
+ if (priv->substream)
+ snd_pcm_period_elapsed(priv->substream);
+
+ test_component_timer_start(priv);
+}
+
+static int test_component_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct test_priv *priv = dev_get_drvdata(component->dev);
+
+ mile_stone(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ test_component_timer_start(priv);
+ priv->substream = substream; /* set substream later */
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ priv->substream = NULL;
+ test_component_timer_stop(priv);
+ }
+
+ return 0;
+}
+
+static int test_component_sync_stop(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ static int pointer;
+
+ if (!runtime)
+ return 0;
+
+ pointer += 10;
+ if (pointer > PREALLOC_BUFFER)
+ pointer = 0;
+
+ /* mile_stone(component); */
+
+ return bytes_to_frames(runtime, pointer);
+}
+
+static int test_component_get_time_info(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct timespec64 *system_ts,
+ struct timespec64 *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ mile_stone_x(rtd->dev);
+
+ return 0;
+}
+
+/* CPU */
+static const struct test_adata test_cpu = { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
+static const struct test_adata test_cpu_vv = { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
+static const struct test_adata test_cpu_nv = { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
+static const struct test_adata test_cpu_vn = { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
+/* Codec */
+static const struct test_adata test_codec = { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
+static const struct test_adata test_codec_vv = { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
+static const struct test_adata test_codec_nv = { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
+static const struct test_adata test_codec_vn = { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
+
+static const struct of_device_id test_of_match[] = {
+ { .compatible = "test-cpu", .data = (void *)&test_cpu, },
+ { .compatible = "test-cpu-verbose", .data = (void *)&test_cpu_vv, },
+ { .compatible = "test-cpu-verbose-dai", .data = (void *)&test_cpu_nv, },
+ { .compatible = "test-cpu-verbose-component", .data = (void *)&test_cpu_vn, },
+ { .compatible = "test-codec", .data = (void *)&test_codec, },
+ { .compatible = "test-codec-verbose", .data = (void *)&test_codec_vv, },
+ { .compatible = "test-codec-verbose-dai", .data = (void *)&test_codec_nv, },
+ { .compatible = "test-codec-verbose-component", .data = (void *)&test_codec_vn, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, test_of_match);
+
+static const struct snd_soc_dapm_widget widgets[] = {
+ /*
+ * FIXME
+ *
+ * Just IN/OUT is OK for now,
+ * but need to be updated ?
+ */
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static int test_driver_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *ep;
+ const struct of_device_id *of_id = of_match_device(test_of_match, &pdev->dev);
+ const struct test_adata *adata = of_id->data;
+ struct snd_soc_component_driver *cdriv;
+ struct snd_soc_dai_driver *ddriv;
+ struct test_dai_name *dname;
+ struct test_priv *priv;
+ int num, ret, i;
+
+ num = of_graph_get_endpoint_count(node);
+ if (!num) {
+ dev_err(dev, "no port exits\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL);
+ ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL);
+ dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL);
+ if (!priv || !cdriv || !ddriv || !dname)
+ return -EINVAL;
+
+ priv->dev = dev;
+ priv->component_driver = cdriv;
+ priv->dai_driver = ddriv;
+ priv->name = dname;
+
+ INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
+ dev_set_drvdata(dev, priv);
+
+ if (adata->is_cpu) {
+ cdriv->name = "test_cpu";
+ cdriv->pcm_construct = test_component_pcm_construct;
+ cdriv->pointer = test_component_pointer;
+ cdriv->trigger = test_component_trigger;
+ } else {
+ cdriv->name = "test_codec";
+ cdriv->idle_bias_on = 1;
+ cdriv->endianness = 1;
+ cdriv->non_legacy_dai_naming = 1;
+ }
+
+ cdriv->open = test_component_open;
+ cdriv->dapm_widgets = widgets;
+ cdriv->num_dapm_widgets = ARRAY_SIZE(widgets);
+
+ if (adata->cmp_v) {
+ cdriv->probe = test_component_probe;
+ cdriv->remove = test_component_remove;
+ cdriv->suspend = test_component_suspend;
+ cdriv->resume = test_component_resume;
+ cdriv->set_sysclk = test_component_set_sysclk;
+ cdriv->set_pll = test_component_set_pll;
+ cdriv->set_jack = test_component_set_jack;
+ cdriv->seq_notifier = test_component_seq_notifier;
+ cdriv->stream_event = test_component_stream_event;
+ cdriv->set_bias_level = test_component_set_bias_level;
+ cdriv->close = test_component_close;
+ cdriv->ioctl = test_component_ioctl;
+ cdriv->hw_params = test_component_hw_params;
+ cdriv->hw_free = test_component_hw_free;
+ cdriv->prepare = test_component_prepare;
+ cdriv->sync_stop = test_component_sync_stop;
+ cdriv->get_time_info = test_component_get_time_info;
+ cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup;
+
+ if (adata->is_cpu)
+ cdriv->pcm_destruct = test_component_pcm_destruct;
+ }
+
+ i = 0;
+ for_each_endpoint_of_node(node, ep) {
+ snprintf(dname[i].name, TEST_NAME_LEN, "%s.%d", node->name, i);
+ ddriv[i].name = dname[i].name;
+
+ snprintf(dname[i].name_playback, TEST_NAME_LEN, "DAI%d Playback", i);
+ ddriv[i].playback.stream_name = dname[i].name_playback;
+ ddriv[i].playback.channels_min = 1;
+ ddriv[i].playback.channels_max = 384;
+ ddriv[i].playback.rates = STUB_RATES;
+ ddriv[i].playback.formats = STUB_FORMATS;
+
+ snprintf(dname[i].name_capture, TEST_NAME_LEN, "DAI%d Capture", i);
+ ddriv[i].capture.stream_name = dname[i].name_capture;
+ ddriv[i].capture.channels_min = 1;
+ ddriv[i].capture.channels_max = 384;
+ ddriv[i].capture.rates = STUB_RATES;
+ ddriv[i].capture.formats = STUB_FORMATS;
+
+ if (adata->dai_v)
+ ddriv[i].ops = &test_verbose_ops;
+ else
+ ddriv[i].ops = &test_ops;
+
+ i++;
+ }
+
+ ret = devm_snd_soc_register_component(dev, cdriv, ddriv, num);
+ if (ret < 0)
+ return ret;
+
+ mile_stone_x(dev);
+
+ return 0;
+}
+
+static int test_driver_remove(struct platform_device *pdev)
+{
+ mile_stone_x(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver test_driver = {
+ .driver = {
+ .name = "test-component",
+ .of_match_table = test_of_match,
+ },
+ .probe = test_driver_probe,
+ .remove = test_driver_remove,
+};
+module_platform_driver(test_driver);
+
+MODULE_ALIAS("platform:asoc-test-component");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("ASoC Test Component");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 61b71d6c44cf..2dd5ff7e35ce 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -371,7 +371,7 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH
config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH
tristate "KBL with DA7219 and MAX98357A in I2S Mode"
- depends on I2C && ACPI
+ depends on I2C && ACPI && GPIOLIB
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
help
@@ -427,6 +427,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
depends on MFD_INTEL_LPSS || COMPILE_TEST
depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_MAX98357A
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
@@ -470,6 +471,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_RT1015
select SND_SOC_RT1015P
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
select SND_SOC_INTEL_HDA_DSP_COMMON
@@ -511,6 +513,20 @@ config SND_SOC_INTEL_SOF_PCM512x_MACH
Say Y or m if you have such a device.
If unsure select "N".
+config SND_SOC_INTEL_SOF_ES8336_MACH
+ tristate "SOF with ES8336 codec in I2S mode"
+ depends on I2C && ACPI && GPIOLIB
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
+ select SND_SOC_ES8316
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with es8336 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK)
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index ed21b82a4cf6..9ee8ed864f5d 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -21,6 +21,7 @@ snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o
snd-soc-sof_cs42l42-objs := sof_cs42l42.o
+snd-soc-sof_es8336-objs := sof_es8336.o
snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o
snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o
snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o
@@ -42,6 +43,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \
sof_sdw_dmic.o sof_sdw_hdmi.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c
index c5122d3b0e6c..6cba5552f7a2 100644
--- a/sound/soc/intel/boards/bdw-rt5650.c
+++ b/sound/soc/intel/boards/bdw-rt5650.c
@@ -251,7 +251,7 @@ static struct snd_soc_dai_link bdw_rt5650_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broadwell_ssp0_fixup,
.ops = &bdw_rt5650_ops,
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
index e01b7a90ca6c..119c441f4c10 100644
--- a/sound/soc/intel/boards/bdw-rt5677.c
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -351,7 +351,7 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broadwell_ssp0_fixup,
.ops = &bdw_rt5677_ops,
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index 3c3aff9c61cc..618d0645ed8d 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -217,7 +217,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
.no_pcm = 1,
.init = broadwell_rt286_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broadwell_ssp0_fixup,
.ops = &broadwell_rt286_ops,
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index e67ddfb8e469..b768d9b8ec02 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -572,7 +572,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp_fixup,
.dpcm_playback = 1,
@@ -585,7 +585,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.no_pcm = 1,
.init = broxton_da7219_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp_fixup,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 47f6b1523ae6..920e575b4314 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -468,7 +468,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.no_pcm = 1,
.init = broxton_rt298_codec_init,
.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp5_fixup,
.ops = &broxton_rt298_ops,
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index a9e51bbf018c..0a736308052a 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -126,7 +126,7 @@ static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
return ret;
@@ -195,7 +195,7 @@ static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.init = byt_cht_cx2072x_init,
.be_hw_params_fixup = byt_cht_cx2072x_fixup,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
index a28773fb7892..fae1e7e785b0 100644
--- a/sound/soc/intel/boards/bytcht_da7213.c
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -81,7 +81,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
return ret;
@@ -195,7 +195,7 @@ static struct snd_soc_dai_link dailink[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = codec_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 4d313d0d0f23..9d86fea51a7d 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -265,7 +265,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS
+ SND_SOC_DAIFMT_CBC_CFC
);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
@@ -336,7 +336,7 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_cht_es8316_codec_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
index 9b48fe701a2c..67b3c4e97864 100644
--- a/sound/soc/intel/boards/bytcht_nocodec.c
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -61,7 +61,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
@@ -141,7 +141,7 @@ static struct snd_soc_dai_link dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = codec_fixup,
.ignore_suspend = 1,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index a6e837290c7d..a0c5f0e9c22a 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -269,13 +269,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO;
if (SND_SOC_DAPM_EVENT_ON(event)) {
- if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
- ret = clk_prepare_enable(priv->mclk);
- if (ret < 0) {
- dev_err(card->dev,
- "could not configure MCLK state\n");
- return ret;
- }
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev, "could not configure MCLK state\n");
+ return ret;
}
ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000);
} else {
@@ -287,10 +284,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK,
48000 * 512,
SND_SOC_CLOCK_IN);
- if (!ret) {
- if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
- clk_disable_unprepare(priv->mclk);
- }
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
}
if (ret < 0) {
@@ -1217,30 +1212,25 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
- /*
- * The firmware might enable the clock at
- * boot (this information may or may not
- * be reflected in the enable clock register).
- * To change the rate we must disable the clock
- * first to cover these cases. Due to common
- * clock framework restrictions that do not allow
- * to disable a clock that has not been enabled,
- * we need to enable the clock first.
- */
- ret = clk_prepare_enable(priv->mclk);
- if (!ret)
- clk_disable_unprepare(priv->mclk);
-
- if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
- ret = clk_set_rate(priv->mclk, 25000000);
- else
- ret = clk_set_rate(priv->mclk, 19200000);
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover
+ * these cases. Due to common clock framework restrictions that
+ * do not allow to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
- if (ret) {
- dev_err(card->dev, "unable to set MCLK rate\n");
- return ret;
- }
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
+ ret = clk_set_rate(priv->mclk, 25000000);
+ else
+ ret = clk_set_rate(priv->mclk, 19200000);
+ if (ret) {
+ dev_err(card->dev, "unable to set MCLK rate\n");
+ return ret;
}
if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
@@ -1336,7 +1326,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
return ret;
@@ -1411,7 +1401,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_rt5640_codec_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
@@ -1495,12 +1485,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
static const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3", "none" };
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
__maybe_unused const char *spk_type;
const struct dmi_system_id *dmi_id;
const char *headset2_string = "";
const char *lineout_string = "";
struct byt_rt5640_private *priv;
- struct snd_soc_acpi_mach *mach;
const char *platform_name;
struct acpi_device *adev;
struct device *codec_dev;
@@ -1511,13 +1501,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
int aif;
is_bytcr = false;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* register the soc card */
- byt_rt5640_card.dev = &pdev->dev;
- mach = byt_rt5640_card.dev->platform_data;
+ byt_rt5640_card.dev = dev;
snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
/* fix index of codec dai */
@@ -1537,7 +1526,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
put_device(&adev->dev);
byt_rt5640_dais[dai_index].codecs->name = byt_rt5640_codec_name;
} else {
- dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
return -ENXIO;
}
@@ -1580,13 +1569,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
&pkg_ctx);
if (pkg_found) {
if (chan_package.aif_value == 1) {
- dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
+ dev_info(dev, "BIOS Routing: AIF1 connected\n");
byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1;
} else if (chan_package.aif_value == 2) {
- dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
+ dev_info(dev, "BIOS Routing: AIF2 connected\n");
byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
} else {
- dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
+ dev_info(dev, "BIOS Routing isn't valid, ignored\n");
pkg_found = false;
}
}
@@ -1610,7 +1599,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (dmi_id)
byt_rt5640_quirk = (unsigned long)dmi_id->driver_data;
if (quirk_override != -1) {
- dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
byt_rt5640_quirk, quirk_override);
byt_rt5640_quirk = quirk_override;
}
@@ -1624,12 +1613,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
acpi_dev_add_driver_gpios(ACPI_COMPANION(priv->codec_dev),
byt_rt5640_hp_elitepad_1000g2_gpios);
- priv->hsmic_detect = devm_fwnode_gpiod_get(&pdev->dev, codec_dev->fwnode,
+ priv->hsmic_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
"headset-mic-detect", GPIOD_IN,
"headset-mic-detect");
if (IS_ERR(priv->hsmic_detect)) {
- ret_val = PTR_ERR(priv->hsmic_detect);
- dev_err_probe(&pdev->dev, ret_val, "getting hsmic-detect GPIO\n");
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->hsmic_detect),
+ "getting hsmic-detect GPIO\n");
goto err_device;
}
}
@@ -1639,7 +1628,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (ret_val)
goto err_remove_gpios;
- log_quirks(&pdev->dev);
+ log_quirks(dev);
if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
(byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
@@ -1654,23 +1643,18 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
byt_rt5640_dais[dai_index].cpus->dai_name = "ssp0-port";
if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
- priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3");
if (IS_ERR(priv->mclk)) {
- ret_val = PTR_ERR(priv->mclk);
-
- dev_err(&pdev->dev,
- "Failed to get MCLK from pmc_plt_clk_3: %d\n",
- ret_val);
-
- /*
- * Fall back to bit clock usage for -ENOENT (clock not
- * available likely due to missing dependencies), bail
- * for all other errors, including -EPROBE_DEFER
- */
- if (ret_val != -ENOENT)
- goto err;
- byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk),
+ "Failed to get MCLK from pmc_plt_clk_3\n");
+ goto err;
}
+ /*
+ * Fall back to bit clock usage when clock is not
+ * available likely due to missing dependencies.
+ */
+ if (!priv->mclk)
+ byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
}
if (byt_rt5640_quirk & BYT_RT5640_NO_SPEAKERS) {
@@ -1714,7 +1698,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (ret_val)
goto err;
- sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+ sof_parent = snd_soc_acpi_sof_parent(dev);
/* set card and driver name */
if (sof_parent) {
@@ -1729,11 +1713,9 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (sof_parent)
dev->driver->pm = &snd_soc_pm_ops;
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
-
+ ret_val = devm_snd_soc_register_card(dev, &byt_rt5640_card);
if (ret_val) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
- ret_val);
+ dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val);
goto err;
}
platform_set_drvdata(pdev, &byt_rt5640_card);
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index e94c9124d4f4..5e9c53dadbc7 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -188,13 +188,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
}
if (SND_SOC_DAPM_EVENT_ON(event)) {
- if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
- ret = clk_prepare_enable(priv->mclk);
- if (ret < 0) {
- dev_err(card->dev,
- "could not configure MCLK state");
- return ret;
- }
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev, "could not configure MCLK state");
+ return ret;
}
ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50);
} else {
@@ -207,8 +204,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
48000 * 512,
SND_SOC_CLOCK_IN);
if (!ret)
- if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
- clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->mclk);
}
if (ret < 0) {
@@ -629,29 +625,25 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
- /*
- * The firmware might enable the clock at
- * boot (this information may or may not
- * be reflected in the enable clock register).
- * To change the rate we must disable the clock
- * first to cover these cases. Due to common
- * clock framework restrictions that do not allow
- * to disable a clock that has not been enabled,
- * we need to enable the clock first.
- */
- ret = clk_prepare_enable(priv->mclk);
- if (!ret)
- clk_disable_unprepare(priv->mclk);
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover
+ * these cases. Due to common clock framework restrictions that
+ * do not allow to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
- if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
- ret = clk_set_rate(priv->mclk, 25000000);
- else
- ret = clk_set_rate(priv->mclk, 19200000);
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
+ ret = clk_set_rate(priv->mclk, 25000000);
+ else
+ ret = clk_set_rate(priv->mclk, 19200000);
- if (ret)
- dev_err(card->dev, "unable to set MCLK rate\n");
- }
+ if (ret)
+ dev_err(card->dev, "unable to set MCLK rate\n");
report = 0;
if (BYT_RT5651_JDSRC(byt_rt5651_quirk))
@@ -713,7 +705,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS
+ SND_SOC_DAIFMT_CBC_CFC
);
if (ret < 0) {
@@ -798,7 +790,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_rt5651_codec_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
@@ -894,9 +886,10 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */
static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" };
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
struct byt_rt5651_private *priv;
- struct snd_soc_acpi_mach *mach;
const char *platform_name;
struct acpi_device *adev;
struct device *codec_dev;
@@ -906,14 +899,12 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
int dai_index = 0;
int i;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* register the soc card */
- byt_rt5651_card.dev = &pdev->dev;
-
- mach = byt_rt5651_card.dev->platform_data;
+ byt_rt5651_card.dev = dev;
snd_soc_card_set_drvdata(&byt_rt5651_card, priv);
/* fix index of codec dai */
@@ -933,7 +924,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
put_device(&adev->dev);
byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name;
} else {
- dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
return -ENXIO;
}
@@ -981,13 +972,13 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
&pkg_ctx);
if (pkg_found) {
if (chan_package.aif_value == 1) {
- dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
+ dev_info(dev, "BIOS Routing: AIF1 connected\n");
byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF1;
} else if (chan_package.aif_value == 2) {
- dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
+ dev_info(dev, "BIOS Routing: AIF2 connected\n");
byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2;
} else {
- dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
+ dev_info(dev, "BIOS Routing isn't valid, ignored\n");
pkg_found = false;
}
}
@@ -1002,7 +993,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
dmi_check_system(byt_rt5651_quirk_table);
if (quirk_override != -1) {
- dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
byt_rt5651_quirk, quirk_override);
byt_rt5651_quirk = quirk_override;
}
@@ -1018,8 +1009,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
if (byt_rt5651_gpios) {
devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios);
- priv->ext_amp_gpio = devm_fwnode_gpiod_get(&pdev->dev,
- codec_dev->fwnode,
+ priv->ext_amp_gpio = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
"ext-amp-enable",
GPIOD_OUT_LOW,
"speaker-amp");
@@ -1030,15 +1020,13 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
priv->ext_amp_gpio = NULL;
break;
default:
- dev_err(&pdev->dev, "Failed to get ext-amp-enable GPIO: %d\n",
- ret_val);
+ dev_err(dev, "Failed to get ext-amp-enable GPIO: %d\n", ret_val);
fallthrough;
case -EPROBE_DEFER:
goto err;
}
}
- priv->hp_detect = devm_fwnode_gpiod_get(&pdev->dev,
- codec_dev->fwnode,
+ priv->hp_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
"hp-detect",
GPIOD_IN,
"hp-detect");
@@ -1049,8 +1037,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
priv->hp_detect = NULL;
break;
default:
- dev_err(&pdev->dev, "Failed to get hp-detect GPIO: %d\n",
- ret_val);
+ dev_err(dev, "Failed to get hp-detect GPIO: %d\n", ret_val);
fallthrough;
case -EPROBE_DEFER:
goto err;
@@ -1058,7 +1045,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
}
}
- log_quirks(&pdev->dev);
+ log_quirks(dev);
if ((byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) ||
(byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2))
@@ -1069,21 +1056,18 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
byt_rt5651_dais[dai_index].cpus->dai_name = "ssp0-port";
if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
- priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3");
if (IS_ERR(priv->mclk)) {
- ret_val = PTR_ERR(priv->mclk);
- dev_err(&pdev->dev,
- "Failed to get MCLK from pmc_plt_clk_3: %d\n",
- ret_val);
- /*
- * Fall back to bit clock usage for -ENOENT (clock not
- * available likely due to missing dependencies), bail
- * for all other errors, including -EPROBE_DEFER
- */
- if (ret_val != -ENOENT)
- goto err;
- byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk),
+ "Failed to get MCLK from pmc_plt_clk_3\n");
+ goto err;
}
+ /*
+ * Fall back to bit clock usage when clock is not
+ * available likely due to missing dependencies.
+ */
+ if (!priv->mclk)
+ byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
}
snprintf(byt_rt5651_components, sizeof(byt_rt5651_components),
@@ -1112,7 +1096,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
if (ret_val)
goto err;
- sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+ sof_parent = snd_soc_acpi_sof_parent(dev);
/* set card and driver name */
if (sof_parent) {
@@ -1125,13 +1109,11 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
/* set pm ops */
if (sof_parent)
- pdev->dev.driver->pm = &snd_soc_pm_ops;
-
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
+ dev->driver->pm = &snd_soc_pm_ops;
+ ret_val = devm_snd_soc_register_card(dev, &byt_rt5651_card);
if (ret_val) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
- ret_val);
+ dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val);
goto err;
}
platform_set_drvdata(pdev, &byt_rt5651_card);
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 580d5fddae5a..504ef4cab111 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -265,7 +265,7 @@ static int byt_wm5102_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret) {
dev_err(rtd->dev, "Error setting format to I2S: %d\n", ret);
return ret;
@@ -349,7 +349,7 @@ static struct snd_soc_dai_link byt_wm5102_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_wm5102_codec_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 131882378a59..1bc21434c9de 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -264,7 +264,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
}
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS;
+ | SND_SOC_DAIFMT_CBC_CFC;
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt);
if (ret < 0) {
@@ -372,7 +372,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c
index da5a5cbc8759..bad32d2bdf89 100644
--- a/sound/soc/intel/boards/cht_bsw_nau8824.c
+++ b/sound/soc/intel/boards/cht_bsw_nau8824.c
@@ -214,7 +214,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
.dpcm_playback = 1,
@@ -278,6 +278,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
snd_soc_card_cht.driver_name = DRIVER_NAME;
}
+ snd_soc_card_cht.components = nau8824_components();
+
/* set pm ops */
if (sof_parent)
pdev->dev.driver->pm = &snd_soc_pm_ops;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 804dbc7911d5..e182012d0c60 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -362,7 +362,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS
+ SND_SOC_DAIFMT_CBC_CFC
);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
@@ -372,7 +372,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS
+ SND_SOC_DAIFMT_CBC_CFC
);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
@@ -396,7 +396,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0),
SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to TDM %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 9509b6e161b8..26eb8ad0d262 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -300,7 +300,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_CBC_CFC);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c
index 71fe26a1b701..bad3829e52ca 100644
--- a/sound/soc/intel/boards/glk_rt5682_max98357a.c
+++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c
@@ -18,14 +18,18 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../codecs/rt5682.h"
+#include "../../codecs/rt5682s.h"
#include "../../codecs/hdac_hdmi.h"
#include "hda_dsp_common.h"
/* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */
#define GLK_PLAT_CLK_FREQ 19200000
#define RT5682_PLL_FREQ (48000 * 512)
-#define GLK_REALTEK_CODEC_DAI "rt5682-aif1"
+#define RT5682_DAI_NAME "rt5682-aif1"
+#define RT5682S_DAI_NAME "rt5682s-aif1"
#define GLK_MAXIM_CODEC_DAI "HiFi"
+#define RT5682_DEV0_NAME "i2c-10EC5682:00"
+#define RT5682S_DEV0_NAME "i2c-RTL5682:00"
#define MAXIM_DEV0_NAME "MX98357A:00"
#define DUAL_CHANNEL 2
#define QUAD_CHANNEL 4
@@ -43,6 +47,7 @@ struct glk_card_private {
struct snd_soc_jack geminilake_headset;
struct list_head hdmi_pcm_list;
bool common_hdmi_codec_drv;
+ int is_rt5682s;
};
enum {
@@ -139,9 +144,19 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_jack *jack;
- int ret;
+ int pll_id, pll_source, clk_id, ret;
+
+ if (ctx->is_rt5682s) {
+ pll_id = RT5682S_PLL2;
+ pll_source = RT5682S_PLL_S_MCLK;
+ clk_id = RT5682S_SCLK_S_PLL2;
+ } else {
+ pll_id = RT5682_PLL1;
+ pll_source = RT5682_PLL1_S_MCLK;
+ clk_id = RT5682_SCLK_S_PLL1;
+ }
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK,
+ ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
GLK_PLAT_CLK_FREQ, RT5682_PLL_FREQ);
if (ret < 0) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
@@ -149,7 +164,7 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
}
/* Configure sysclk for codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id,
RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
@@ -344,9 +359,12 @@ SND_SOC_DAILINK_DEF(ssp1_codec,
SND_SOC_DAILINK_DEF(ssp2_pin,
DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin")));
-SND_SOC_DAILINK_DEF(ssp2_codec,
- DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00",
- GLK_REALTEK_CODEC_DAI)));
+SND_SOC_DAILINK_DEF(ssp2_codec_5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
+ RT5682_DAI_NAME)));
+SND_SOC_DAILINK_DEF(ssp2_codec_5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT5682S_DEV0_NAME,
+ RT5682S_DAI_NAME)));
SND_SOC_DAILINK_DEF(dmic_pin,
DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
@@ -473,7 +491,7 @@ static struct snd_soc_dai_link geminilake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = geminilake_ssp_fixup,
.dpcm_playback = 1,
@@ -486,13 +504,13 @@ static struct snd_soc_dai_link geminilake_dais[] = {
.no_pcm = 1,
.init = geminilake_rt5682_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = geminilake_ssp_fixup,
.ops = &geminilake_rt5682_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
- SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec, platform),
+ SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec_5682, platform),
},
{
.name = "dmic01",
@@ -592,12 +610,28 @@ static int geminilake_audio_probe(struct platform_device *pdev)
struct snd_soc_acpi_mach *mach;
const char *platform_name;
struct snd_soc_card *card;
- int ret;
+ int ret, i;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ /* Detect the headset codec variant */
+ if (acpi_dev_present("RTL5682", NULL, -1)) {
+ /* ALC5682I-VS is detected */
+ ctx->is_rt5682s = 1;
+
+ for (i = 0; i < glk_audio_card_rt5682_m98357a.num_links; i++) {
+ if (strcmp(geminilake_dais[i].name, "SSP2-Codec"))
+ continue;
+
+ /* update the dai link to use rt5682s codec */
+ geminilake_dais[i].codecs = ssp2_codec_5682s;
+ geminilake_dais[i].num_codecs = ARRAY_SIZE(ssp2_codec_5682s);
+ break;
+ }
+ }
+
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
card = &glk_audio_card_rt5682_m98357a;
diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c
index c763bfeb1f38..36e136acbef5 100644
--- a/sound/soc/intel/boards/haswell.c
+++ b/sound/soc/intel/boards/haswell.c
@@ -145,7 +145,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = haswell_ssp0_fixup,
.ops = &haswell_rt5640_ops,
diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c
index 14b625e947f5..a4bdf634e9b9 100644
--- a/sound/soc/intel/boards/kbl_da7219_max98357a.c
+++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c
@@ -518,7 +518,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.dpcm_playback = 1,
@@ -531,7 +531,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.init = kabylake_da7219_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c
index 2b43459adc33..620a9fbcb08f 100644
--- a/sound/soc/intel/boards/kbl_da7219_max98927.c
+++ b/sound/soc/intel/boards/kbl_da7219_max98927.c
@@ -764,7 +764,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ignore_pmdown_time = 1,
@@ -779,7 +779,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.init = kabylake_da7219_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.dpcm_playback = 1,
@@ -907,7 +907,7 @@ static struct snd_soc_dai_link kabylake_max98_927_373_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ignore_pmdown_time = 1,
diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c
index 289ca39b8206..1cb56ec363e8 100644
--- a/sound/soc/intel/boards/kbl_rt5660.c
+++ b/sound/soc/intel/boards/kbl_rt5660.c
@@ -436,7 +436,7 @@ static struct snd_soc_dai_link kabylake_rt5660_dais[] = {
.exit = kabylake_rt5660_codec_exit,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp0_fixup,
.ops = &kabylake_rt5660_ops,
diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c
index a3e040a249f6..f24e0ce5d49f 100644
--- a/sound/soc/intel/boards/kbl_rt5663_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c
@@ -767,7 +767,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.dpcm_playback = 1,
@@ -781,7 +781,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.init = kabylake_rt5663_max98927_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.ops = &kabylake_rt5663_ops,
@@ -879,7 +879,7 @@ static struct snd_soc_dai_link kabylake_5663_dais[] = {
.no_pcm = 1,
.init = kabylake_rt5663_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.ops = &kabylake_rt5663_ops,
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index dd38fdaf2ff5..6874e981c8df 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -639,7 +639,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.dpcm_playback = 1,
@@ -653,7 +653,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.no_pcm = 1,
.init = kabylake_rt5663_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = kabylake_ssp_fixup,
.ops = &kabylake_rt5663_ops,
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index e3a1f04a8b53..7297eb05613c 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -539,7 +539,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
.dpcm_playback = 1,
@@ -552,7 +552,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
.init = skylake_nau8825_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
.ops = &skylake_nau8825_ops,
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index adf5992a9ec5..68efde1633b3 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -578,7 +578,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.init = skylake_ssm4567_codec_init,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
@@ -593,7 +593,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
.init = skylake_nau8825_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
.ops = &skylake_nau8825_ops,
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
index 75dab5405380..eca4a78668af 100644
--- a/sound/soc/intel/boards/skl_rt286.c
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -434,7 +434,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.init = skylake_rt286_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp0_fixup,
.ops = &skylake_rt286_ops,
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
new file mode 100644
index 000000000000..20d577eaab6d
--- /dev/null
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver with es8336 Codec
+ */
+
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "hda_dsp_common.h"
+
+#define SOF_ES8336_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0))
+#define SOF_ES8336_SSP_CODEC_MASK (GENMASK(3, 0))
+
+#define SOF_ES8336_TGL_GPIO_QUIRK BIT(4)
+#define SOF_ES8336_ENABLE_DMIC BIT(5)
+
+static unsigned long quirk;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+struct sof_es8336_private {
+ struct device *codec_dev;
+ struct gpio_desc *gpio_pa;
+ struct snd_soc_jack jack;
+ struct list_head hdmi_pcm_list;
+ bool speaker_en;
+};
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, true };
+static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
+ { "pa-enable-gpios", &pa_enable_gpio, 1 },
+ { }
+};
+
+static const struct acpi_gpio_params quirk_pa_enable_gpio = { 1, 0, true };
+static const struct acpi_gpio_mapping quirk_acpi_es8336_gpios[] = {
+ { "pa-enable-gpios", &quirk_pa_enable_gpio, 1 },
+ { }
+};
+
+static const struct acpi_gpio_mapping *gpio_mapping = acpi_es8336_gpios;
+
+static void log_quirks(struct device *dev)
+{
+ dev_info(dev, "quirk SSP%ld", SOF_ES8336_SSP_CODEC(quirk));
+}
+
+static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->speaker_en = false;
+ else
+ priv->speaker_en = true;
+
+ gpiod_set_value_cansleep(priv->gpio_pa, priv->speaker_en);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget sof_es8316_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ sof_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_es8316_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled by the "Speaker Power" supply.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_soc_dapm_route sof_es8316_intmic_in1_map[] = {
+ {"MIC1", NULL, "Internal Mic"},
+ {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static const struct snd_kcontrol_new sof_es8316_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static struct snd_soc_jack_pin sof_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int dmic_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+
+static int sof_hdmi_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_dai *dai = asoc_rtd_to_codec(runtime, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(runtime->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = runtime->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &priv->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+ const struct snd_soc_dapm_route *custom_map;
+ int num_routes;
+ int ret;
+
+ card->dapm.idle_bias_off = true;
+
+ custom_map = sof_es8316_intmic_in1_map;
+ num_routes = ARRAY_SIZE(sof_es8316_intmic_in1_map);
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_card_jack_new(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, sof_es8316_jack_pins,
+ ARRAY_SIZE(sof_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+ snd_soc_component_set_jack(codec, &priv->jack, NULL);
+
+ return 0;
+}
+
+static void sof_es8316_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_es8336_quirk_cb(const struct dmi_system_id *id)
+{
+ quirk = (unsigned long)id->driver_data;
+
+ if (quirk & SOF_ES8336_TGL_GPIO_QUIRK)
+ gpio_mapping = quirk_acpi_es8336_gpios;
+
+ return 1;
+}
+
+static const struct dmi_system_id sof_es8336_quirk_table[] = {
+ {
+ .callback = sof_es8336_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "CHUWI Innovation And Technology"),
+ DMI_MATCH(DMI_BOARD_NAME, "Hi10 X"),
+ },
+ .driver_data = (void *)SOF_ES8336_SSP_CODEC(2)
+ },
+ {
+ .callback = sof_es8336_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "IP3 tech"),
+ DMI_MATCH(DMI_BOARD_NAME, "WN1"),
+ },
+ .driver_data = (void *)(SOF_ES8336_SSP_CODEC(0) |
+ SOF_ES8336_TGL_GPIO_QUIRK |
+ SOF_ES8336_ENABLE_DMIC)
+ },
+ {}
+};
+
+static int sof_es8336_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ const int sysclk = 19200000;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s, Failed to set ES8336 SYSCLK: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* machine stream operations */
+static struct snd_soc_ops sof_es8336_ops = {
+ .hw_params = sof_es8336_hw_params,
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:00:1f.3"
+ }
+};
+
+SND_SOC_DAILINK_DEF(ssp1_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+
+static struct snd_soc_dai_link_component dmic_component[] = {
+ {
+ .name = "dmic-codec",
+ .dai_name = "dmic-hifi",
+ }
+};
+
+static int sof_es8336_late_probe(struct snd_soc_card *card)
+{
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+ struct sof_hdmi_pcm *pcm;
+
+ if (list_empty(&priv->hdmi_pcm_list))
+ return -ENOENT;
+
+ pcm = list_first_entry(&priv->hdmi_pcm_list, struct sof_hdmi_pcm, head);
+
+ return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
+}
+
+/* SoC card */
+static struct snd_soc_card sof_es8336_card = {
+ .name = "essx8336", /* sof- prefix added automatically */
+ .owner = THIS_MODULE,
+ .dapm_widgets = sof_es8316_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_es8316_widgets),
+ .dapm_routes = sof_es8316_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_es8316_audio_map),
+ .controls = sof_es8316_controls,
+ .num_controls = ARRAY_SIZE(sof_es8316_controls),
+ .fully_routed = true,
+ .late_probe = sof_es8336_late_probe,
+ .num_links = 1,
+};
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+ int ssp_codec,
+ int dmic_be_num,
+ int hdmi_num)
+{
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link *links;
+ struct snd_soc_dai_link_component *idisp_components;
+ int hdmi_id_offset = 0;
+ int id = 0;
+ int i;
+
+ links = devm_kcalloc(dev, sof_es8336_card.num_links,
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ cpus = devm_kcalloc(dev, sof_es8336_card.num_links,
+ sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!links || !cpus)
+ goto devm_err;
+
+ /* codec SSP */
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d-Codec", ssp_codec);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ links[id].codecs = ssp1_codec;
+ links[id].num_codecs = ARRAY_SIZE(ssp1_codec);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_es8316_init;
+ links[id].exit = sof_es8316_exit;
+ links[id].ops = &sof_es8336_ops;
+ links[id].nonatomic = true;
+ links[id].dpcm_playback = 1;
+ links[id].dpcm_capture = 1;
+ links[id].no_pcm = 1;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin",
+ ssp_codec);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ id++;
+
+ /* dmic */
+ if (dmic_be_num > 0) {
+ /* at least we have dmic01 */
+ links[id].name = "dmic01";
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = "DMIC01 Pin";
+ links[id].init = dmic_init;
+ if (dmic_be_num > 1) {
+ /* set up 2 BE links at most */
+ links[id + 1].name = "dmic16k";
+ links[id + 1].cpus = &cpus[id + 1];
+ links[id + 1].cpus->dai_name = "DMIC16k Pin";
+ dmic_be_num = 2;
+ }
+ } else {
+ /* HDMI dai link starts at 3 according to current topology settings */
+ hdmi_id_offset = 2;
+ }
+
+ for (i = 0; i < dmic_be_num; i++) {
+ links[id].id = id;
+ links[id].num_cpus = 1;
+ links[id].codecs = dmic_component;
+ links[id].num_codecs = ARRAY_SIZE(dmic_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].ignore_suspend = 1;
+ links[id].dpcm_capture = 1;
+ links[id].no_pcm = 1;
+
+ id++;
+ }
+
+ /* HDMI */
+ if (hdmi_num > 0) {
+ idisp_components = devm_kzalloc(dev,
+ sizeof(struct snd_soc_dai_link_component) *
+ hdmi_num, GFP_KERNEL);
+ if (!idisp_components)
+ goto devm_err;
+ }
+
+ for (i = 1; i <= hdmi_num; i++) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d", i);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id + hdmi_id_offset;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d Pin", i);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ idisp_components[i - 1].name = "ehdaudio0D2";
+ idisp_components[i - 1].dai_name = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "intel-hdmi-hifi%d",
+ i);
+ if (!idisp_components[i - 1].dai_name)
+ goto devm_err;
+
+ links[id].codecs = &idisp_components[i - 1];
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_hdmi_init;
+ links[id].dpcm_playback = 1;
+ links[id].no_pcm = 1;
+
+ id++;
+ }
+
+ return links;
+
+devm_err:
+ return NULL;
+}
+
+ /* i2c-<HID>:00 with HID being 8 chars */
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int sof_es8336_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_es8336_private *priv;
+ struct acpi_device *adev;
+ struct snd_soc_dai_link *dai_links;
+ struct device *codec_dev;
+ int dmic_be_num = 0;
+ int hdmi_num = 3;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card = &sof_es8336_card;
+ card->dev = dev;
+
+ if (!dmi_check_system(sof_es8336_quirk_table))
+ quirk = SOF_ES8336_SSP_CODEC(2);
+
+ if (quirk & SOF_ES8336_ENABLE_DMIC)
+ dmic_be_num = 2;
+
+ if (quirk_override != -1) {
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ quirk, quirk_override);
+ quirk = quirk_override;
+ }
+ log_quirks(dev);
+
+ sof_es8336_card.num_links += dmic_be_num + hdmi_num;
+ dai_links = sof_card_dai_links_create(dev,
+ SOF_ES8336_SSP_CODEC(quirk),
+ dmic_be_num, hdmi_num);
+ if (!dai_links)
+ return -ENOMEM;
+
+ sof_es8336_card.dai_link = dai_links;
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ put_device(&adev->dev);
+ dai_links[0].codecs->name = codec_name;
+ }
+
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_es8336_card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ /* get speaker enable GPIO */
+ codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dev, gpio_mapping);
+ if (ret)
+ dev_warn(codec_dev, "unable to add GPIO mapping table\n");
+
+ priv->gpio_pa = gpiod_get(codec_dev, "pa-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_pa)) {
+ ret = PTR_ERR(priv->gpio_pa);
+ dev_err(codec_dev, "%s, could not get pa-enable: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ priv->codec_dev = codec_dev;
+ INIT_LIST_HEAD(&priv->hdmi_pcm_list);
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ gpiod_put(priv->gpio_pa);
+ dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
+ goto err;
+ }
+ platform_set_drvdata(pdev, &sof_es8336_card);
+ return 0;
+
+err:
+ put_device(codec_dev);
+ return ret;
+}
+
+static int 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);
+
+ gpiod_put(priv->gpio_pa);
+ put_device(priv->codec_dev);
+
+ return 0;
+}
+
+static struct platform_driver sof_es8336_driver = {
+ .driver = {
+ .name = "sof-essx8336",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = sof_es8336_probe,
+ .remove = sof_es8336_remove,
+};
+module_platform_driver(sof_es8336_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) SOF + ES8336 Machine driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sof-essx8336");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index f096bd6d69be..c41f386b4138 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -18,9 +18,11 @@
#include <sound/soc.h>
#include <sound/sof.h>
#include <sound/rt5682.h>
+#include <sound/rt5682s.h>
#include <sound/soc-acpi.h>
#include "../../codecs/rt1015.h"
#include "../../codecs/rt5682.h"
+#include "../../codecs/rt5682s.h"
#include "../../codecs/hdac_hdmi.h"
#include "../common/soc-intel-quirks.h"
#include "hda_dsp_common.h"
@@ -56,6 +58,7 @@
#define SOF_BT_OFFLOAD_SSP(quirk) \
(((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22)
+#define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23)
/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
@@ -208,9 +211,16 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
/* need to enable ASRC function for 24MHz mclk rate */
if ((sof_rt5682_quirk & SOF_RT5682_MCLK_EN) &&
(sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ)) {
- rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER |
- RT5682_AD_STEREO1_FILTER,
- RT5682_CLK_SEL_I2S1_ASRC);
+ if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT)
+ rt5682s_sel_asrc_clk_src(component,
+ RT5682S_DA_STEREO1_FILTER |
+ RT5682S_AD_STEREO1_FILTER,
+ RT5682S_CLK_SEL_I2S1_ASRC);
+ else
+ rt5682_sel_asrc_clk_src(component,
+ RT5682_DA_STEREO1_FILTER |
+ RT5682_AD_STEREO1_FILTER,
+ RT5682_CLK_SEL_I2S1_ASRC);
}
if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) {
@@ -277,7 +287,7 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- int clk_id, clk_freq, pll_out, ret;
+ int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) {
if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) {
@@ -289,35 +299,52 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
}
}
- clk_id = RT5682_PLL1_S_MCLK;
+ if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT)
+ pll_source = RT5682S_PLL_S_MCLK;
+ else
+ pll_source = RT5682_PLL1_S_MCLK;
/* get the tplg configured mclk. */
- clk_freq = sof_dai_get_mclk(rtd);
+ pll_in = sof_dai_get_mclk(rtd);
/* mclk from the quirk is the first choice */
if (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ) {
- if (clk_freq != 24000000)
+ if (pll_in != 24000000)
dev_warn(rtd->dev, "configure wrong mclk in tplg, please use 24MHz.\n");
- clk_freq = 24000000;
- } else if (clk_freq == 0) {
+ pll_in = 24000000;
+ } else if (pll_in == 0) {
/* use default mclk if not specified correct in topology */
- clk_freq = 19200000;
- } else if (clk_freq < 0) {
- return clk_freq;
+ pll_in = 19200000;
+ } else if (pll_in < 0) {
+ return pll_in;
}
} else {
- clk_id = RT5682_PLL1_S_BCLK1;
- clk_freq = params_rate(params) * 50;
+ if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT)
+ pll_source = RT5682S_PLL_S_BCLK1;
+ else
+ pll_source = RT5682_PLL1_S_BCLK1;
+
+ pll_in = params_rate(params) * 50;
+ }
+
+ if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) {
+ pll_id = RT5682S_PLL2;
+ clk_id = RT5682S_SCLK_S_PLL2;
+ } else {
+ pll_id = RT5682_PLL1;
+ clk_id = RT5682_SCLK_S_PLL1;
}
pll_out = params_rate(params) * 512;
- ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, pll_in,
+ pll_out);
if (ret < 0)
dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret);
/* Configure sysclk for codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id,
pll_out, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
@@ -560,6 +587,13 @@ static struct snd_soc_dai_link_component rt5682_component[] = {
}
};
+static struct snd_soc_dai_link_component rt5682s_component[] = {
+ {
+ .name = "i2c-RTL5682:00",
+ .dai_name = "rt5682s-aif1",
+ }
+};
+
static struct snd_soc_dai_link_component dmic_component[] = {
{
.name = "dmic-codec",
@@ -610,8 +644,13 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
goto devm_err;
links[id].id = id;
- links[id].codecs = rt5682_component;
- links[id].num_codecs = ARRAY_SIZE(rt5682_component);
+ if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) {
+ links[id].codecs = rt5682s_component;
+ links[id].num_codecs = ARRAY_SIZE(rt5682s_component);
+ } else {
+ links[id].codecs = rt5682_component;
+ links[id].num_codecs = ARRAY_SIZE(rt5682_component);
+ }
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].init = sof_rt5682_codec_init;
@@ -825,6 +864,10 @@ static int sof_audio_probe(struct platform_device *pdev)
if ((sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data)
sof_rt5682_quirk &= ~SOF_SPEAKER_AMP_PRESENT;
+ /* Detect the headset codec variant */
+ if (acpi_dev_present("RTL5682", NULL, -1))
+ sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT;
+
if (soc_intel_is_byt() || soc_intel_is_cht()) {
is_legacy_cpu = 1;
dmic_be_num = 0;
@@ -920,7 +963,7 @@ static const struct platform_device_id board_ids[] = {
.name = "sof_rt5682",
},
{
- .name = "tgl_mx98357a_rt5682",
+ .name = "tgl_mx98357_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
@@ -950,7 +993,7 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
- .name = "jsl_rt5682_mx98360a",
+ .name = "jsl_rt5682_mx98360",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_MCLK_24MHZ |
SOF_RT5682_SSP_CODEC(0) |
@@ -1000,13 +1043,24 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
- .name = "adl_mx98357a_rt5682",
+ .name = "adl_mx98357_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(2) |
SOF_RT5682_NUM_HDMIDEV(4)),
},
+ {
+ .name = "adl_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),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 6b06248a9327..f10496206cee 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -213,6 +213,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2 |
+ SOF_RT715_DAI_ID_FIX),
+ },
/* AlderLake devices */
{
.callback = sof_sdw_quirk_cb,
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
index a0f6a69c7038..06f503452aa5 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -280,9 +280,19 @@ static const struct snd_soc_acpi_codecs adl_max98357a_amp = {
.codecs = {"MX98357A"}
};
+static const struct snd_soc_acpi_codecs adl_max98360a_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
{
- .id = "10EC5682",
+ .comp_ids = &adl_rt5682_rt5682s_hp,
.drv_name = "adl_mx98373_rt5682",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98373_amp,
@@ -290,13 +300,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
.sof_tplg_filename = "sof-adl-max98373-rt5682.tplg",
},
{
- .id = "10EC5682",
- .drv_name = "adl_mx98357a_rt5682",
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_mx98357_rt5682",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98357a_amp,
.sof_fw_filename = "sof-adl.ri",
.sof_tplg_filename = "sof-adl-max98357a-rt5682.tplg",
},
+ {
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_mx98360_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98360a_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
index 576407b5daf2..342d34052204 100644
--- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
@@ -41,7 +41,7 @@ static struct snd_soc_acpi_mach *apl_quirk(void *arg)
return mach;
}
-static struct snd_soc_acpi_codecs bxt_codecs = {
+static const struct snd_soc_acpi_codecs bxt_codecs = {
.num_codecs = 1,
.codecs = {"MX98357A"}
};
@@ -82,6 +82,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = {
.sof_fw_filename = "sof-apl.ri",
.sof_tplg_filename = "sof-apl-tdf8532.tplg",
},
+ {
+ .id = "ESSX8336",
+ .drv_name = "sof-essx8336",
+ .sof_fw_filename = "sof-apl.ri",
+ .sof_tplg_filename = "sof-apl-es8336.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
index 510a5f38b7f1..142000991813 100644
--- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
@@ -120,9 +120,29 @@ static struct snd_soc_acpi_mach *byt_quirk(void *arg)
}
}
+static const struct snd_soc_acpi_codecs rt5640_comp_ids = {
+ .num_codecs = 3,
+ .codecs = { "10EC5640", "10EC5642", "INTCCFFD"},
+};
+
+static const struct snd_soc_acpi_codecs wm5102_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "WM510204", "WM510205"},
+};
+
+static const struct snd_soc_acpi_codecs da7213_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "DGLS7212", "DGLS7213"},
+};
+
+static const struct snd_soc_acpi_codecs rt5645_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5645", "10EC5648"},
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
{
- .id = "10EC5640",
+ .comp_ids = &rt5640_comp_ids,
.drv_name = "bytcr_rt5640",
.fw_filename = "intel/fw_sst_0f28.bin",
.board = "bytcr_rt5640",
@@ -131,22 +151,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
.sof_tplg_filename = "sof-byt-rt5640.tplg",
},
{
- .id = "10EC5642",
- .drv_name = "bytcr_rt5640",
- .fw_filename = "intel/fw_sst_0f28.bin",
- .board = "bytcr_rt5640",
- .sof_fw_filename = "sof-byt.ri",
- .sof_tplg_filename = "sof-byt-rt5640.tplg",
- },
- {
- .id = "INTCCFFD",
- .drv_name = "bytcr_rt5640",
- .fw_filename = "intel/fw_sst_0f28.bin",
- .board = "bytcr_rt5640",
- .sof_fw_filename = "sof-byt.ri",
- .sof_tplg_filename = "sof-byt-rt5640.tplg",
- },
- {
.id = "10EC5651",
.drv_name = "bytcr_rt5651",
.fw_filename = "intel/fw_sst_0f28.bin",
@@ -155,7 +159,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
.sof_tplg_filename = "sof-byt-rt5651.tplg",
},
{
- .id = "WM510204",
+ .comp_ids = &wm5102_comp_ids,
.drv_name = "bytcr_wm5102",
.fw_filename = "intel/fw_sst_0f28.bin",
.board = "bytcr_wm5102",
@@ -163,23 +167,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
.sof_tplg_filename = "sof-byt-wm5102.tplg",
},
{
- .id = "WM510205",
- .drv_name = "bytcr_wm5102",
- .fw_filename = "intel/fw_sst_0f28.bin",
- .board = "bytcr_wm5102",
- .sof_fw_filename = "sof-byt.ri",
- .sof_tplg_filename = "sof-byt-wm5102.tplg",
- },
- {
- .id = "DLGS7212",
- .drv_name = "bytcht_da7213",
- .fw_filename = "intel/fw_sst_0f28.bin",
- .board = "bytcht_da7213",
- .sof_fw_filename = "sof-byt.ri",
- .sof_tplg_filename = "sof-byt-da7213.tplg",
- },
- {
- .id = "DLGS7213",
+ .comp_ids = &da7213_comp_ids,
.drv_name = "bytcht_da7213",
.fw_filename = "intel/fw_sst_0f28.bin",
.board = "bytcht_da7213",
@@ -202,15 +190,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
},
/* some Baytrail platforms rely on RT5645, use CHT machine driver */
{
- .id = "10EC5645",
- .drv_name = "cht-bsw-rt5645",
- .fw_filename = "intel/fw_sst_0f28.bin",
- .board = "cht-bsw",
- .sof_fw_filename = "sof-byt.ri",
- .sof_tplg_filename = "sof-byt-rt5645.tplg",
- },
- {
- .id = "10EC5648",
+ .comp_ids = &rt5645_comp_ids,
.drv_name = "cht-bsw-rt5645",
.fw_filename = "intel/fw_sst_0f28.bin",
.board = "cht-bsw",
diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
index 227424236fd5..c60a5e8e7bc9 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
@@ -51,18 +51,31 @@ static struct snd_soc_acpi_mach *cht_quirk(void *arg)
return mach;
}
+static const struct snd_soc_acpi_codecs rt5640_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5640", "10EC3276" },
+};
+
+static const struct snd_soc_acpi_codecs rt5670_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5670", "10EC5672" },
+};
+
+static const struct snd_soc_acpi_codecs rt5645_comp_ids = {
+ .num_codecs = 3,
+ .codecs = { "10EC5645", "10EC5650", "10EC3270" },
+};
+
+static const struct snd_soc_acpi_codecs da7213_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "DGLS7212", "DGLS7213"},
+
+};
+
/* Cherryview-based platforms: CherryTrail and Braswell */
struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
{
- .id = "10EC5670",
- .drv_name = "cht-bsw-rt5672",
- .fw_filename = "intel/fw_sst_22a8.bin",
- .board = "cht-bsw",
- .sof_fw_filename = "sof-cht.ri",
- .sof_tplg_filename = "sof-cht-rt5670.tplg",
- },
- {
- .id = "10EC5672",
+ .comp_ids = &rt5670_comp_ids,
.drv_name = "cht-bsw-rt5672",
.fw_filename = "intel/fw_sst_22a8.bin",
.board = "cht-bsw",
@@ -70,23 +83,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
.sof_tplg_filename = "sof-cht-rt5670.tplg",
},
{
- .id = "10EC5645",
- .drv_name = "cht-bsw-rt5645",
- .fw_filename = "intel/fw_sst_22a8.bin",
- .board = "cht-bsw",
- .sof_fw_filename = "sof-cht.ri",
- .sof_tplg_filename = "sof-cht-rt5645.tplg",
- },
- {
- .id = "10EC5650",
- .drv_name = "cht-bsw-rt5645",
- .fw_filename = "intel/fw_sst_22a8.bin",
- .board = "cht-bsw",
- .sof_fw_filename = "sof-cht.ri",
- .sof_tplg_filename = "sof-cht-rt5645.tplg",
- },
- {
- .id = "10EC3270",
+ .comp_ids = &rt5645_comp_ids,
.drv_name = "cht-bsw-rt5645",
.fw_filename = "intel/fw_sst_22a8.bin",
.board = "cht-bsw",
@@ -110,15 +107,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
.sof_tplg_filename = "sof-cht-nau8824.tplg",
},
{
- .id = "DLGS7212",
- .drv_name = "bytcht_da7213",
- .fw_filename = "intel/fw_sst_22a8.bin",
- .board = "bytcht_da7213",
- .sof_fw_filename = "sof-cht.ri",
- .sof_tplg_filename = "sof-cht-da7213.tplg",
- },
- {
- .id = "DLGS7213",
+ .comp_ids = &da7213_comp_ids,
.drv_name = "bytcht_da7213",
.fw_filename = "intel/fw_sst_22a8.bin",
.board = "bytcht_da7213",
@@ -135,7 +124,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
},
/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
{
- .id = "10EC5640",
+ .comp_ids = &rt5640_comp_ids,
.drv_name = "bytcr_rt5640",
.fw_filename = "intel/fw_sst_22a8.bin",
.board = "bytcr_rt5640",
@@ -144,14 +133,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
.sof_tplg_filename = "sof-cht-rt5640.tplg",
},
{
- .id = "10EC3276",
- .drv_name = "bytcr_rt5640",
- .fw_filename = "intel/fw_sst_22a8.bin",
- .board = "bytcr_rt5640",
- .sof_fw_filename = "sof-cht.ri",
- .sof_tplg_filename = "sof-cht-rt5640.tplg",
- },
- {
.id = "10EC5682",
.drv_name = "sof_rt5682",
.sof_fw_filename = "sof-cht.ri",
diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
index b591c6fd13fd..b4eb0c97edf1 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
@@ -9,22 +9,22 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
-static struct snd_soc_acpi_codecs rt1011_spk_codecs = {
+static const struct snd_soc_acpi_codecs rt1011_spk_codecs = {
.num_codecs = 1,
.codecs = {"10EC1011"}
};
-static struct snd_soc_acpi_codecs rt1015_spk_codecs = {
+static const struct snd_soc_acpi_codecs rt1015_spk_codecs = {
.num_codecs = 1,
.codecs = {"10EC1015"}
};
-static struct snd_soc_acpi_codecs max98357a_spk_codecs = {
+static const struct snd_soc_acpi_codecs max98357a_spk_codecs = {
.num_codecs = 1,
.codecs = {"MX98357A"}
};
-static struct snd_soc_acpi_codecs max98390_spk_codecs = {
+static const struct snd_soc_acpi_codecs max98390_spk_codecs = {
.num_codecs = 1,
.codecs = {"MX98390"}
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
index da1e151190b4..8492b7e2a945 100644
--- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
@@ -9,7 +9,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
-static struct snd_soc_acpi_codecs glk_codecs = {
+static const struct snd_soc_acpi_codecs glk_codecs = {
.num_codecs = 1,
.codecs = {"MX98357A"}
};
@@ -41,6 +41,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
.sof_tplg_filename = "sof-glk-rt5682.tplg",
},
{
+ .id = "RTL5682",
+ .drv_name = "glk_rt5682_max98357a",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &glk_codecs,
+ .sof_fw_filename = "sof-glk.ri",
+ .sof_tplg_filename = "sof-glk-rt5682.tplg",
+ },
+ {
.id = "10134242",
.drv_name = "glk_cs4242_mx98357a",
.fw_filename = "intel/dsp_fw_glk.bin",
@@ -49,7 +57,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
.sof_fw_filename = "sof-glk.ri",
.sof_tplg_filename = "sof-glk-cs42l42.tplg",
},
-
+ {
+ .id = "ESSX8336",
+ .drv_name = "sof-essx8336",
+ .sof_fw_filename = "sof-glk.ri",
+ .sof_tplg_filename = "sof-glk-es8336.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
index 69ff7286d357..278ec196da7b 100644
--- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
@@ -9,26 +9,31 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
-static struct snd_soc_acpi_codecs jsl_7219_98373_codecs = {
+static const struct snd_soc_acpi_codecs jsl_7219_98373_codecs = {
.num_codecs = 1,
.codecs = {"MX98373"}
};
-static struct snd_soc_acpi_codecs rt1015_spk = {
+static const struct snd_soc_acpi_codecs rt1015_spk = {
.num_codecs = 1,
.codecs = {"10EC1015"}
};
-static struct snd_soc_acpi_codecs rt1015p_spk = {
+static const struct snd_soc_acpi_codecs rt1015p_spk = {
.num_codecs = 1,
.codecs = {"RTL1015"}
};
-static struct snd_soc_acpi_codecs mx98360a_spk = {
+static const struct snd_soc_acpi_codecs mx98360a_spk = {
.num_codecs = 1,
.codecs = {"MX98360A"}
};
+static const struct snd_soc_acpi_codecs rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
/*
* When adding new entry to the snd_soc_acpi_intel_jsl_machines array,
* use .quirk_data member to distinguish different machine driver,
@@ -50,7 +55,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
.sof_tplg_filename = "sof-jsl-da7219-mx98360a.tplg",
},
{
- .id = "10EC5682",
+ .comp_ids = &rt5682_rt5682s_hp,
.drv_name = "jsl_rt5682_rt1015",
.sof_fw_filename = "sof-jsl.ri",
.machine_quirk = snd_soc_acpi_codec_list,
@@ -58,7 +63,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
.sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
},
{
- .id = "10EC5682",
+ .comp_ids = &rt5682_rt5682s_hp,
.drv_name = "jsl_rt5682_rt1015p",
.sof_fw_filename = "sof-jsl.ri",
.machine_quirk = snd_soc_acpi_codec_list,
@@ -66,8 +71,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
.sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
},
{
- .id = "10EC5682",
- .drv_name = "jsl_rt5682_mx98360a",
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682_mx98360",
.sof_fw_filename = "sof-jsl.ri",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &mx98360a_spk,
@@ -81,6 +86,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
.quirk_data = &mx98360a_spk,
.sof_tplg_filename = "sof-jsl-cs42l42-mx98360a.tplg",
},
+ {
+ .id = "ESSX8336",
+ .drv_name = "sof-essx8336",
+ .sof_fw_filename = "sof-jsl.ri",
+ .sof_tplg_filename = "sof-jsl-es8336.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
index 741bf2f9e081..4e817f559d38 100644
--- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
@@ -12,32 +12,32 @@
static struct skl_machine_pdata skl_dmic_data;
-static struct snd_soc_acpi_codecs kbl_codecs = {
+static const struct snd_soc_acpi_codecs kbl_codecs = {
.num_codecs = 1,
.codecs = {"10508825"}
};
-static struct snd_soc_acpi_codecs kbl_poppy_codecs = {
+static const struct snd_soc_acpi_codecs kbl_poppy_codecs = {
.num_codecs = 1,
.codecs = {"10EC5663"}
};
-static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = {
+static const struct snd_soc_acpi_codecs kbl_5663_5514_codecs = {
.num_codecs = 2,
.codecs = {"10EC5663", "10EC5514"}
};
-static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = {
+static const struct snd_soc_acpi_codecs kbl_7219_98357_codecs = {
.num_codecs = 1,
.codecs = {"MX98357A"}
};
-static struct snd_soc_acpi_codecs kbl_7219_98927_codecs = {
+static const struct snd_soc_acpi_codecs kbl_7219_98927_codecs = {
.num_codecs = 1,
.codecs = {"MX98927"}
};
-static struct snd_soc_acpi_codecs kbl_7219_98373_codecs = {
+static const struct snd_soc_acpi_codecs kbl_7219_98373_codecs = {
.num_codecs = 1,
.codecs = {"MX98373"}
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c
index 961df8d6b5e4..75302e956742 100644
--- a/sound/soc/intel/common/soc-acpi-intel-skl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c
@@ -12,7 +12,7 @@
static struct skl_machine_pdata skl_dmic_data;
-static struct snd_soc_acpi_codecs skl_codecs = {
+static const struct snd_soc_acpi_codecs skl_codecs = {
.num_codecs = 1,
.codecs = {"10508825"}
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
index 785d5f5f8a9c..da31bb3cca17 100644
--- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
@@ -156,6 +156,15 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = {
+ {
+ .adr = 0x000131025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
{
.adr = 0x000131025D131601ull, /* unique ID is set for some reason */
@@ -320,6 +329,25 @@ static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca[] = {
{}
};
+static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca_mono[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_single_adr),
+ .adr_d = rt1316_1_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_codecs tgl_max98373_amp = {
.num_codecs = 1,
.codecs = {"MX98373"}
@@ -330,17 +358,22 @@ static const struct snd_soc_acpi_codecs tgl_rt1011_amp = {
.codecs = {"10EC1011"}
};
+static const struct snd_soc_acpi_codecs tgl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
{
- .id = "10EC5682",
- .drv_name = "tgl_mx98357a_rt5682",
+ .comp_ids = &tgl_rt5682_rt5682s_hp,
+ .drv_name = "tgl_mx98357_rt5682",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &tgl_codecs,
.sof_fw_filename = "sof-tgl.ri",
.sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg",
},
{
- .id = "10EC5682",
+ .comp_ids = &tgl_rt5682_rt5682s_hp,
.drv_name = "tgl_mx98373_rt5682",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &tgl_max98373_amp,
@@ -348,13 +381,19 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
.sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg",
},
{
- .id = "10EC5682",
+ .comp_ids = &tgl_rt5682_rt5682s_hp,
.drv_name = "tgl_rt1011_rt5682",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &tgl_rt1011_amp,
.sof_fw_filename = "sof-tgl.ri",
.sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg",
},
+ {
+ .id = "ESSX8336",
+ .drv_name = "sof-essx8336",
+ .sof_fw_filename = "sof-tgl.ri",
+ .sof_tplg_filename = "sof-tgl-es8336.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines);
@@ -413,6 +452,19 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
.sof_tplg_filename = "sof-tgl-rt711-rt1316-rt714.tplg",
},
{
+ /*
+ * link_mask should be 0xB, but all links are enabled by BIOS.
+ * This entry will be selected if there is no rt1316 amplifier exposed
+ * on link2 since it will fail to match the above entry.
+ */
+
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_3_in_1_sdca_mono,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-l0-rt1316-l1-mono-rt714-l3.tplg",
+ },
+
+ {
.link_mask = 0x3, /* rt711 on link 0 and 1 rt1308 on link 1 */
.links = tgl_hp,
.drv_name = "sof_sdw",
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index b036852d6889..89e4231304dd 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -3637,7 +3637,7 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index,
return 0;
}
-static void skl_tplg_complete(struct snd_soc_component *component)
+static int skl_tplg_complete(struct snd_soc_component *component)
{
struct snd_soc_dobj *dobj;
struct snd_soc_acpi_mach *mach;
@@ -3646,7 +3646,7 @@ static void skl_tplg_complete(struct snd_soc_component *component)
val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
- return;
+ return -ENOMEM;
mach = dev_get_platdata(component->card->dev);
list_for_each_entry(dobj, &component->dobj_list, list) {
@@ -3671,7 +3671,9 @@ static void skl_tplg_complete(struct snd_soc_component *component)
}
}
}
+
kfree(val);
+ return 0;
}
static struct snd_soc_tplg_ops skl_tplg_ops = {
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 81ad2dcee9eb..3b1ddea26a9e 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -120,7 +120,7 @@ config SND_SOC_MT8183
config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A
tristate "ASoC Audio driver for MT8183 with MT6358 TS3A227E MAX98357A RT1015 codec"
- depends on I2C
+ depends on I2C && GPIOLIB
depends on SND_SOC_MT8183
select SND_SOC_MT6358
select SND_SOC_MAX98357A
@@ -138,7 +138,7 @@ config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A
config SND_SOC_MT8183_DA7219_MAX98357A
tristate "ASoC Audio driver for MT8183 with DA7219 MAX98357A RT1015 codec"
- depends on SND_SOC_MT8183 && I2C
+ depends on SND_SOC_MT8183 && I2C && GPIOLIB
select SND_SOC_MT6358
select SND_SOC_MAX98357A
select SND_SOC_RT1015
@@ -173,7 +173,7 @@ config SND_SOC_MT8192
config SND_SOC_MT8192_MT6359_RT1015_RT5682
tristate "ASoC Audio driver for MT8192 with MT6359 RT1015 RT5682 codec"
- depends on I2C
+ depends on I2C && GPIOLIB
depends on SND_SOC_MT8192 && MTK_PMIC_WRAP
select SND_SOC_MT6359
select SND_SOC_RT1015
@@ -200,7 +200,7 @@ config SND_SOC_MT8195
config SND_SOC_MT8195_MT6359_RT1019_RT5682
tristate "ASoC Audio driver for MT8195 with MT6359 RT1019 RT5682 codec"
- depends on I2C
+ depends on I2C && GPIOLIB
depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
select SND_SOC_MT6359
select SND_SOC_RT1015P
@@ -212,3 +212,18 @@ config SND_SOC_MT8195_MT6359_RT1019_RT5682
with the MT6359 RT1019 RT5682 audio codec.
Select Y if you have such device.
If unsure select "N".
+
+config SND_SOC_MT8195_MT6359_RT1011_RT5682
+ tristate "ASoC Audio driver for MT8195 with MT6359 RT1011 RT5682 codec"
+ depends on I2C
+ depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
+ select SND_SOC_MT6359
+ select SND_SOC_RT1011
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds ASoC driver for Mediatek MT8195 boards
+ with the MT6359 RT1011 RT5682 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
index e95c7c018e7d..395be97f13ae 100644
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -288,7 +288,6 @@ const struct snd_soc_dai_ops mtk_afe_fe_ops = {
};
EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
-static DEFINE_MUTEX(irqs_lock);
int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
{
int i;
@@ -351,7 +350,7 @@ int mtk_afe_resume(struct snd_soc_component *component)
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
struct device *dev = afe->dev;
struct regmap *regmap = afe->regmap;
- int i = 0;
+ int i;
if (pm_runtime_status_suspended(dev) || !afe->suspended)
return 0;
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
index 44a8d5cfb0aa..d9fd6eb786aa 100644
--- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -146,7 +146,7 @@ static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt2701_cs42448_be_ops = {
+static const struct snd_soc_ops mt2701_cs42448_be_ops = {
.hw_params = mt2701_cs42448_be_ops_hw_params
};
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
index 414e422c0eba..f56de1b918bf 100644
--- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -40,7 +40,7 @@ static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt2701_wm8960_be_ops = {
+static const struct snd_soc_ops mt2701_wm8960_be_ops = {
.hw_params = mt2701_wm8960_be_ops_hw_params
};
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index 94dcbd36c869..aeb1af86047e 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -335,7 +335,7 @@ static void mt8183_mt6358_tdm_shutdown(struct snd_pcm_substream *substream)
__func__, ret);
}
-static struct snd_soc_ops mt8183_mt6358_tdm_ops = {
+static const struct snd_soc_ops mt8183_mt6358_tdm_ops = {
.startup = mt8183_mt6358_tdm_startup,
.shutdown = mt8183_mt6358_tdm_shutdown,
};
diff --git a/sound/soc/mediatek/mt8195/Makefile b/sound/soc/mediatek/mt8195/Makefile
index 44775f400b40..e5f0df5010b6 100644
--- a/sound/soc/mediatek/mt8195/Makefile
+++ b/sound/soc/mediatek/mt8195/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o
# machine driver
obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1019_RT5682) += mt8195-mt6359-rt1019-rt5682.o
+obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1011_RT5682) += mt8195-mt6359-rt1011-rt5682.o
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
index 6635c3f72ecc..2bb05a828e8d 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -2232,7 +2232,7 @@ static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = {
},
};
-static const struct mtk_base_irq_data irq_data[MT8195_AFE_IRQ_NUM] = {
+static const struct mtk_base_irq_data irq_data_array[MT8195_AFE_IRQ_NUM] = {
[MT8195_AFE_IRQ_1] = {
.id = MT8195_AFE_IRQ_1,
.irq_cnt_reg = -1,
@@ -3057,7 +3057,6 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8195_afe_private *afe_priv;
- struct resource *res;
struct device *dev = &pdev->dev;
int i, irq_id, ret;
struct snd_soc_component *component;
@@ -3078,8 +3077,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
afe_priv = afe->platform_priv;
afe->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
@@ -3102,7 +3100,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < afe->irqs_size; i++)
- afe->irqs[i].irq_data = &irq_data[i];
+ afe->irqs[i].irq_data = &irq_data_array[i];
/* init memif */
afe->memif_size = MT8195_AFE_MEMIF_NUM;
@@ -3266,9 +3264,7 @@ static struct platform_driver mt8195_afe_pcm_driver = {
.driver = {
.name = "mt8195-audio",
.of_match_table = mt8195_afe_pcm_dt_match,
-#ifdef CONFIG_PM
.pm = &mt8195_afe_pm_ops,
-#endif
},
.probe = mt8195_afe_pcm_dev_probe,
.remove = mt8195_afe_pcm_dev_remove,
diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
index 740aa6ddda0e..e0670e0dbd5b 100644
--- a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
+++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
@@ -59,93 +59,93 @@ struct afe_gate {
static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = {
/* AUD0 */
- GATE_AUD0(CLK_AUD_AFE, "aud_afe", "a1sys_hp_sel", 2),
- GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "a1sys_hp_sel", 4),
- GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "apll4_sel", 10),
- GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "apll4_sel", 11),
- GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "a1sys_hp_sel", 18),
- GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "apll1_sel", 19),
- GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "apll2_sel", 20),
- GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "aud_iec_sel", 21),
- GATE_AUD0(CLK_AUD_APLL, "aud_apll", "apll1_sel", 23),
- GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "apll2_sel", 24),
- GATE_AUD0(CLK_AUD_DAC, "aud_dac", "a1sys_hp_sel", 25),
- GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "a1sys_hp_sel", 26),
- GATE_AUD0(CLK_AUD_TML, "aud_tml", "a1sys_hp_sel", 27),
- GATE_AUD0(CLK_AUD_ADC, "aud_adc", "a1sys_hp_sel", 28),
- GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "audio_h_sel", 31),
+ GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2),
+ GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11),
+ GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18),
+ GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19),
+ GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20),
+ GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21),
+ GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23),
+ GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24),
+ GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25),
+ GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26),
+ GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27),
+ GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28),
+ GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31),
/* AUD1 */
- GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "a1sys_hp_sel", 2),
- GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "a1sys_hp_sel", 10),
- GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "a1sys_hp_sel", 11),
- GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "a1sys_hp_sel", 12),
- GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "a1sys_hp_sel", 13),
- GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "a1sys_hp_sel", 14),
- GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "audio_h_sel", 16),
- GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "audio_h_sel", 17),
- GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "a1sys_hp_sel", 18),
- GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "audio_h_sel", 19),
+ GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2),
+ GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10),
+ GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11),
+ GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12),
+ GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13),
+ GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14),
+ GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16),
+ GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "top_a1sys_hp", 18),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "top_audio_h", 19),
/* AUD3 */
- GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "apll5_sel", 5),
- GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "apll3_sel", 7),
+ GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5),
+ GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7),
/* AUD4 */
- GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "a1sys_hp_sel", 0),
- GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "a1sys_hp_sel", 1),
- GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "a1sys_hp_sel", 6),
- GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "a1sys_hp_sel", 7),
- GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "a1sys_hp_sel", 8),
- GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "a1sys_hp_sel", 16),
- GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "a1sys_hp_sel", 17),
+ GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0),
+ GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1),
+ GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6),
+ GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7),
+ GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8),
+ GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16),
+ GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17),
GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19),
- GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "intdir_sel", 20),
- GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "a1sys_hp_sel", 21),
- GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "a2sys_sel", 22),
- GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "a1sys_hp_sel", 24),
- GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "a3sys_sel", 30),
- GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "a4sys_sel", 31),
+ GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20),
+ GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21),
+ GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys_hf", 22),
+ GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24),
+ GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys_hf", 30),
+ GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys_hf", 31),
/* AUD5 */
- GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "a1sys_hp_sel", 0),
- GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "a1sys_hp_sel", 1),
- GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "a1sys_hp_sel", 2),
- GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "a1sys_hp_sel", 3),
- GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "a1sys_hp_sel", 4),
- GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "a1sys_hp_sel", 5),
- GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "a1sys_hp_sel", 7),
- GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "a1sys_hp_sel", 8),
- GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "a1sys_hp_sel", 9),
- GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "a1sys_hp_sel", 18),
- GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "a1sys_hp_sel", 19),
- GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "a1sys_hp_sel", 22),
- GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "a1sys_hp_sel", 23),
- GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "a1sys_hp_sel", 24),
- GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "a1sys_hp_sel", 26),
- GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "a1sys_hp_sel", 27),
+ GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0),
+ GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1),
+ GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2),
+ GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3),
+ GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4),
+ GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5),
+ GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7),
+ GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8),
+ GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9),
+ GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18),
+ GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19),
+ GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22),
+ GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23),
+ GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24),
+ GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26),
+ GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27),
/* AUD6 */
- GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "asm_h_sel", 0),
- GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "asm_h_sel", 1),
- GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "asm_h_sel", 2),
- GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "asm_h_sel", 3),
- GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "asm_h_sel", 4),
- GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "asm_h_sel", 5),
- GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "asm_h_sel", 6),
- GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "asm_h_sel", 7),
- GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "asm_h_sel", 8),
- GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "asm_h_sel", 9),
- GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "asm_h_sel", 10),
- GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "asm_h_sel", 11),
- GATE_AUD6(CLK_AUD_GASRC12, "aud_gasrc12", "asm_h_sel", 12),
- GATE_AUD6(CLK_AUD_GASRC13, "aud_gasrc13", "asm_h_sel", 13),
- GATE_AUD6(CLK_AUD_GASRC14, "aud_gasrc14", "asm_h_sel", 14),
- GATE_AUD6(CLK_AUD_GASRC15, "aud_gasrc15", "asm_h_sel", 15),
- GATE_AUD6(CLK_AUD_GASRC16, "aud_gasrc16", "asm_h_sel", 16),
- GATE_AUD6(CLK_AUD_GASRC17, "aud_gasrc17", "asm_h_sel", 17),
- GATE_AUD6(CLK_AUD_GASRC18, "aud_gasrc18", "asm_h_sel", 18),
- GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "asm_h_sel", 19),
+ GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0),
+ GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1),
+ GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2),
+ GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3),
+ GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4),
+ GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5),
+ GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6),
+ GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7),
+ GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8),
+ GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9),
+ GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10),
+ GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11),
+ GATE_AUD6(CLK_AUD_GASRC12, "aud_gasrc12", "top_asm_h", 12),
+ GATE_AUD6(CLK_AUD_GASRC13, "aud_gasrc13", "top_asm_h", 13),
+ GATE_AUD6(CLK_AUD_GASRC14, "aud_gasrc14", "top_asm_h", 14),
+ GATE_AUD6(CLK_AUD_GASRC15, "aud_gasrc15", "top_asm_h", 15),
+ GATE_AUD6(CLK_AUD_GASRC16, "aud_gasrc16", "top_asm_h", 16),
+ GATE_AUD6(CLK_AUD_GASRC17, "aud_gasrc17", "top_asm_h", 17),
+ GATE_AUD6(CLK_AUD_GASRC18, "aud_gasrc18", "top_asm_h", 18),
+ GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "top_asm_h", 19),
};
int mt8195_audsys_clk_register(struct mtk_base_afe *afe)
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-adda.c b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c
index 878dec0b69ed..f04bd1781356 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-adda.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c
@@ -788,9 +788,11 @@ static int init_adda_priv_data(struct mtk_base_afe *afe)
{
struct mt8195_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv;
- int adda_dai_list[] = { MT8195_AFE_IO_DL_SRC,
- MT8195_AFE_IO_UL_SRC1,
- MT8195_AFE_IO_UL_SRC2};
+ static const int adda_dai_list[] = {
+ MT8195_AFE_IO_DL_SRC,
+ MT8195_AFE_IO_UL_SRC1,
+ MT8195_AFE_IO_UL_SRC2
+ };
int i;
for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
index 7378e42f2766..c02c10da3600 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
@@ -1316,7 +1316,7 @@ static int mt8195_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id)
}
out:
spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
- return 0;
+ return ret;
}
static int etdm_cowork_slv_sel(int id, int slave_mode)
@@ -2094,7 +2094,7 @@ static int mtk_dai_etdm_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;
int dai_id;
dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
new file mode 100644
index 000000000000..e103102d7ef6
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
@@ -0,0 +1,1155 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8195-mt6359-rt1011-rt5682.c --
+// MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver
+//
+// Copyright (c) 2021 MediaTek Inc.
+// Author: Trevor Wu <trevor.wu@mediatek.com>
+//
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/rt5682.h>
+#include <sound/soc.h>
+#include "../../codecs/mt6359.h"
+#include "../../codecs/rt1011.h"
+#include "../../codecs/rt5682.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "mt8195-afe-common.h"
+
+#define RT1011_CODEC_DAI "rt1011-aif"
+#define RT1011_DEV0_NAME "rt1011.2-0038"
+#define RT1011_DEV1_NAME "rt1011.2-0039"
+
+#define RT5682_CODEC_DAI "rt5682-aif1"
+#define RT5682_DEV0_NAME "rt5682.2-001a"
+
+struct mt8195_mt6359_rt1011_rt5682_priv {
+ struct device_node *platform_node;
+ struct device_node *hdmi_node;
+ struct device_node *dp_node;
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack dp_jack;
+ struct snd_soc_jack hdmi_jack;
+};
+
+static const struct snd_soc_dapm_widget
+mt8195_mt6359_rt1011_rt5682_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Speaker", NULL),
+ SND_SOC_DAPM_SPK("Right Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8195_mt6359_rt1011_rt5682_routes[] = {
+ /* speaker */
+ { "Left Speaker", NULL, "Left SPO" },
+ { "Right Speaker", NULL, "Right SPO" },
+ /* headset */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static const struct snd_kcontrol_new mt8195_mt6359_rt1011_rt5682_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Speaker"),
+ SOC_DAPM_PIN_SWITCH("Right Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_BCLK1,
+ rate * 64, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 128,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
+ .hw_params = mt8195_rt5682_etdm_hw_params,
+};
+
+static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ int srate, i, ret = 0;
+
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
+ .hw_params = mt8195_rt1011_etdm_hw_params,
+};
+
+#define CKSYS_AUD_TOP_CFG 0x032c
+#define CKSYS_AUD_TOP_MON 0x0330
+
+static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int chosen_phase_1, chosen_phase_2, chosen_phase_3;
+ int prev_cycle_1, prev_cycle_2, prev_cycle_3;
+ int test_done_1, test_done_2, test_done_3;
+ int cycle_1, cycle_2, cycle_3;
+ int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_calibration_num_phase;
+ bool mtkaif_calibration_ok;
+ unsigned int monitor;
+ int counter;
+ int phase;
+ int i;
+
+ dev_dbg(afe->dev, "%s(), start\n", __func__);
+
+ param->mtkaif_calibration_ok = false;
+ for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
+ param->mtkaif_chosen_phase[i] = -1;
+ param->mtkaif_phase_cycle[i] = 0;
+ mtkaif_chosen_phase[i] = -1;
+ mtkaif_phase_cycle[i] = 0;
+ }
+
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
+ __func__);
+ return 0;
+ }
+
+ pm_runtime_get_sync(afe->dev);
+ mt6359_mtkaif_calibration_enable(cmpnt_codec);
+
+ /* set test type to synchronizer pulse */
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
+ mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
+ mtkaif_calibration_ok = true;
+
+ for (phase = 0;
+ phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
+ phase++) {
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ phase, phase, phase);
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x1);
+
+ test_done_1 = 0;
+ test_done_2 = 0;
+ test_done_3 = 0;
+ cycle_1 = -1;
+ cycle_2 = -1;
+ cycle_3 = -1;
+ counter = 0;
+ while (!(test_done_1 & test_done_2 & test_done_3)) {
+ regmap_read(afe_priv->topckgen,
+ CKSYS_AUD_TOP_MON, &monitor);
+ test_done_1 = (monitor >> 28) & 0x1;
+ test_done_2 = (monitor >> 29) & 0x1;
+ test_done_3 = (monitor >> 30) & 0x1;
+ if (test_done_1 == 1)
+ cycle_1 = monitor & 0xf;
+
+ if (test_done_2 == 1)
+ cycle_2 = (monitor >> 4) & 0xf;
+
+ if (test_done_3 == 1)
+ cycle_3 = (monitor >> 8) & 0xf;
+
+ /* handle if never test done */
+ if (++counter > 10000) {
+ dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n",
+ __func__,
+ cycle_1, cycle_2, cycle_3, monitor);
+ mtkaif_calibration_ok = false;
+ break;
+ }
+ }
+
+ if (phase == 0) {
+ prev_cycle_1 = cycle_1;
+ prev_cycle_2 = cycle_2;
+ prev_cycle_3 = cycle_3;
+ }
+
+ if (cycle_1 != prev_cycle_1 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] = prev_cycle_1;
+ }
+
+ if (cycle_2 != prev_cycle_2 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] = prev_cycle_2;
+ }
+
+ if (cycle_3 != prev_cycle_3 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] = prev_cycle_3;
+ }
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x0);
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] >= 0 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] >= 0 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] >= 0)
+ break;
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_1 = 0;
+ } else {
+ chosen_phase_1 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0];
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_2 = 0;
+ } else {
+ chosen_phase_2 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1];
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_3 = 0;
+ } else {
+ chosen_phase_3 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2];
+ }
+
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ chosen_phase_1,
+ chosen_phase_2,
+ chosen_phase_3);
+
+ mt6359_mtkaif_calibration_disable(cmpnt_codec);
+ pm_runtime_put(afe->dev);
+
+ param->mtkaif_calibration_ok = mtkaif_calibration_ok;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = chosen_phase_1;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = chosen_phase_2;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = chosen_phase_3;
+ for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++)
+ param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
+
+ dev_info(afe->dev, "%s(), end, calibration ok %d\n",
+ __func__, param->mtkaif_calibration_ok);
+
+ return 0;
+}
+
+static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+
+ /* set mtkaif protocol */
+ mt6359_set_mtkaif_protocol(cmpnt_codec,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
+
+ /* mtkaif calibration */
+ mt8195_mt6359_mtkaif_calibration(rtd);
+
+ return 0;
+}
+
+static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &priv->headset_jack;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, NULL, 0);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000
+ };
+ static const unsigned int channels[] = {
+ 2, 4, 6, 8
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list rate failed\n");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list channel failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_hdmitx_dptx_playback_ops = {
+ .startup = mt8195_hdmitx_dptx_startup,
+};
+
+static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
+ SND_SOC_CLOCK_OUT);
+}
+
+static struct snd_soc_ops mt8195_dptx_ops = {
+ .hw_params = mt8195_dptx_hw_params,
+};
+
+static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+ &priv->dp_jack, NULL, 0);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(cmpnt_codec, &priv->dp_jack, NULL);
+}
+
+static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ &priv->hdmi_jack, NULL, 0);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(cmpnt_codec, &priv->hdmi_jack, NULL);
+}
+
+static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+
+{
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int mt8195_playback_startup(struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000
+ };
+ static const unsigned int channels[] = {
+ 2
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list rate failed\n");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list channel failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_playback_ops = {
+ .startup = mt8195_playback_startup,
+};
+
+static int mt8195_capture_startup(struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000
+ };
+ static const unsigned int channels[] = {
+ 1, 2
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list rate failed\n");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list channel failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_capture_ops = {
+ .startup = mt8195_capture_startup,
+};
+
+enum {
+ DAI_LINK_DL2_FE,
+ DAI_LINK_DL3_FE,
+ DAI_LINK_DL6_FE,
+ DAI_LINK_DL7_FE,
+ DAI_LINK_DL8_FE,
+ DAI_LINK_DL10_FE,
+ DAI_LINK_DL11_FE,
+ DAI_LINK_UL1_FE,
+ DAI_LINK_UL2_FE,
+ DAI_LINK_UL3_FE,
+ DAI_LINK_UL4_FE,
+ DAI_LINK_UL5_FE,
+ DAI_LINK_UL6_FE,
+ DAI_LINK_UL8_FE,
+ DAI_LINK_UL9_FE,
+ DAI_LINK_UL10_FE,
+ DAI_LINK_DL_SRC_BE,
+ DAI_LINK_DPTX_BE,
+ DAI_LINK_ETDM1_IN_BE,
+ DAI_LINK_ETDM2_IN_BE,
+ DAI_LINK_ETDM1_OUT_BE,
+ DAI_LINK_ETDM2_OUT_BE,
+ DAI_LINK_ETDM3_OUT_BE,
+ DAI_LINK_PCM1_BE,
+ DAI_LINK_UL_SRC1_BE,
+ DAI_LINK_UL_SRC2_BE,
+};
+
+/* FE */
+SND_SOC_DAILINK_DEFS(DL2_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL3_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL6_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL7_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL8_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL10_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL11_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL1_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL2_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL3_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL4_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL5_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL6_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL8_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL9_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL10_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(DL_SRC_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DPTX_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
+ RT5682_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
+ RT5682_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
+ RT1011_CODEC_DAI),
+ COMP_CODEC(RT1011_DEV1_NAME,
+ RT1011_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(PCM1_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL_SRC1_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1"),
+ COMP_CODEC("dmic-codec",
+ "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL_SRC2_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif2")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = {
+ /* FE */
+ [DAI_LINK_DL2_FE] = {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_playback_ops,
+ SND_SOC_DAILINK_REG(DL2_FE),
+ },
+ [DAI_LINK_DL3_FE] = {
+ .name = "DL3_FE",
+ .stream_name = "DL3 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_playback_ops,
+ SND_SOC_DAILINK_REG(DL3_FE),
+ },
+ [DAI_LINK_DL6_FE] = {
+ .name = "DL6_FE",
+ .stream_name = "DL6 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_playback_ops,
+ SND_SOC_DAILINK_REG(DL6_FE),
+ },
+ [DAI_LINK_DL7_FE] = {
+ .name = "DL7_FE",
+ .stream_name = "DL7 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(DL7_FE),
+ },
+ [DAI_LINK_DL8_FE] = {
+ .name = "DL8_FE",
+ .stream_name = "DL8 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_playback_ops,
+ SND_SOC_DAILINK_REG(DL8_FE),
+ },
+ [DAI_LINK_DL10_FE] = {
+ .name = "DL10_FE",
+ .stream_name = "DL10 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_hdmitx_dptx_playback_ops,
+ SND_SOC_DAILINK_REG(DL10_FE),
+ },
+ [DAI_LINK_DL11_FE] = {
+ .name = "DL11_FE",
+ .stream_name = "DL11 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_playback_ops,
+ SND_SOC_DAILINK_REG(DL11_FE),
+ },
+ [DAI_LINK_UL1_FE] = {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(UL1_FE),
+ },
+ [DAI_LINK_UL2_FE] = {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL2_FE),
+ },
+ [DAI_LINK_UL3_FE] = {
+ .name = "UL3_FE",
+ .stream_name = "UL3 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL3_FE),
+ },
+ [DAI_LINK_UL4_FE] = {
+ .name = "UL4_FE",
+ .stream_name = "UL4 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL4_FE),
+ },
+ [DAI_LINK_UL5_FE] = {
+ .name = "UL5_FE",
+ .stream_name = "UL5 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL5_FE),
+ },
+ [DAI_LINK_UL6_FE] = {
+ .name = "UL6_FE",
+ .stream_name = "UL6 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(UL6_FE),
+ },
+ [DAI_LINK_UL8_FE] = {
+ .name = "UL8_FE",
+ .stream_name = "UL8 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL8_FE),
+ },
+ [DAI_LINK_UL9_FE] = {
+ .name = "UL9_FE",
+ .stream_name = "UL9 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL9_FE),
+ },
+ [DAI_LINK_UL10_FE] = {
+ .name = "UL10_FE",
+ .stream_name = "UL10 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .ops = &mt8195_capture_ops,
+ SND_SOC_DAILINK_REG(UL10_FE),
+ },
+ /* BE */
+ [DAI_LINK_DL_SRC_BE] = {
+ .name = "DL_SRC_BE",
+ .init = mt8195_mt6359_init,
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(DL_SRC_BE),
+ },
+ [DAI_LINK_DPTX_BE] = {
+ .name = "DPTX_BE",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ops = &mt8195_dptx_ops,
+ .be_hw_params_fixup = mt8195_dptx_hw_params_fixup,
+ SND_SOC_DAILINK_REG(DPTX_BE),
+ },
+ [DAI_LINK_ETDM1_IN_BE] = {
+ .name = "ETDM1_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(ETDM1_IN_BE),
+ },
+ [DAI_LINK_ETDM2_IN_BE] = {
+ .name = "ETDM2_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_capture = 1,
+ .init = mt8195_rt5682_init,
+ .ops = &mt8195_rt5682_etdm_ops,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM2_IN_BE),
+ },
+ [DAI_LINK_ETDM1_OUT_BE] = {
+ .name = "ETDM1_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
+ .ops = &mt8195_rt5682_etdm_ops,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
+ },
+ [DAI_LINK_ETDM2_OUT_BE] = {
+ .name = "ETDM2_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
+ .ops = &mt8195_rt1011_etdm_ops,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM2_OUT_BE),
+ },
+ [DAI_LINK_ETDM3_OUT_BE] = {
+ .name = "ETDM3_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(ETDM3_OUT_BE),
+ },
+ [DAI_LINK_PCM1_BE] = {
+ .name = "PCM1_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(PCM1_BE),
+ },
+ [DAI_LINK_UL_SRC1_BE] = {
+ .name = "UL_SRC1_BE",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(UL_SRC1_BE),
+ },
+ [DAI_LINK_UL_SRC2_BE] = {
+ .name = "UL_SRC2_BE",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(UL_SRC2_BE),
+ },
+};
+
+static struct snd_soc_codec_conf rt1011_amp_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = {
+ .name = "mt8195_r1011_5682",
+ .owner = THIS_MODULE,
+ .dai_link = mt8195_mt6359_rt1011_rt5682_dai_links,
+ .num_links = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_dai_links),
+ .controls = mt8195_mt6359_rt1011_rt5682_controls,
+ .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_controls),
+ .dapm_widgets = mt8195_mt6359_rt1011_rt5682_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_widgets),
+ .dapm_routes = mt8195_mt6359_rt1011_rt5682_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes),
+ .codec_conf = rt1011_amp_conf,
+ .num_configs = ARRAY_SIZE(rt1011_amp_conf),
+};
+
+static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card;
+ struct snd_soc_dai_link *dai_link;
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!priv->platform_node) {
+ dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = priv->platform_node;
+
+ if (strcmp(dai_link->name, "DPTX_BE") == 0) {
+ priv->dp_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,dptx-codec", 0);
+
+ if (!priv->dp_node) {
+ dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
+ } else {
+ dai_link->codecs->of_node = priv->dp_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name = "i2s-hifi";
+ dai_link->init = mt8195_dptx_codec_init;
+ }
+ }
+
+ if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ priv->hdmi_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,hdmi-codec", 0);
+ if (!priv->hdmi_node) {
+ dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n");
+ } else {
+ dai_link->codecs->of_node = priv->hdmi_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name = "i2s-hifi";
+ dai_link->init = mt8195_hdmi_codec_init;
+ }
+ }
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ of_node_put(priv->hdmi_node);
+ of_node_put(priv->dp_node);
+ of_node_put(priv->platform_node);
+ }
+
+ return ret;
+}
+
+static int mt8195_mt6359_rt1011_rt5682_dev_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(card);
+
+ of_node_put(priv->hdmi_node);
+ of_node_put(priv->dp_node);
+ of_node_put(priv->platform_node);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = {
+ {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",},
+ {}
+};
+#endif
+
+static const struct dev_pm_ops mt8195_mt6359_rt1011_rt5682_pm_ops = {
+ .poweroff = snd_soc_poweroff,
+ .restore = snd_soc_resume,
+};
+
+static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = {
+ .driver = {
+ .name = "mt8195_mt6359_rt1011_rt5682",
+#ifdef CONFIG_OF
+ .of_match_table = mt8195_mt6359_rt1011_rt5682_dt_match,
+#endif
+ .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops,
+ },
+ .probe = mt8195_mt6359_rt1011_rt5682_dev_probe,
+ .remove = mt8195_mt6359_rt1011_rt5682_dev_remove,
+};
+
+module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver");
+MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt8195_mt6359_rt1011_rt5682 soc card");
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
index de09f67c0450..95abaadcd842 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
@@ -26,6 +26,9 @@
#define RT5682_DEV0_NAME "rt5682.2-001a"
struct mt8195_mt6359_rt1019_rt5682_priv {
+ struct device_node *platform_node;
+ struct device_node *hdmi_node;
+ struct device_node *dp_node;
struct snd_soc_jack headset_jack;
struct snd_soc_jack dp_jack;
struct snd_soc_jack hdmi_jack;
@@ -994,31 +997,36 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = {
static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card;
- struct device_node *platform_node;
struct snd_soc_dai_link *dai_link;
- struct mt8195_mt6359_rt1019_rt5682_priv *priv = NULL;
+ struct mt8195_mt6359_rt1019_rt5682_priv *priv;
int ret, i;
card->dev = &pdev->dev;
- platform_node = of_parse_phandle(pdev->dev.of_node,
- "mediatek,platform", 0);
- if (!platform_node) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!priv->platform_node) {
dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
for_each_card_prelinks(card, i, dai_link) {
if (!dai_link->platforms->name)
- dai_link->platforms->of_node = platform_node;
+ dai_link->platforms->of_node = priv->platform_node;
if (strcmp(dai_link->name, "DPTX_BE") == 0) {
- dai_link->codecs->of_node =
+ priv->dp_node =
of_parse_phandle(pdev->dev.of_node,
"mediatek,dptx-codec", 0);
- if (!dai_link->codecs->of_node) {
+
+ if (!priv->dp_node) {
dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
} else {
+ dai_link->codecs->of_node = priv->dp_node;
dai_link->codecs->name = NULL;
dai_link->codecs->dai_name = "i2s-hifi";
dai_link->init = mt8195_dptx_codec_init;
@@ -1026,12 +1034,13 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
}
if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
- dai_link->codecs->of_node =
+ priv->hdmi_node =
of_parse_phandle(pdev->dev.of_node,
"mediatek,hdmi-codec", 0);
- if (!dai_link->codecs->of_node) {
+ if (!priv->hdmi_node) {
dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n");
} else {
+ dai_link->codecs->of_node = priv->hdmi_node;
dai_link->codecs->name = NULL;
dai_link->codecs->dai_name = "i2s-hifi";
dai_link->init = mt8195_hdmi_codec_init;
@@ -1039,19 +1048,33 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
}
}
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
+ of_node_put(priv->hdmi_node);
+ of_node_put(priv->dp_node);
+ of_node_put(priv->platform_node);
+ }
+
return ret;
}
+static int mt8195_mt6359_rt1019_rt5682_dev_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct mt8195_mt6359_rt1019_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(card);
+
+ of_node_put(priv->hdmi_node);
+ of_node_put(priv->dp_node);
+ of_node_put(priv->platform_node);
+
+ return 0;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = {
{.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",},
@@ -1073,6 +1096,7 @@ static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = {
.pm = &mt8195_mt6359_rt1019_rt5682_pm_ops,
},
.probe = mt8195_mt6359_rt1019_rt5682_dev_probe,
+ .remove = mt8195_mt6359_rt1019_rt5682_dev_remove,
};
module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver);
diff --git a/sound/soc/meson/aiu-encoder-spdif.c b/sound/soc/meson/aiu-encoder-spdif.c
index de850913975f..97da60db2c4d 100644
--- a/sound/soc/meson/aiu-encoder-spdif.c
+++ b/sound/soc/meson/aiu-encoder-spdif.c
@@ -113,7 +113,7 @@ static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
val |= AIU_958_MISC_MODE_32BITS;
break;
default:
- dev_err(dai->dev, "Unsupport physical width\n");
+ dev_err(dai->dev, "Unsupported physical width\n");
return -EINVAL;
}
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index 2b77010c2c5c..cbbaa55d92a6 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -320,6 +320,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->cpus = cpu;
dai_link->num_cpus = 1;
+ dai_link->nonatomic = true;
ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node,
&dai_link->cpus->dai_name);
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index 87cac440b369..db077773af7a 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -351,13 +351,29 @@ static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
return 0;
}
-static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream,
+static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream,
+ int cmd,
struct snd_soc_dai *dai)
{
- struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+ struct axg_tdm_stream *ts =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ axg_tdm_stream_start(ts);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ axg_tdm_stream_stop(ts);
+ break;
+ default:
+ return -EINVAL;
+ }
- /* Force all attached formatters to update */
- return axg_tdm_stream_reset(ts);
+ return 0;
}
static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
@@ -397,8 +413,8 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
.set_fmt = axg_tdm_iface_set_fmt,
.startup = axg_tdm_iface_startup,
.hw_params = axg_tdm_iface_hw_params,
- .prepare = axg_tdm_iface_prepare,
.hw_free = axg_tdm_iface_hw_free,
+ .trigger = axg_tdm_iface_trigger,
};
/* TDM Backend DAIs */
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 415cc0046e4b..29b0174f4b5c 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -302,6 +302,7 @@ int meson_card_probe(struct platform_device *pdev)
priv->card.owner = THIS_MODULE;
priv->card.dev = dev;
+ priv->card.driver_name = dev->driver->name;
priv->match_data = data;
ret = snd_soc_of_parse_card_name(&priv->card, "model");
diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c
index d07270d17cee..2870cfad813a 100644
--- a/sound/soc/meson/meson-codec-glue.c
+++ b/sound/soc/meson/meson-codec-glue.c
@@ -113,9 +113,6 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
/* Replace link params with the input params */
rtd->dai_link->params = &in_data->params;
- if (!in_data->fmt)
- return 0;
-
return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt);
}
EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup);
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index cc7c1de2f1d9..b2173847dc47 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -38,6 +38,7 @@ config SND_SOC_LPASS_SC7180
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
+ depends on GPIOLIB
select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A
help
@@ -84,6 +85,25 @@ config SND_SOC_QDSP6_ASM_DAI
select SND_SOC_COMPRESS
tristate
+config SND_SOC_QDSP6_APM_DAI
+ tristate
+ select SND_SOC_COMPRESS
+
+config SND_SOC_QDSP6_APM_LPASS_DAI
+ tristate
+
+config SND_SOC_QDSP6_APM
+ tristate
+ select SND_SOC_QDSP6_APM_DAI
+ select SND_SOC_QDSP6_APM_LPASS_DAI
+
+config SND_SOC_QDSP6_PRM_LPASS_CLOCKS
+ tristate
+
+config SND_SOC_QDSP6_PRM
+ tristate
+ select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
+
config SND_SOC_QDSP6
tristate "SoC ALSA audio driver for QDSP6"
depends on QCOM_APR
@@ -97,6 +117,9 @@ config SND_SOC_QDSP6
select SND_SOC_QDSP6_ROUTING
select SND_SOC_QDSP6_ASM
select SND_SOC_QDSP6_ASM_DAI
+ select SND_SOC_TOPOLOGY
+ select SND_SOC_QDSP6_APM
+ select SND_SOC_QDSP6_PRM
help
To add support for MSM QDSP6 Soc Audio.
This will enable sound soc platform specific
@@ -141,7 +164,7 @@ config SND_SOC_SM8250
config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
- depends on I2C
+ depends on I2C && GPIOLIB
select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
index 1a69baefc5ce..c7b7d0864d1a 100644
--- a/sound/soc/qcom/apq8096.c
+++ b/sound/soc/qcom/apq8096.c
@@ -60,7 +60,7 @@ end:
return ret;
}
-static struct snd_soc_ops apq8096_ops = {
+static const struct snd_soc_ops apq8096_ops = {
.hw_params = msm_snd_hw_params,
};
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 09af00700700..2e1c618f7529 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -44,7 +44,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
return ret;
/* Populate links */
- num_links = of_get_child_count(dev->of_node);
+ num_links = of_get_available_child_count(dev->of_node);
/* Allocate the DAI link array */
card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
@@ -54,7 +54,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
card->num_links = num_links;
link = card->dai_link;
- for_each_child_of_node(dev->of_node, np) {
+ for_each_available_child_of_node(dev->of_node, np) {
dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
if (!dlc) {
ret = -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index 3c1dd9f32f1d..3963bf234664 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o
+snd-q6dsp-common-objs := q6dsp-common.o q6dsp-lpass-ports.o q6dsp-lpass-clocks.o
+snd-q6apm-objs := q6apm.o audioreach.o topology.o
+
+obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += snd-q6dsp-common.o
obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o
@@ -8,3 +11,9 @@ obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o
+
+obj-$(CONFIG_SND_SOC_QDSP6_APM) += snd-q6apm.o
+obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
+obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o
+obj-$(CONFIG_SND_SOC_QDSP6_PRM) += q6prm.o
+obj-$(CONFIG_SND_SOC_QDSP6_PRM_LPASS_CLOCKS) += q6prm-clocks.o
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
new file mode 100644
index 000000000000..98c0efa1d0fe
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/apr.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <dt-bindings/soc/qcom,gpr.h>
+#include "q6apm.h"
+#include "audioreach.h"
+
+/* SubGraph Config */
+struct apm_sub_graph_data {
+ struct apm_sub_graph_cfg sub_graph_cfg;
+ struct apm_prop_data perf_data;
+ struct apm_sg_prop_id_perf_mode perf;
+ struct apm_prop_data dir_data;
+ struct apm_sg_prop_id_direction dir;
+ struct apm_prop_data sid_data;
+ struct apm_sg_prop_id_scenario_id sid;
+
+} __packed;
+
+#define APM_SUB_GRAPH_CFG_NPROP 3
+
+struct apm_sub_graph_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_sub_graphs;
+ struct apm_sub_graph_data sg_cfg[];
+} __packed;
+
+#define APM_SUB_GRAPH_PSIZE(p, n) ALIGN(struct_size(p, sg_cfg, n), 8)
+
+/* container config */
+struct apm_container_obj {
+ struct apm_container_cfg container_cfg;
+ /* Capability ID list */
+ struct apm_prop_data cap_data;
+ uint32_t num_capability_id;
+ uint32_t capability_id;
+
+ /* Container graph Position */
+ struct apm_prop_data pos_data;
+ struct apm_cont_prop_id_graph_pos pos;
+
+ /* Container Stack size */
+ struct apm_prop_data stack_data;
+ struct apm_cont_prop_id_stack_size stack;
+
+ /* Container proc domain id */
+ struct apm_prop_data domain_data;
+ struct apm_cont_prop_id_domain domain;
+} __packed;
+
+struct apm_container_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_containers;
+ struct apm_container_obj cont_obj[];
+} __packed;
+
+#define APM_CONTAINER_PSIZE(p, n) ALIGN(struct_size(p, cont_obj, n), 8)
+
+/* Module List config */
+struct apm_mod_list_obj {
+ /* Modules list cfg */
+ uint32_t sub_graph_id;
+ uint32_t container_id;
+ uint32_t num_modules;
+ struct apm_module_obj mod_cfg[];
+} __packed;
+
+#define APM_MOD_LIST_OBJ_PSIZE(p, n) struct_size(p, mod_cfg, n)
+
+struct apm_module_list_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_modules_list;
+ /* Module list config array */
+ struct apm_mod_list_obj mod_list_obj[];
+} __packed;
+
+
+/* Module Properties */
+struct apm_mod_prop_obj {
+ u32 instance_id;
+ u32 num_props;
+ struct apm_prop_data prop_data_1;
+ struct apm_module_prop_id_port_info prop_id_port;
+} __packed;
+
+struct apm_prop_list_params {
+ struct apm_module_param_data param_data;
+ u32 num_modules_prop_cfg;
+ struct apm_mod_prop_obj mod_prop_obj[];
+
+} __packed;
+
+#define APM_MOD_PROP_PSIZE(p, n) ALIGN(struct_size(p, mod_prop_obj, n), 8)
+
+/* Module Connections */
+struct apm_mod_conn_list_params {
+ struct apm_module_param_data param_data;
+ u32 num_connections;
+ struct apm_module_conn_obj conn_obj[];
+
+} __packed;
+
+#define APM_MOD_CONN_PSIZE(p, n) ALIGN(struct_size(p, conn_obj, n), 8)
+
+struct apm_graph_open_params {
+ struct apm_cmd_header *cmd_header;
+ struct apm_sub_graph_params *sg_data;
+ struct apm_container_params *cont_data;
+ struct apm_module_list_params *mod_list_data;
+ struct apm_prop_list_params *mod_prop_data;
+ struct apm_mod_conn_list_params *mod_conn_list_data;
+} __packed;
+
+struct apm_pcm_module_media_fmt_cmd {
+ struct apm_module_param_data param_data;
+ struct param_id_pcm_output_format_cfg header;
+ struct payload_pcm_output_format_cfg media_cfg;
+} __packed;
+
+struct apm_rd_shmem_module_config_cmd {
+ struct apm_module_param_data param_data;
+ struct param_id_rd_sh_mem_cfg cfg;
+} __packed;
+
+struct apm_sh_module_media_fmt_cmd {
+ struct media_format header;
+ struct payload_media_fmt_pcm cfg;
+} __packed;
+
+#define APM_SHMEM_FMT_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_sh_module_media_fmt_cmd) + \
+ ch * sizeof(uint8_t), 8)
+
+/* num of channels as argument */
+#define APM_PCM_MODULE_FMT_CMD_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_pcm_module_media_fmt_cmd) + \
+ ch * sizeof(uint8_t), 8)
+
+#define APM_PCM_OUT_FMT_CFG_PSIZE(p, n) ALIGN(struct_size(p, channel_mapping, n), 4)
+
+struct apm_i2s_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_i2s_intf_cfg cfg;
+} __packed;
+
+#define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8)
+
+struct apm_module_hw_ep_mf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_mf mf;
+} __packed;
+
+#define APM_HW_EP_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_mf_cfg), 8)
+
+struct apm_module_frame_size_factor_cfg {
+ struct apm_module_param_data param_data;
+ uint32_t frame_size_factor;
+} __packed;
+
+#define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8)
+
+struct apm_module_hw_ep_power_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_power_mode_cfg power_mode;
+} __packed;
+
+#define APM_HW_EP_PMODE_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_power_mode_cfg), 8)
+
+struct apm_module_hw_ep_dma_data_align_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_dma_data_align align;
+} __packed;
+
+#define APM_HW_EP_DALIGN_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_dma_data_align_cfg), 8)
+
+struct apm_gain_module_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_gain_cfg gain_cfg;
+} __packed;
+
+#define APM_GAIN_CFG_PSIZE ALIGN(sizeof(struct apm_gain_module_cfg), 8)
+
+struct apm_codec_dma_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_codec_dma_intf_cfg cfg;
+} __packed;
+
+#define APM_CDMA_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_codec_dma_module_intf_cfg), 8)
+
+static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int pkt_size = GPR_HDR_SIZE + payload_size;
+
+ if (has_cmd_hdr)
+ pkt_size += APM_CMD_HDR_SIZE;
+
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ pkt = p;
+ pkt->hdr.version = GPR_PKT_VER;
+ pkt->hdr.hdr_size = GPR_PKT_HEADER_WORD_SIZE;
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.dest_port = dest_port;
+ pkt->hdr.src_port = src_port;
+
+ pkt->hdr.dest_domain = GPR_DOMAIN_ID_ADSP;
+ pkt->hdr.src_domain = GPR_DOMAIN_ID_APPS;
+ pkt->hdr.token = token;
+ pkt->hdr.opcode = opcode;
+
+ if (has_cmd_hdr) {
+ struct apm_cmd_header *cmd_header;
+
+ p = p + GPR_HDR_SIZE;
+ cmd_header = p;
+ cmd_header->payload_size = payload_size;
+ }
+
+ return pkt;
+}
+
+void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port)
+{
+ return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, false);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_pkt);
+
+void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token, uint32_t src_port)
+{
+ return __audioreach_alloc_pkt(pkt_size, opcode, token, src_port, APM_MODULE_INSTANCE_ID,
+ false);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_apm_pkt);
+
+void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port)
+{
+ return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, true);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_cmd_pkt);
+
+void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token)
+{
+ return __audioreach_alloc_pkt(pkt_size, opcode, token, GPR_APM_MODULE_IID,
+ APM_MODULE_INSTANCE_ID, true);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt);
+
+static void apm_populate_container_config(struct apm_container_obj *cfg,
+ struct audioreach_container *cont)
+{
+
+ /* Container Config */
+ cfg->container_cfg.container_id = cont->container_id;
+ cfg->container_cfg.num_prop = 4;
+
+ /* Capability list */
+ cfg->cap_data.prop_id = APM_CONTAINER_PROP_ID_CAPABILITY_LIST;
+ cfg->cap_data.prop_size = APM_CONTAINER_PROP_ID_CAPABILITY_SIZE;
+ cfg->num_capability_id = 1;
+ cfg->capability_id = cont->capability_id;
+
+ /* Graph Position */
+ cfg->pos_data.prop_id = APM_CONTAINER_PROP_ID_GRAPH_POS;
+ cfg->pos_data.prop_size = sizeof(struct apm_cont_prop_id_graph_pos);
+ cfg->pos.graph_pos = cont->graph_pos;
+
+ /* Stack size */
+ cfg->stack_data.prop_id = APM_CONTAINER_PROP_ID_STACK_SIZE;
+ cfg->stack_data.prop_size = sizeof(struct apm_cont_prop_id_stack_size);
+ cfg->stack.stack_size = cont->stack_size;
+
+ /* Proc domain */
+ cfg->domain_data.prop_id = APM_CONTAINER_PROP_ID_PROC_DOMAIN;
+ cfg->domain_data.prop_size = sizeof(struct apm_cont_prop_id_domain);
+ cfg->domain.proc_domain = cont->proc_domain;
+}
+
+static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
+ struct audioreach_sub_graph *sg)
+{
+ cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id;
+ cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP;
+
+ /* Perf Mode */
+ cfg->perf_data.prop_id = APM_SUB_GRAPH_PROP_ID_PERF_MODE;
+ cfg->perf_data.prop_size = APM_SG_PROP_ID_PERF_MODE_SIZE;
+ cfg->perf.perf_mode = sg->perf_mode;
+
+ /* Direction */
+ cfg->dir_data.prop_id = APM_SUB_GRAPH_PROP_ID_DIRECTION;
+ cfg->dir_data.prop_size = APM_SG_PROP_ID_DIR_SIZE;
+ cfg->dir.direction = sg->direction;
+
+ /* Scenario ID */
+ cfg->sid_data.prop_id = APM_SUB_GRAPH_PROP_ID_SCENARIO_ID;
+ cfg->sid_data.prop_size = APM_SG_PROP_ID_SID_SIZE;
+ cfg->sid.scenario_id = sg->scenario_id;
+}
+
+static void apm_populate_connection_obj(struct apm_module_conn_obj *obj,
+ struct audioreach_module *module)
+{
+ obj->src_mod_inst_id = module->src_mod_inst_id;
+ obj->src_mod_op_port_id = module->src_mod_op_port_id;
+ obj->dst_mod_inst_id = module->instance_id;
+ obj->dst_mod_ip_port_id = module->in_port;
+}
+
+static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
+ struct audioreach_module *module)
+{
+
+ obj->instance_id = module->instance_id;
+ obj->num_props = 1;
+ obj->prop_data_1.prop_id = APM_MODULE_PROP_ID_PORT_INFO;
+ obj->prop_data_1.prop_size = APM_MODULE_PROP_ID_PORT_INFO_SZ;
+ obj->prop_id_port.max_ip_port = module->max_ip_port;
+ obj->prop_id_port.max_op_port = module->max_op_port;
+}
+
+struct audioreach_module *audioreach_get_container_last_module(
+ struct audioreach_container *container)
+{
+ struct audioreach_module *module;
+
+ list_for_each_entry(module, &container->modules_list, node) {
+ if (module->dst_mod_inst_id == 0)
+ return module;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(audioreach_get_container_last_module);
+
+static bool is_module_in_container(struct audioreach_container *container, int module_iid)
+{
+ struct audioreach_module *module;
+
+ list_for_each_entry(module, &container->modules_list, node) {
+ if (module->instance_id == module_iid)
+ return true;
+ }
+
+ return false;
+}
+
+struct audioreach_module *audioreach_get_container_first_module(
+ struct audioreach_container *container)
+{
+ struct audioreach_module *module;
+
+ /* get the first module from both connected or un-connected containers */
+ list_for_each_entry(module, &container->modules_list, node) {
+ if (module->src_mod_inst_id == 0 ||
+ !is_module_in_container(container, module->src_mod_inst_id))
+ return module;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(audioreach_get_container_first_module);
+
+struct audioreach_module *audioreach_get_container_next_module(
+ struct audioreach_container *container,
+ struct audioreach_module *module)
+{
+ int nmodule_iid = module->dst_mod_inst_id;
+ struct audioreach_module *nmodule;
+
+ list_for_each_entry(nmodule, &container->modules_list, node) {
+ if (nmodule->instance_id == nmodule_iid)
+ return nmodule;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(audioreach_get_container_next_module);
+
+static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
+ struct audioreach_container *container,
+ int sub_graph_id)
+{
+ struct audioreach_module *module;
+ int i;
+
+ obj->sub_graph_id = sub_graph_id;
+ obj->container_id = container->container_id;
+ obj->num_modules = container->num_modules;
+ i = 0;
+ list_for_each_container_module(module, container) {
+ obj->mod_cfg[i].module_id = module->module_id;
+ obj->mod_cfg[i].instance_id = module->instance_id;
+ i++;
+ }
+}
+
+static void audioreach_populate_graph(struct apm_graph_open_params *open,
+ struct list_head *sg_list,
+ int num_sub_graphs)
+{
+ struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data;
+ struct apm_module_list_params *ml_data = open->mod_list_data;
+ struct apm_prop_list_params *mp_data = open->mod_prop_data;
+ struct apm_container_params *c_data = open->cont_data;
+ struct apm_sub_graph_params *sg_data = open->sg_data;
+ int ncontainer = 0, nmodule = 0, nconn = 0;
+ struct apm_mod_prop_obj *module_prop_obj;
+ struct audioreach_container *container;
+ struct apm_module_conn_obj *conn_obj;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sg;
+ struct apm_container_obj *cobj;
+ struct apm_mod_list_obj *mlobj;
+ int i = 0;
+
+ mlobj = &ml_data->mod_list_obj[0];
+
+ list_for_each_entry(sg, sg_list, node) {
+ struct apm_sub_graph_data *sg_cfg = &sg_data->sg_cfg[i++];
+
+ apm_populate_sub_graph_config(sg_cfg, sg);
+
+ list_for_each_entry(container, &sg->container_list, node) {
+ cobj = &c_data->cont_obj[ncontainer];
+
+ apm_populate_container_config(cobj, container);
+ apm_populate_module_list_obj(mlobj, container, sg->sub_graph_id);
+
+ list_for_each_container_module(module, container) {
+ uint32_t src_mod_inst_id;
+
+ src_mod_inst_id = module->src_mod_inst_id;
+
+ module_prop_obj = &mp_data->mod_prop_obj[nmodule];
+ apm_populate_module_prop_obj(module_prop_obj, module);
+
+ if (src_mod_inst_id) {
+ conn_obj = &mc_data->conn_obj[nconn];
+ apm_populate_connection_obj(conn_obj, module);
+ nconn++;
+ }
+
+ nmodule++;
+ }
+ mlobj = (void *) mlobj + APM_MOD_LIST_OBJ_PSIZE(mlobj, container->num_modules);
+
+ ncontainer++;
+ }
+ }
+}
+
+void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct list_head *sg_list, int graph_id)
+{
+ int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz;
+ struct apm_module_param_data *param_data;
+ struct apm_container_params *cont_params;
+ struct audioreach_container *container;
+ struct apm_sub_graph_params *sg_params;
+ struct apm_mod_conn_list_params *mcon;
+ struct apm_graph_open_params params;
+ struct apm_prop_list_params *mprop;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sgs;
+ struct apm_mod_list_obj *mlobj;
+ int num_modules_per_list;
+ int num_connections = 0;
+ int num_containers = 0;
+ int num_sub_graphs = 0;
+ int num_modules = 0;
+ int num_modules_list;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ list_for_each_entry(sgs, sg_list, node) {
+ num_sub_graphs++;
+ list_for_each_entry(container, &sgs->container_list, node) {
+ num_containers++;
+ num_modules += container->num_modules;
+ list_for_each_container_module(module, container) {
+ if (module->src_mod_inst_id)
+ num_connections++;
+ }
+ }
+ }
+
+ num_modules_list = num_containers;
+ num_modules_per_list = num_modules/num_containers;
+ sg_sz = APM_SUB_GRAPH_PSIZE(sg_params, num_sub_graphs);
+ cont_sz = APM_CONTAINER_PSIZE(cont_params, num_containers);
+ ml_sz = ALIGN(sizeof(struct apm_module_list_params) +
+ num_modules_list * APM_MOD_LIST_OBJ_PSIZE(mlobj, num_modules_per_list), 8);
+ mp_sz = APM_MOD_PROP_PSIZE(mprop, num_modules);
+ mc_sz = APM_MOD_CONN_PSIZE(mcon, num_connections);
+
+ payload_size = sg_sz + cont_sz + ml_sz + mp_sz + mc_sz;
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0);
+ if (IS_ERR(pkt))
+ return pkt;
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ /* SubGraph */
+ params.sg_data = p;
+ param_data = &params.sg_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_SUB_GRAPH_CONFIG;
+ param_data->param_size = sg_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.sg_data->num_sub_graphs = num_sub_graphs;
+ p += sg_sz;
+
+ /* Container */
+ params.cont_data = p;
+ param_data = &params.cont_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_CONTAINER_CONFIG;
+ param_data->param_size = cont_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.cont_data->num_containers = num_containers;
+ p += cont_sz;
+
+ /* Module List*/
+ params.mod_list_data = p;
+ param_data = &params.mod_list_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_LIST;
+ param_data->param_size = ml_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_list_data->num_modules_list = num_sub_graphs;
+ p += ml_sz;
+
+ /* Module Properties */
+ params.mod_prop_data = p;
+ param_data = &params.mod_prop_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_PROP;
+ param_data->param_size = mp_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_prop_data->num_modules_prop_cfg = num_modules;
+ p += mp_sz;
+
+ /* Module Connections */
+ params.mod_conn_list_data = p;
+ param_data = &params.mod_conn_list_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_CONN;
+ param_data->param_size = mc_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_conn_list_data->num_connections = num_connections;
+ p += mc_sz;
+
+ audioreach_populate_graph(&params, sg_list, num_sub_graphs);
+
+ return pkt;
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt);
+
+int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
+ struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock,
+ gpr_port_t *port, wait_queue_head_t *cmd_wait,
+ struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+
+ struct gpr_hdr *hdr = &pkt->hdr;
+ int rc;
+
+ mutex_lock(cmd_lock);
+ result->opcode = 0;
+ result->status = 0;
+
+ if (port)
+ rc = gpr_send_port_pkt(port, pkt);
+ else if (gdev)
+ rc = gpr_send_pkt(gdev, pkt);
+ else
+ rc = -EINVAL;
+
+ if (rc < 0)
+ goto err;
+
+ if (rsp_opcode)
+ rc = wait_event_timeout(*cmd_wait, (result->opcode == hdr->opcode) ||
+ (result->opcode == rsp_opcode), 5 * HZ);
+ else
+ rc = wait_event_timeout(*cmd_wait, (result->opcode == hdr->opcode), 5 * HZ);
+
+ if (!rc) {
+ dev_err(dev, "CMD timeout for [%x] opcode\n", hdr->opcode);
+ rc = -ETIMEDOUT;
+ } else if (result->status > 0) {
+ dev_err(dev, "DSP returned error[%x] %x\n", hdr->opcode, result->status);
+ rc = -EINVAL;
+ } else {
+ /* DSP successfully finished the command */
+ rc = 0;
+ }
+
+err:
+ mutex_unlock(cmd_lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync);
+
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode)
+{
+
+ return audioreach_send_cmd_sync(graph->dev, NULL, &graph->result, &graph->lock,
+ graph->port, &graph->cmd_wait, pkt, rsp_opcode);
+}
+EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
+
+/* LPASS Codec DMA port Module Media Format Setup */
+static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_codec_dma_module_intf_cfg *intf_cfg;
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_hw_ep_power_mode_cfg *pm_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz, ep_sz, fs_sz, pm_sz, dl_sz;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ ic_sz = APM_CDMA_INTF_CFG_PSIZE;
+ ep_sz = APM_HW_EP_CFG_PSIZE;
+ fs_sz = APM_FS_CFG_PSIZE;
+ pm_sz = APM_HW_EP_PMODE_CFG_PSIZE;
+ dl_sz = 0;
+
+ payload_size = ic_sz + ep_sz + fs_sz + pm_sz + dl_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+ p += ep_sz;
+
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+ p += fs_sz;
+
+ intf_cfg = p;
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_CODEC_DMA_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.lpaif_type = module->hw_interface_type;
+ intf_cfg->cfg.intf_index = module->hw_interface_idx;
+ intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
+ p += ic_sz;
+
+ pm_cfg = p;
+ param_data = &pm_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_POWER_MODE_CFG;
+ param_data->param_size = pm_sz - APM_MODULE_PARAM_DATA_SIZE;
+ pm_cfg->power_mode.power_mode = 0;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_i2s_module_intf_cfg *intf_cfg;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz, ep_sz, fs_sz;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ ic_sz = APM_I2S_INTF_CFG_PSIZE;
+ ep_sz = APM_HW_EP_CFG_PSIZE;
+ fs_sz = APM_FS_CFG_PSIZE;
+
+ payload_size = ic_sz + ep_sz + fs_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ intf_cfg = p;
+
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_I2S_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.intf_idx = module->hw_interface_idx;
+ intf_cfg->cfg.sd_line_idx = module->sd_line_idx;
+
+ switch (cfg->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ intf_cfg->cfg.ws_src = CONFIG_I2S_WS_SRC_INTERNAL;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* CPU is slave */
+ intf_cfg->cfg.ws_src = CONFIG_I2S_WS_SRC_EXTERNAL;
+ break;
+ default:
+ break;
+ }
+
+ p += ic_sz;
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+
+ p += ep_sz;
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module)
+{
+ struct apm_module_param_data *param_data;
+ struct data_logging_config *cfg;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DATA_LOGGING_CONFIG;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->log_code = module->log_code;
+ cfg->log_tap_point_id = module->log_tap_point_id;
+ cfg->mode = module->log_mode;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *mcfg)
+{
+ struct payload_pcm_output_format_cfg *media_cfg;
+ uint32_t num_channels = mcfg->num_channels;
+ struct apm_pcm_module_media_fmt_cmd *cfg;
+ struct apm_module_param_data *param_data;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+
+ if (num_channels > 2) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels);
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cfg = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_PCM_OUTPUT_FORMAT_CFG;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->header.data_format = DATA_FORMAT_FIXED_POINT;
+ cfg->header.fmt_id = MEDIA_FMT_ID_PCM;
+ cfg->header.payload_size = APM_PCM_OUT_FMT_CFG_PSIZE(media_cfg, num_channels);
+
+ media_cfg = &cfg->media_cfg;
+ media_cfg->alignment = PCM_LSB_ALIGNED;
+ media_cfg->bit_width = mcfg->bit_width;
+ media_cfg->endianness = PCM_LITTLE_ENDIAN;
+ media_cfg->interleaved = module->interleave_type;
+ media_cfg->num_channels = mcfg->num_channels;
+ media_cfg->q_factor = mcfg->bit_width - 1;
+ media_cfg->bits_per_sample = mcfg->bit_width;
+
+ if (num_channels == 1) {
+ media_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ } else if (num_channels == 2) {
+ media_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ media_cfg->channel_mapping[1] = PCM_CHANNEL_R;
+
+ }
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *mcfg)
+{
+ uint32_t num_channels = mcfg->num_channels;
+ struct apm_module_param_data *param_data;
+ struct payload_media_fmt_pcm *cfg;
+ struct media_format *header;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ if (num_channels > 2) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE;
+
+ pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
+ graph->port->id, module->instance_id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_MEDIA_FORMAT;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+
+ header = p;
+ header->data_format = DATA_FORMAT_FIXED_POINT;
+ header->fmt_id = MEDIA_FMT_ID_PCM;
+ header->payload_size = payload_size - sizeof(*header);
+
+ p = p + sizeof(*header);
+ cfg = p;
+ cfg->sample_rate = mcfg->sample_rate;
+ cfg->bit_width = mcfg->bit_width;
+ cfg->alignment = PCM_LSB_ALIGNED;
+ cfg->bits_per_sample = mcfg->bit_width;
+ cfg->q_factor = mcfg->bit_width - 1;
+ cfg->endianness = PCM_LITTLE_ENDIAN;
+ cfg->num_channels = mcfg->num_channels;
+
+ if (mcfg->num_channels == 1) {
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ } else if (num_channels == 2) {
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ }
+
+ rc = audioreach_graph_send_cmd_sync(graph, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol)
+{
+ struct param_id_vol_ctrl_master_gain *cfg;
+ struct apm_module_param_data *param_data;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_VOL_CTRL_MASTER_GAIN;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->master_gain = vol;
+ rc = q6apm_send_cmd_sync(apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_gain_set_vol_ctrl);
+
+static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_module *module)
+{
+ struct apm_module_param_data *param_data;
+ struct apm_gain_module_cfg *cfg;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+
+ payload_size = APM_GAIN_CFG_PSIZE;
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cfg = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = APM_PARAM_ID_GAIN;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->gain_cfg.gain = module->gain;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ int rc;
+
+ switch (module->module_id) {
+ case MODULE_ID_DATA_LOGGING:
+ rc = audioreach_logging_set_media_format(graph, module);
+ break;
+ case MODULE_ID_PCM_DEC:
+ case MODULE_ID_PCM_ENC:
+ case MODULE_ID_PCM_CNV:
+ rc = audioreach_pcm_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_I2S_SOURCE:
+ case MODULE_ID_I2S_SINK:
+ rc = audioreach_i2s_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_WR_SHARED_MEM_EP:
+ rc = audioreach_shmem_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_GAIN:
+ rc = audioreach_gain_set(graph, module);
+ break;
+ case MODULE_ID_CODEC_DMA_SINK:
+ case MODULE_ID_CODEC_DMA_SOURCE:
+ rc = audioreach_codec_dma_set_media_format(graph, module, cfg);
+ break;
+ default:
+ rc = 0;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_set_media_format);
+
+void audioreach_graph_free_buf(struct q6apm_graph *graph)
+{
+ struct audioreach_graph_data *port;
+
+ mutex_lock(&graph->lock);
+ port = &graph->rx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+
+ port = &graph->tx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&graph->lock);
+}
+EXPORT_SYMBOL_GPL(audioreach_graph_free_buf);
+
+int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz,
+ unsigned int periods, bool is_contiguous)
+{
+ struct apm_shared_map_region_payload *mregions;
+ struct apm_cmd_shared_mem_map_regions *cmd;
+ uint32_t num_regions, buf_sz, payload_size;
+ struct audioreach_graph_data *data;
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, i;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ if (is_contiguous) {
+ num_regions = 1;
+ buf_sz = period_sz * periods;
+ } else {
+ buf_sz = period_sz;
+ num_regions = periods;
+ }
+
+ /* DSP expects size should be aligned to 4K */
+ buf_sz = ALIGN(buf_sz, 4096);
+
+ payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions);
+
+ pkt = audioreach_alloc_apm_pkt(payload_size, APM_CMD_SHARED_MEM_MAP_REGIONS, dir,
+ graph->port->id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE;
+ cmd = p;
+ cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL;
+ cmd->num_regions = num_regions;
+
+ cmd->property_flag = 0x0;
+
+ mregions = p + sizeof(*cmd);
+
+ mutex_lock(&graph->lock);
+
+ for (i = 0; i < num_regions; i++) {
+ struct audio_buffer *ab;
+
+ ab = &data->buf[i];
+ mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+ mregions->shm_addr_msw = upper_32_bits(ab->phys);
+ mregions->mem_size_bytes = buf_sz;
+ ++mregions;
+ }
+ mutex_unlock(&graph->lock);
+
+ rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS);
+
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_map_memory_regions);
+
+int audioreach_shared_memory_send_eos(struct q6apm_graph *graph)
+{
+ struct data_cmd_wr_sh_mem_ep_eos *eos;
+ struct gpr_pkt *pkt;
+ int rc = 0, iid;
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*eos), DATA_CMD_WR_SH_MEM_EP_EOS, 0,
+ graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ eos = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ eos->policy = WR_SH_MEM_EP_EOS_POLICY_LAST;
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_shared_memory_send_eos);
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
new file mode 100644
index 000000000000..4f693a2660b5
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -0,0 +1,726 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __AUDIOREACH_H__
+#define __AUDIOREACH_H__
+#include <linux/types.h>
+#include <linux/soc/qcom/apr.h>
+#include <sound/soc.h>
+struct q6apm;
+struct q6apm_graph;
+
+/* Module IDs */
+#define MODULE_ID_WR_SHARED_MEM_EP 0x07001000
+#define MODULE_ID_RD_SHARED_MEM_EP 0x07001001
+#define MODULE_ID_GAIN 0x07001002
+#define MODULE_ID_PCM_CNV 0x07001003
+#define MODULE_ID_PCM_ENC 0x07001004
+#define MODULE_ID_PCM_DEC 0x07001005
+#define MODULE_ID_CODEC_DMA_SINK 0x07001023
+#define MODULE_ID_CODEC_DMA_SOURCE 0x07001024
+#define MODULE_ID_I2S_SINK 0x0700100A
+#define MODULE_ID_I2S_SOURCE 0x0700100B
+#define MODULE_ID_DATA_LOGGING 0x0700101A
+
+#define APM_CMD_GET_SPF_STATE 0x01001021
+#define APM_CMD_RSP_GET_SPF_STATE 0x02001007
+
+#define APM_MODULE_INSTANCE_ID 0x00000001
+#define PRM_MODULE_INSTANCE_ID 0x00000002
+#define AMDB_MODULE_INSTANCE_ID 0x00000003
+#define VCPM_MODULE_INSTANCE_ID 0x00000004
+#define AR_MODULE_INSTANCE_ID_START 0x00006000
+#define AR_MODULE_INSTANCE_ID_END 0x00007000
+#define AR_MODULE_DYNAMIC_INSTANCE_ID_START 0x00007000
+#define AR_MODULE_DYNAMIC_INSTANCE_ID_END 0x00008000
+#define AR_CONT_INSTANCE_ID_START 0x00005000
+#define AR_CONT_INSTANCE_ID_END 0x00006000
+#define AR_SG_INSTANCE_ID_START 0x00004000
+
+#define APM_CMD_GRAPH_OPEN 0x01001000
+#define APM_CMD_GRAPH_PREPARE 0x01001001
+#define APM_CMD_GRAPH_START 0x01001002
+#define APM_CMD_GRAPH_STOP 0x01001003
+#define APM_CMD_GRAPH_CLOSE 0x01001004
+#define APM_CMD_GRAPH_FLUSH 0x01001005
+#define APM_CMD_SET_CFG 0x01001006
+#define APM_CMD_GET_CFG 0x01001007
+#define APM_CMD_SHARED_MEM_MAP_REGIONS 0x0100100C
+#define APM_CMD_SHARED_MEM_UNMAP_REGIONS 0x0100100D
+#define APM_CMD_RSP_SHARED_MEM_MAP_REGIONS 0x02001001
+#define APM_CMD_RSP_GET_CFG 0x02001000
+#define APM_CMD_CLOSE_ALL 0x01001013
+#define APM_CMD_REGISTER_SHARED_CFG 0x0100100A
+
+#define APM_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+struct apm_cmd_shared_mem_map_regions {
+ uint16_t mem_pool_id;
+ uint16_t num_regions;
+ uint32_t property_flag;
+} __packed;
+
+struct apm_shared_map_region_payload {
+ uint32_t shm_addr_lsw;
+ uint32_t shm_addr_msw;
+ uint32_t mem_size_bytes;
+} __packed;
+
+struct apm_cmd_shared_mem_unmap_regions {
+ uint32_t mem_map_handle;
+} __packed;
+
+struct apm_cmd_rsp_shared_mem_map_regions {
+ uint32_t mem_map_handle;
+} __packed;
+
+/* APM module */
+#define APM_PARAM_ID_SUB_GRAPH_LIST 0x08001005
+
+#define APM_PARAM_ID_MODULE_LIST 0x08001002
+
+struct apm_param_id_modules_list {
+ uint32_t num_modules_list;
+} __packed;
+
+#define APM_PARAM_ID_MODULE_PROP 0x08001003
+
+struct apm_param_id_module_prop {
+ uint32_t num_modules_prop_cfg;
+} __packed;
+
+struct apm_module_prop_cfg {
+ uint32_t instance_id;
+ uint32_t num_props;
+} __packed;
+
+#define APM_PARAM_ID_MODULE_CONN 0x08001004
+
+struct apm_param_id_module_conn {
+ uint32_t num_connections;
+} __packed;
+
+struct apm_module_conn_obj {
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+} __packed;
+
+#define APM_PARAM_ID_GAIN 0x08001006
+
+struct param_id_gain_cfg {
+ uint16_t gain;
+ uint16_t reserved;
+} __packed;
+
+#define PARAM_ID_PCM_OUTPUT_FORMAT_CFG 0x08001008
+
+struct param_id_pcm_output_format_cfg {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+struct payload_pcm_output_format_cfg {
+ uint16_t bit_width;
+ uint16_t alignment;
+ uint16_t bits_per_sample;
+ uint16_t q_factor;
+ uint16_t endianness;
+ uint16_t interleaved;
+ uint16_t reserved;
+ uint16_t num_channels;
+ uint8_t channel_mapping[];
+} __packed;
+
+#define PARAM_ID_ENC_BITRATE 0x08001052
+
+struct param_id_enc_bitrate_param {
+ uint32_t bitrate;
+} __packed;
+
+#define DATA_FORMAT_FIXED_POINT 1
+#define PCM_LSB_ALIGNED 1
+#define PCM_MSB_ALIGNED 2
+#define PCM_LITTLE_ENDIAN 1
+#define PCM_BIT_ENDIAN 2
+
+#define MEDIA_FMT_ID_PCM 0x09001000
+#define PCM_CHANNEL_L 1
+#define PCM_CHANNEL_R 2
+#define SAMPLE_RATE_48K 48000
+#define BIT_WIDTH_16 16
+
+#define APM_PARAM_ID_PROP_PORT_INFO 0x08001015
+
+struct apm_modules_prop_info {
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+} __packed;
+
+/* Shared memory module */
+#define DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER 0x04001000
+#define WR_SH_MEM_EP_TIMESTAMP_VALID_FLAG BIT(31)
+#define WR_SH_MEM_EP_LAST_BUFFER_FLAG BIT(30)
+#define WR_SH_MEM_EP_TS_CONTINUE_FLAG BIT(29)
+#define WR_SH_MEM_EP_EOF_FLAG BIT(4)
+
+struct apm_data_cmd_wr_sh_mem_ep_data_buffer {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2 0x0400100A
+
+struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t md_addr_lsw;
+ uint32_t md_addr_msw;
+ uint32_t md_map_handle;
+ uint32_t md_buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE 0x05001000
+
+struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t status;
+
+} __packed;
+
+#define DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2 0x05001004
+
+struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t status;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_status;
+} __packed;
+
+#define PARAM_ID_MEDIA_FORMAT 0x0800100C
+#define DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT 0x04001001
+
+struct apm_media_format {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002
+#define WR_SH_MEM_EP_EOS_POLICY_LAST 1
+#define WR_SH_MEM_EP_EOS_POLICY_EACH 2
+
+struct data_cmd_wr_sh_mem_ep_eos {
+ uint32_t policy;
+
+} __packed;
+
+#define DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER 0x04001003
+
+struct data_cmd_rd_sh_mem_ep_data_buffer {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER 0x05001002
+
+struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done {
+ uint32_t status;
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t data_size;
+ uint32_t offset;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t num_frames;
+} __packed;
+
+#define DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2 0x0400100B
+
+struct data_cmd_rd_sh_mem_ep_data_buffer_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2 0x05001005
+
+struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 {
+ uint32_t status;
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t data_size;
+ uint32_t offset;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t num_frames;
+ uint32_t md_status;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_size;
+} __packed;
+
+#define PARAM_ID_RD_SH_MEM_CFG 0x08001007
+
+struct param_id_rd_sh_mem_cfg {
+ uint32_t num_frames_per_buffer;
+ uint32_t metadata_control_flags;
+
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED 0x05001001
+
+struct data_cmd_wr_sh_mem_ep_eos_rendered {
+ uint32_t module_instance_id;
+ uint32_t render_status;
+} __packed;
+
+#define MODULE_ID_WR_SHARED_MEM_EP 0x07001000
+
+struct apm_cmd_header {
+ uint32_t payload_address_lsw;
+ uint32_t payload_address_msw;
+ uint32_t mem_map_handle;
+ uint32_t payload_size;
+} __packed;
+
+#define APM_CMD_HDR_SIZE sizeof(struct apm_cmd_header)
+
+struct apm_module_param_data {
+ uint32_t module_instance_id;
+ uint32_t param_id;
+ uint32_t param_size;
+ uint32_t error_code;
+} __packed;
+
+#define APM_MODULE_PARAM_DATA_SIZE sizeof(struct apm_module_param_data)
+
+struct apm_module_param_shared_data {
+ uint32_t param_id;
+ uint32_t param_size;
+} __packed;
+
+struct apm_prop_data {
+ uint32_t prop_id;
+ uint32_t prop_size;
+} __packed;
+
+/* Sub-Graph Properties */
+#define APM_PARAM_ID_SUB_GRAPH_CONFIG 0x08001001
+
+struct apm_param_id_sub_graph_cfg {
+ uint32_t num_sub_graphs;
+} __packed;
+
+struct apm_sub_graph_cfg {
+ uint32_t sub_graph_id;
+ uint32_t num_sub_graph_prop;
+} __packed;
+
+#define APM_SUB_GRAPH_PROP_ID_PERF_MODE 0x0800100E
+
+struct apm_sg_prop_id_perf_mode {
+ uint32_t perf_mode;
+} __packed;
+
+#define APM_SG_PROP_ID_PERF_MODE_SIZE 4
+
+#define APM_SUB_GRAPH_PROP_ID_DIRECTION 0x0800100F
+
+struct apm_sg_prop_id_direction {
+ uint32_t direction;
+} __packed;
+
+#define APM_SG_PROP_ID_DIR_SIZE 4
+
+#define APM_SUB_GRAPH_PROP_ID_SCENARIO_ID 0x08001010
+#define APM_SUB_GRAPH_SID_AUDIO_PLAYBACK 0x1
+#define APM_SUB_GRAPH_SID_AUDIO_RECORD 0x2
+#define APM_SUB_GRAPH_SID_AUDIO_VOICE_CALL 0x3
+
+struct apm_sg_prop_id_scenario_id {
+ uint32_t scenario_id;
+} __packed;
+
+#define APM_SG_PROP_ID_SID_SIZE 4
+/* container api */
+#define APM_PARAM_ID_CONTAINER_CONFIG 0x08001000
+
+struct apm_param_id_container_cfg {
+ uint32_t num_containers;
+} __packed;
+
+struct apm_container_cfg {
+ uint32_t container_id;
+ uint32_t num_prop;
+} __packed;
+
+struct apm_cont_capability {
+ uint32_t capability_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_CAPABILITY_LIST 0x08001011
+#define APM_CONTAINER_PROP_ID_CAPABILITY_SIZE 8
+
+#define APM_PROP_ID_INVALID 0x0
+#define APM_CONTAINER_CAP_ID_PP 0x1
+#define APM_CONTAINER_CAP_ID_PP 0x1
+
+struct apm_cont_prop_id_cap_list {
+ uint32_t num_capability_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_GRAPH_POS 0x08001012
+
+struct apm_cont_prop_id_graph_pos {
+ uint32_t graph_pos;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_STACK_SIZE 0x08001013
+
+struct apm_cont_prop_id_stack_size {
+ uint32_t stack_size;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_PROC_DOMAIN 0x08001014
+
+struct apm_cont_prop_id_domain {
+ uint32_t proc_domain;
+} __packed;
+
+#define CONFIG_I2S_WS_SRC_EXTERNAL 0x0
+#define CONFIG_I2S_WS_SRC_INTERNAL 0x1
+
+#define PARAM_ID_I2S_INTF_CFG 0x08001019
+struct param_id_i2s_intf_cfg {
+ uint32_t lpaif_type;
+ uint32_t intf_idx;
+ uint16_t sd_line_idx;
+ uint16_t ws_src;
+} __packed;
+
+#define I2S_INTF_TYPE_PRIMARY 0
+#define I2S_INTF_TYPE_SECOINDARY 1
+#define I2S_INTF_TYPE_TERTINARY 2
+#define I2S_INTF_TYPE_QUATERNARY 3
+#define I2S_INTF_TYPE_QUINARY 4
+#define I2S_SD0 1
+#define I2S_SD1 2
+#define I2S_SD2 3
+#define I2S_SD3 4
+
+#define PORT_ID_I2S_INPUT 2
+#define PORT_ID_I2S_OUPUT 1
+#define I2S_STACK_SIZE 2048
+
+#define PARAM_ID_HW_EP_MF_CFG 0x08001017
+struct param_id_hw_ep_mf {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t num_channels;
+ uint32_t data_format;
+} __packed;
+
+#define PARAM_ID_HW_EP_FRAME_SIZE_FACTOR 0x08001018
+
+struct param_id_fram_size_factor {
+ uint32_t frame_size_factor;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_PARENT_CONTAINER_ID 0x080010CB
+
+struct apm_cont_prop_id_parent_container {
+ uint32_t parent_container_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_HEAP_ID 0x08001174
+#define APM_CONT_HEAP_DEFAULT 0x1
+#define APM_CONT_HEAP_LOW_POWER 0x2
+
+struct apm_cont_prop_id_headp_id {
+ uint32_t heap_id;
+} __packed;
+
+struct apm_modules_list {
+ uint32_t sub_graph_id;
+ uint32_t container_id;
+ uint32_t num_modules;
+} __packed;
+
+struct apm_module_obj {
+ uint32_t module_id;
+ uint32_t instance_id;
+} __packed;
+
+#define APM_MODULE_PROP_ID_PORT_INFO 0x08001015
+#define APM_MODULE_PROP_ID_PORT_INFO_SZ 8
+struct apm_module_prop_id_port_info {
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+} __packed;
+
+#define DATA_LOGGING_MAX_INPUT_PORTS 0x1
+#define DATA_LOGGING_MAX_OUTPUT_PORTS 0x1
+#define DATA_LOGGING_STACK_SIZE 2048
+#define PARAM_ID_DATA_LOGGING_CONFIG 0x08001031
+
+struct data_logging_config {
+ uint32_t log_code;
+ uint32_t log_tap_point_id;
+ uint32_t mode;
+} __packed;
+
+#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024
+
+struct param_id_mfc_media_format {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t num_channels;
+ uint16_t channel_mapping[];
+} __packed;
+
+struct media_format {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+struct payload_media_fmt_pcm {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t alignment;
+ uint16_t bits_per_sample;
+ uint16_t q_factor;
+ uint16_t endianness;
+ uint16_t num_channels;
+ uint8_t channel_mapping[];
+} __packed;
+
+#define PARAM_ID_CODEC_DMA_INTF_CFG 0x08001063
+
+struct param_id_codec_dma_intf_cfg {
+ /* 1 - RXTX
+ * 2 - WSA
+ * 3 - VA
+ * 4 - AXI
+ */
+ uint32_t lpaif_type;
+ /*
+ * RX0 | TX0 = 1
+ * RX1 | TX1 = 2
+ * RX2 | TX2 = 3... so on
+ */
+ uint32_t intf_index;
+ uint32_t active_channels_mask;
+} __packed;
+
+struct audio_hw_clk_cfg {
+ uint32_t clock_id;
+ uint32_t clock_freq;
+ uint32_t clock_attri;
+ uint32_t clock_root;
+} __packed;
+
+#define PARAM_ID_HW_EP_POWER_MODE_CFG 0x8001176
+#define AR_HW_EP_POWER_MODE_0 0 /* default */
+#define AR_HW_EP_POWER_MODE_1 1 /* XO Shutdown allowed */
+#define AR_HW_EP_POWER_MODE_2 2 /* XO Shutdown not allowed */
+
+struct param_id_hw_ep_power_mode_cfg {
+ uint32_t power_mode;
+} __packed;
+
+#define PARAM_ID_HW_EP_DMA_DATA_ALIGN 0x08001233
+#define AR_HW_EP_DMA_DATA_ALIGN_MSB 0
+#define AR_HW_EP_DMA_DATA_ALIGN_LSB 1
+#define AR_PCM_MAX_NUM_CHANNEL 8
+
+struct param_id_hw_ep_dma_data_align {
+ uint32_t dma_data_align;
+} __packed;
+
+#define PARAM_ID_VOL_CTRL_MASTER_GAIN 0x08001035
+#define VOL_CTRL_DEFAULT_GAIN 0x2000
+
+struct param_id_vol_ctrl_master_gain {
+ uint16_t master_gain;
+ uint16_t reserved;
+} __packed;
+
+
+/* Graph */
+struct audioreach_connection {
+ /* Connections */
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+ struct list_head node;
+};
+
+struct audioreach_graph_info {
+ int id;
+ uint32_t num_sub_graphs;
+ struct list_head sg_list;
+ struct list_head connection_list;
+};
+
+struct audioreach_sub_graph {
+ uint32_t sub_graph_id;
+ uint32_t perf_mode;
+ uint32_t direction;
+ uint32_t scenario_id;
+ struct list_head node;
+
+ struct audioreach_graph_info *info;
+ uint32_t num_containers;
+ struct list_head container_list;
+};
+
+struct audioreach_container {
+ uint32_t container_id;
+ uint32_t capability_id;
+ uint32_t graph_pos;
+ uint32_t stack_size;
+ uint32_t proc_domain;
+ struct list_head node;
+
+ uint32_t num_modules;
+ struct list_head modules_list;
+ struct audioreach_sub_graph *sub_graph;
+};
+
+struct audioreach_module {
+ uint32_t module_id;
+ uint32_t instance_id;
+
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+
+ uint32_t in_port;
+ uint32_t out_port;
+
+ /* Connections */
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+
+ /* Format specifics */
+ uint32_t ch_fmt;
+ uint32_t rate;
+ uint32_t bit_depth;
+
+ /* I2S module */
+ uint32_t hw_interface_idx;
+ uint32_t sd_line_idx;
+ uint32_t ws_src;
+ uint32_t frame_size_factor;
+ uint32_t data_format;
+ uint32_t hw_interface_type;
+
+ /* PCM module specific */
+ uint32_t interleave_type;
+
+ /* GAIN/Vol Control Module */
+ uint16_t gain;
+
+ /* Logging */
+ uint32_t log_code;
+ uint32_t log_tap_point_id;
+ uint32_t log_mode;
+
+ /* bookkeeping */
+ struct list_head node;
+ struct audioreach_container *container;
+ struct snd_soc_dapm_widget *widget;
+};
+
+struct audioreach_module_config {
+ int direction;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 bits_per_sample;
+
+ u16 data_format;
+ u16 num_channels;
+ u16 active_channels_mask;
+ u32 sd_line_mask;
+ int fmt;
+ u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
+};
+
+/* Packet Allocation routines */
+void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t
+ token);
+void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode,
+ uint32_t token, uint32_t src_port,
+ uint32_t dest_port);
+void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port);
+void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
+ uint32_t token, uint32_t src_port,
+ uint32_t dest_port);
+void *audioreach_alloc_graph_pkt(struct q6apm *apm,
+ struct list_head *sg_list,
+ int graph_id);
+/* Topology specific */
+int audioreach_tplg_init(struct snd_soc_component *component);
+
+/* Module specific */
+void audioreach_graph_free_buf(struct q6apm_graph *graph);
+int audioreach_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, size_t period_sz,
+ unsigned int periods,
+ bool is_contiguous);
+int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result,
+ struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait,
+ struct gpr_pkt *pkt, uint32_t rsp_opcode);
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode);
+int audioreach_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg);
+int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
+ struct audioreach_module *module, int vol);
+struct audioreach_module *audioreach_get_container_last_module(
+ struct audioreach_container *container);
+struct audioreach_module *audioreach_get_container_first_module(
+ struct audioreach_container *container);
+struct audioreach_module *audioreach_get_container_next_module(
+ struct audioreach_container *container,
+ struct audioreach_module *module);
+#define list_for_each_container_module(mod, cont) \
+ for (mod = audioreach_get_container_first_module(cont); mod != NULL; \
+ mod = audioreach_get_container_next_module(cont, mod))
+#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c
index 9431656283cd..1ccab64ff00b 100644
--- a/sound/soc/qcom/qdsp6/q6afe-clocks.c
+++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c
@@ -7,115 +7,18 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/slab.h>
+#include "q6dsp-lpass-clocks.h"
#include "q6afe.h"
#define Q6AFE_CLK(id) { \
.clk_id = id, \
- .afe_clk_id = Q6AFE_##id, \
+ .q6dsp_clk_id = Q6AFE_##id, \
.name = #id, \
.rate = 19200000, \
}
-#define Q6AFE_VOTE_CLK(id, blkid, n) { \
- .clk_id = id, \
- .afe_clk_id = blkid, \
- .name = n, \
- }
-
-struct q6afe_clk_init {
- int clk_id;
- int afe_clk_id;
- char *name;
- int rate;
-};
-
-struct q6afe_clk {
- struct device *dev;
- int afe_clk_id;
- int attributes;
- int rate;
- uint32_t handle;
- struct clk_hw hw;
-};
-
-#define to_q6afe_clk(_hw) container_of(_hw, struct q6afe_clk, hw)
-
-struct q6afe_cc {
- struct device *dev;
- struct q6afe_clk *clks[Q6AFE_MAX_CLK_ID];
-};
-
-static int clk_q6afe_prepare(struct clk_hw *hw)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- return q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes,
- Q6AFE_LPASS_CLK_ROOT_DEFAULT, clk->rate);
-}
-
-static void clk_q6afe_unprepare(struct clk_hw *hw)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes,
- Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0);
-}
-
-static int clk_q6afe_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- clk->rate = rate;
-
- return 0;
-}
-
-static unsigned long clk_q6afe_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- return clk->rate;
-}
-
-static long clk_q6afe_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
-{
- return rate;
-}
-
-static const struct clk_ops clk_q6afe_ops = {
- .prepare = clk_q6afe_prepare,
- .unprepare = clk_q6afe_unprepare,
- .set_rate = clk_q6afe_set_rate,
- .round_rate = clk_q6afe_round_rate,
- .recalc_rate = clk_q6afe_recalc_rate,
-};
-
-static int clk_vote_q6afe_block(struct clk_hw *hw)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- return q6afe_vote_lpass_core_hw(clk->dev, clk->afe_clk_id,
- clk_hw_get_name(&clk->hw), &clk->handle);
-}
-static void clk_unvote_q6afe_block(struct clk_hw *hw)
-{
- struct q6afe_clk *clk = to_q6afe_clk(hw);
-
- q6afe_unvote_lpass_core_hw(clk->dev, clk->afe_clk_id, clk->handle);
-}
-
-static const struct clk_ops clk_vote_q6afe_ops = {
- .prepare = clk_vote_q6afe_block,
- .unprepare = clk_unvote_q6afe_block,
-};
-
-static const struct q6afe_clk_init q6afe_clks[] = {
+static const struct q6dsp_clk_init q6afe_clks[] = {
Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT),
Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT),
Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT),
@@ -176,88 +79,28 @@ static const struct q6afe_clk_init q6afe_clks[] = {
Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_MCLK),
Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK),
Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK),
- Q6AFE_VOTE_CLK(LPASS_HW_AVTIMER_VOTE,
+ Q6DSP_VOTE_CLK(LPASS_HW_AVTIMER_VOTE,
Q6AFE_LPASS_CORE_AVTIMER_BLOCK,
"LPASS_AVTIMER_MACRO"),
- Q6AFE_VOTE_CLK(LPASS_HW_MACRO_VOTE,
+ Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE,
Q6AFE_LPASS_CORE_HW_MACRO_BLOCK,
"LPASS_HW_MACRO"),
- Q6AFE_VOTE_CLK(LPASS_HW_DCODEC_VOTE,
+ Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE,
Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK,
"LPASS_HW_DCODEC"),
};
-static struct clk_hw *q6afe_of_clk_hw_get(struct of_phandle_args *clkspec,
- void *data)
-{
- struct q6afe_cc *cc = data;
- unsigned int idx = clkspec->args[0];
- unsigned int attr = clkspec->args[1];
-
- if (idx >= Q6AFE_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
- dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
- return ERR_PTR(-EINVAL);
- }
-
- if (cc->clks[idx]) {
- cc->clks[idx]->attributes = attr;
- return &cc->clks[idx]->hw;
- }
-
- return ERR_PTR(-ENOENT);
-}
-
-static int q6afe_clock_dev_probe(struct platform_device *pdev)
-{
- struct q6afe_cc *cc;
- struct device *dev = &pdev->dev;
- int i, ret;
-
- cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
- if (!cc)
- return -ENOMEM;
-
- cc->dev = dev;
- for (i = 0; i < ARRAY_SIZE(q6afe_clks); i++) {
- unsigned int id = q6afe_clks[i].clk_id;
- struct clk_init_data init = {
- .name = q6afe_clks[i].name,
- };
- struct q6afe_clk *clk;
-
- clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
- if (!clk)
- return -ENOMEM;
-
- clk->dev = dev;
- clk->afe_clk_id = q6afe_clks[i].afe_clk_id;
- clk->rate = q6afe_clks[i].rate;
- clk->hw.init = &init;
-
- if (clk->rate)
- init.ops = &clk_q6afe_ops;
- else
- init.ops = &clk_vote_q6afe_ops;
-
- cc->clks[id] = clk;
-
- ret = devm_clk_hw_register(dev, &clk->hw);
- if (ret)
- return ret;
- }
-
- ret = devm_of_clk_add_hw_provider(dev, q6afe_of_clk_hw_get, cc);
- if (ret)
- return ret;
-
- dev_set_drvdata(dev, cc);
-
- return 0;
-}
+static const struct q6dsp_clk_desc q6dsp_clk_q6afe __maybe_unused = {
+ .clks = q6afe_clks,
+ .num_clks = ARRAY_SIZE(q6afe_clks),
+ .lpass_set_clk = q6afe_set_lpass_clock,
+ .lpass_vote_clk = q6afe_vote_lpass_core_hw,
+ .lpass_unvote_clk = q6afe_unvote_lpass_core_hw,
+};
#ifdef CONFIG_OF
static const struct of_device_id q6afe_clock_device_id[] = {
- { .compatible = "qcom,q6afe-clocks" },
+ { .compatible = "qcom,q6afe-clocks", .data = &q6dsp_clk_q6afe },
{},
};
MODULE_DEVICE_TABLE(of, q6afe_clock_device_id);
@@ -268,7 +111,7 @@ static struct platform_driver q6afe_clock_platform_driver = {
.name = "q6afe-clock",
.of_match_table = of_match_ptr(q6afe_clock_device_id),
},
- .probe = q6afe_clock_dev_probe,
+ .probe = q6dsp_clock_dev_probe,
};
module_platform_driver(q6afe_clock_platform_driver);
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index ac8f7324e94b..8bb7452b8f18 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -11,91 +11,9 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include "q6dsp-lpass-ports.h"
#include "q6afe.h"
-#define Q6AFE_TDM_PB_DAI(pre, num, did) { \
- .playback = { \
- .stream_name = pre" TDM"#num" Playback", \
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_176400, \
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE, \
- .channels_min = 1, \
- .channels_max = 8, \
- .rate_min = 8000, \
- .rate_max = 176400, \
- }, \
- .name = #did, \
- .ops = &q6tdm_ops, \
- .id = did, \
- .probe = msm_dai_q6_dai_probe, \
- .remove = msm_dai_q6_dai_remove, \
- }
-
-#define Q6AFE_TDM_CAP_DAI(pre, num, did) { \
- .capture = { \
- .stream_name = pre" TDM"#num" Capture", \
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_176400, \
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE, \
- .channels_min = 1, \
- .channels_max = 8, \
- .rate_min = 8000, \
- .rate_max = 176400, \
- }, \
- .name = #did, \
- .ops = &q6tdm_ops, \
- .id = did, \
- .probe = msm_dai_q6_dai_probe, \
- .remove = msm_dai_q6_dai_remove, \
- }
-
-#define Q6AFE_CDC_DMA_RX_DAI(did) { \
- .playback = { \
- .stream_name = #did" Playback", \
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_176400, \
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE, \
- .channels_min = 1, \
- .channels_max = 8, \
- .rate_min = 8000, \
- .rate_max = 176400, \
- }, \
- .name = #did, \
- .ops = &q6dma_ops, \
- .id = did, \
- .probe = msm_dai_q6_dai_probe, \
- .remove = msm_dai_q6_dai_remove, \
- }
-
-#define Q6AFE_CDC_DMA_TX_DAI(did) { \
- .capture = { \
- .stream_name = #did" Capture", \
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_176400, \
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE, \
- .channels_min = 1, \
- .channels_max = 8, \
- .rate_min = 8000, \
- .rate_max = 176400, \
- }, \
- .name = #did, \
- .ops = &q6dma_ops, \
- .id = did, \
- .probe = msm_dai_q6_dai_probe, \
- .remove = msm_dai_q6_dai_remove, \
- }
struct q6afe_dai_priv_data {
uint32_t sd_line_mask;
@@ -784,591 +702,6 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
return 0;
}
-static struct snd_soc_dai_driver q6afe_dais[] = {
- {
- .playback = {
- .stream_name = "HDMI Playback",
- .rates = SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 2,
- .channels_max = 8,
- .rate_max = 192000,
- .rate_min = 48000,
- },
- .ops = &q6hdmi_ops,
- .id = HDMI_RX,
- .name = "HDMI",
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .name = "SLIMBUS_0_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_0_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .playback = {
- .stream_name = "Slimbus Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .name = "SLIMBUS_0_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_0_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus1 Playback",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .name = "SLIMBUS_1_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_1_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .name = "SLIMBUS_1_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_1_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus1 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus2 Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .name = "SLIMBUS_2_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_2_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
-
- }, {
- .name = "SLIMBUS_2_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_2_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus2 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus3 Playback",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .name = "SLIMBUS_3_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_3_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
-
- }, {
- .name = "SLIMBUS_3_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_3_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus3 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus4 Playback",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .name = "SLIMBUS_4_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_4_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
-
- }, {
- .name = "SLIMBUS_4_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_4_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus4 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus5 Playback",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .name = "SLIMBUS_5_RX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_5_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
-
- }, {
- .name = "SLIMBUS_5_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_5_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus5 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Slimbus6 Playback",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .ops = &q6slim_ops,
- .name = "SLIMBUS_6_RX",
- .id = SLIMBUS_6_RX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
-
- }, {
- .name = "SLIMBUS_6_TX",
- .ops = &q6slim_ops,
- .id = SLIMBUS_6_TX,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- .capture = {
- .stream_name = "Slimbus6 Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- }, {
- .playback = {
- .stream_name = "Primary MI2S Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = PRIMARY_MI2S_RX,
- .name = "PRI_MI2S_RX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .capture = {
- .stream_name = "Primary MI2S Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = PRIMARY_MI2S_TX,
- .name = "PRI_MI2S_TX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .playback = {
- .stream_name = "Secondary MI2S Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .name = "SEC_MI2S_RX",
- .id = SECONDARY_MI2S_RX,
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .capture = {
- .stream_name = "Secondary MI2S Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = SECONDARY_MI2S_TX,
- .name = "SEC_MI2S_TX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .playback = {
- .stream_name = "Tertiary MI2S Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .name = "TERT_MI2S_RX",
- .id = TERTIARY_MI2S_RX,
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .capture = {
- .stream_name = "Tertiary MI2S Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = TERTIARY_MI2S_TX,
- .name = "TERT_MI2S_TX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .playback = {
- .stream_name = "Quaternary MI2S Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .name = "QUAT_MI2S_RX",
- .id = QUATERNARY_MI2S_RX,
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .capture = {
- .stream_name = "Quaternary MI2S Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = QUATERNARY_MI2S_TX,
- .name = "QUAT_MI2S_TX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .playback = {
- .stream_name = "Quinary MI2S Playback",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .id = QUINARY_MI2S_RX,
- .name = "QUIN_MI2S_RX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- }, {
- .capture = {
- .stream_name = "Quinary MI2S Capture",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
- },
- .id = QUINARY_MI2S_TX,
- .name = "QUIN_MI2S_TX",
- .ops = &q6i2s_ops,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- },
- Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0),
- Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1),
- Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2),
- Q6AFE_TDM_PB_DAI("Primary", 3, PRIMARY_TDM_RX_3),
- Q6AFE_TDM_PB_DAI("Primary", 4, PRIMARY_TDM_RX_4),
- Q6AFE_TDM_PB_DAI("Primary", 5, PRIMARY_TDM_RX_5),
- Q6AFE_TDM_PB_DAI("Primary", 6, PRIMARY_TDM_RX_6),
- Q6AFE_TDM_PB_DAI("Primary", 7, PRIMARY_TDM_RX_7),
- Q6AFE_TDM_CAP_DAI("Primary", 0, PRIMARY_TDM_TX_0),
- Q6AFE_TDM_CAP_DAI("Primary", 1, PRIMARY_TDM_TX_1),
- Q6AFE_TDM_CAP_DAI("Primary", 2, PRIMARY_TDM_TX_2),
- Q6AFE_TDM_CAP_DAI("Primary", 3, PRIMARY_TDM_TX_3),
- Q6AFE_TDM_CAP_DAI("Primary", 4, PRIMARY_TDM_TX_4),
- Q6AFE_TDM_CAP_DAI("Primary", 5, PRIMARY_TDM_TX_5),
- Q6AFE_TDM_CAP_DAI("Primary", 6, PRIMARY_TDM_TX_6),
- Q6AFE_TDM_CAP_DAI("Primary", 7, PRIMARY_TDM_TX_7),
- Q6AFE_TDM_PB_DAI("Secondary", 0, SECONDARY_TDM_RX_0),
- Q6AFE_TDM_PB_DAI("Secondary", 1, SECONDARY_TDM_RX_1),
- Q6AFE_TDM_PB_DAI("Secondary", 2, SECONDARY_TDM_RX_2),
- Q6AFE_TDM_PB_DAI("Secondary", 3, SECONDARY_TDM_RX_3),
- Q6AFE_TDM_PB_DAI("Secondary", 4, SECONDARY_TDM_RX_4),
- Q6AFE_TDM_PB_DAI("Secondary", 5, SECONDARY_TDM_RX_5),
- Q6AFE_TDM_PB_DAI("Secondary", 6, SECONDARY_TDM_RX_6),
- Q6AFE_TDM_PB_DAI("Secondary", 7, SECONDARY_TDM_RX_7),
- Q6AFE_TDM_CAP_DAI("Secondary", 0, SECONDARY_TDM_TX_0),
- Q6AFE_TDM_CAP_DAI("Secondary", 1, SECONDARY_TDM_TX_1),
- Q6AFE_TDM_CAP_DAI("Secondary", 2, SECONDARY_TDM_TX_2),
- Q6AFE_TDM_CAP_DAI("Secondary", 3, SECONDARY_TDM_TX_3),
- Q6AFE_TDM_CAP_DAI("Secondary", 4, SECONDARY_TDM_TX_4),
- Q6AFE_TDM_CAP_DAI("Secondary", 5, SECONDARY_TDM_TX_5),
- Q6AFE_TDM_CAP_DAI("Secondary", 6, SECONDARY_TDM_TX_6),
- Q6AFE_TDM_CAP_DAI("Secondary", 7, SECONDARY_TDM_TX_7),
- Q6AFE_TDM_PB_DAI("Tertiary", 0, TERTIARY_TDM_RX_0),
- Q6AFE_TDM_PB_DAI("Tertiary", 1, TERTIARY_TDM_RX_1),
- Q6AFE_TDM_PB_DAI("Tertiary", 2, TERTIARY_TDM_RX_2),
- Q6AFE_TDM_PB_DAI("Tertiary", 3, TERTIARY_TDM_RX_3),
- Q6AFE_TDM_PB_DAI("Tertiary", 4, TERTIARY_TDM_RX_4),
- Q6AFE_TDM_PB_DAI("Tertiary", 5, TERTIARY_TDM_RX_5),
- Q6AFE_TDM_PB_DAI("Tertiary", 6, TERTIARY_TDM_RX_6),
- Q6AFE_TDM_PB_DAI("Tertiary", 7, TERTIARY_TDM_RX_7),
- Q6AFE_TDM_CAP_DAI("Tertiary", 0, TERTIARY_TDM_TX_0),
- Q6AFE_TDM_CAP_DAI("Tertiary", 1, TERTIARY_TDM_TX_1),
- Q6AFE_TDM_CAP_DAI("Tertiary", 2, TERTIARY_TDM_TX_2),
- Q6AFE_TDM_CAP_DAI("Tertiary", 3, TERTIARY_TDM_TX_3),
- Q6AFE_TDM_CAP_DAI("Tertiary", 4, TERTIARY_TDM_TX_4),
- Q6AFE_TDM_CAP_DAI("Tertiary", 5, TERTIARY_TDM_TX_5),
- Q6AFE_TDM_CAP_DAI("Tertiary", 6, TERTIARY_TDM_TX_6),
- Q6AFE_TDM_CAP_DAI("Tertiary", 7, TERTIARY_TDM_TX_7),
- Q6AFE_TDM_PB_DAI("Quaternary", 0, QUATERNARY_TDM_RX_0),
- Q6AFE_TDM_PB_DAI("Quaternary", 1, QUATERNARY_TDM_RX_1),
- Q6AFE_TDM_PB_DAI("Quaternary", 2, QUATERNARY_TDM_RX_2),
- Q6AFE_TDM_PB_DAI("Quaternary", 3, QUATERNARY_TDM_RX_3),
- Q6AFE_TDM_PB_DAI("Quaternary", 4, QUATERNARY_TDM_RX_4),
- Q6AFE_TDM_PB_DAI("Quaternary", 5, QUATERNARY_TDM_RX_5),
- Q6AFE_TDM_PB_DAI("Quaternary", 6, QUATERNARY_TDM_RX_6),
- Q6AFE_TDM_PB_DAI("Quaternary", 7, QUATERNARY_TDM_RX_7),
- Q6AFE_TDM_CAP_DAI("Quaternary", 0, QUATERNARY_TDM_TX_0),
- Q6AFE_TDM_CAP_DAI("Quaternary", 1, QUATERNARY_TDM_TX_1),
- Q6AFE_TDM_CAP_DAI("Quaternary", 2, QUATERNARY_TDM_TX_2),
- Q6AFE_TDM_CAP_DAI("Quaternary", 3, QUATERNARY_TDM_TX_3),
- Q6AFE_TDM_CAP_DAI("Quaternary", 4, QUATERNARY_TDM_TX_4),
- Q6AFE_TDM_CAP_DAI("Quaternary", 5, QUATERNARY_TDM_TX_5),
- Q6AFE_TDM_CAP_DAI("Quaternary", 6, QUATERNARY_TDM_TX_6),
- Q6AFE_TDM_CAP_DAI("Quaternary", 7, QUATERNARY_TDM_TX_7),
- Q6AFE_TDM_PB_DAI("Quinary", 0, QUINARY_TDM_RX_0),
- Q6AFE_TDM_PB_DAI("Quinary", 1, QUINARY_TDM_RX_1),
- Q6AFE_TDM_PB_DAI("Quinary", 2, QUINARY_TDM_RX_2),
- Q6AFE_TDM_PB_DAI("Quinary", 3, QUINARY_TDM_RX_3),
- Q6AFE_TDM_PB_DAI("Quinary", 4, QUINARY_TDM_RX_4),
- Q6AFE_TDM_PB_DAI("Quinary", 5, QUINARY_TDM_RX_5),
- Q6AFE_TDM_PB_DAI("Quinary", 6, QUINARY_TDM_RX_6),
- Q6AFE_TDM_PB_DAI("Quinary", 7, QUINARY_TDM_RX_7),
- Q6AFE_TDM_CAP_DAI("Quinary", 0, QUINARY_TDM_TX_0),
- Q6AFE_TDM_CAP_DAI("Quinary", 1, QUINARY_TDM_TX_1),
- Q6AFE_TDM_CAP_DAI("Quinary", 2, QUINARY_TDM_TX_2),
- Q6AFE_TDM_CAP_DAI("Quinary", 3, QUINARY_TDM_TX_3),
- Q6AFE_TDM_CAP_DAI("Quinary", 4, QUINARY_TDM_TX_4),
- Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5),
- Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6),
- Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7),
- {
- .playback = {
- .stream_name = "Display Port Playback",
- .rates = SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 2,
- .channels_max = 8,
- .rate_max = 192000,
- .rate_min = 48000,
- },
- .ops = &q6hdmi_ops,
- .id = DISPLAY_PORT_RX,
- .name = "DISPLAY_PORT",
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- },
- Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0),
- Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0),
- Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1),
- Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1),
- Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2),
- Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0),
- Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1),
- Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5),
- Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6),
- Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7),
-};
-
-static int q6afe_of_xlate_dai_name(struct snd_soc_component *component,
- const struct of_phandle_args *args,
- const char **dai_name)
-{
- int id = args->args[0];
- int ret = -EINVAL;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(q6afe_dais); i++) {
- if (q6afe_dais[i].id == id) {
- *dai_name = q6afe_dais[i].name;
- ret = 0;
- break;
- }
- }
-
- return ret;
-}
-
static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = {
SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -1627,7 +960,7 @@ static const struct snd_soc_component_driver q6afe_dai_component = {
.num_dapm_widgets = ARRAY_SIZE(q6afe_dai_widgets),
.dapm_routes = q6afe_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(q6afe_dapm_routes),
- .of_xlate_dai_name = q6afe_of_xlate_dai_name,
+ .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
};
@@ -1715,19 +1048,29 @@ static void of_q6afe_parse_dai_data(struct device *dev,
static int q6afe_dai_dev_probe(struct platform_device *pdev)
{
+ struct q6dsp_audio_port_dai_driver_config cfg;
+ struct snd_soc_dai_driver *dais;
struct q6afe_dai_data *dai_data;
struct device *dev = &pdev->dev;
+ int num_dais;
dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
if (!dai_data)
return -ENOMEM;
dev_set_drvdata(dev, dai_data);
-
of_q6afe_parse_dai_data(dev, dai_data);
- return devm_snd_soc_register_component(dev, &q6afe_dai_component,
- q6afe_dais, ARRAY_SIZE(q6afe_dais));
+ cfg.probe = msm_dai_q6_dai_probe;
+ cfg.remove = msm_dai_q6_dai_remove;
+ cfg.q6hdmi_ops = &q6hdmi_ops;
+ cfg.q6slim_ops = &q6slim_ops;
+ cfg.q6i2s_ops = &q6i2s_ops;
+ cfg.q6tdm_ops = &q6tdm_ops;
+ cfg.q6dma_ops = &q6dma_ops;
+ dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
+
+ return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);
}
#ifdef CONFIG_OF
@@ -1747,5 +1090,5 @@ static struct platform_driver q6afe_dai_platform_driver = {
};
module_platform_driver(q6afe_dai_platform_driver);
-MODULE_DESCRIPTION("Q6 Audio Fronend dai driver");
+MODULE_DESCRIPTION("Q6 Audio Frontend dai driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
new file mode 100644
index 000000000000..eb1c3aec479b
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <sound/pcm_params.h>
+#include "q6apm.h"
+
+#define DRV_NAME "q6apm-dai"
+
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 65536
+#define PLAYBACK_MIN_PERIOD_SIZE 128
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 4096
+#define CAPTURE_MIN_PERIOD_SIZE 320
+#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
+#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
+#define SID_MASK_DEFAULT 0xF
+
+enum stream_state {
+ Q6APM_STREAM_IDLE = 0,
+ Q6APM_STREAM_STOPPED,
+ Q6APM_STREAM_RUNNING,
+};
+
+struct q6apm_dai_rtd {
+ struct snd_pcm_substream *substream;
+ struct snd_compr_stream *cstream;
+ struct snd_compr_params codec_param;
+ struct snd_dma_buffer dma_buffer;
+ phys_addr_t phys;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pos; /* Buffer position */
+ unsigned int periods;
+ unsigned int bytes_sent;
+ unsigned int bytes_received;
+ unsigned int copied_total;
+ uint16_t bits_per_sample;
+ uint16_t source; /* Encoding source bit mask */
+ uint16_t session_id;
+ enum stream_state state;
+ struct q6apm_graph *graph;
+};
+
+struct q6apm_dai_data {
+ long long sid;
+};
+
+static struct snd_pcm_hardware q6apm_dai_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware q6apm_dai_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE),
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv)
+{
+ struct q6apm_dai_rtd *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+
+ switch (opcode) {
+ case APM_CLIENT_EVENT_CMD_EOS_DONE:
+ prtd->state = Q6APM_STREAM_STOPPED;
+ break;
+ case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ prtd->pos += prtd->pcm_count;
+ snd_pcm_period_elapsed(substream);
+ if (prtd->state == Q6APM_STREAM_RUNNING)
+ q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
+
+ break;
+ case APM_CLIENT_EVENT_DATA_READ_DONE:
+ prtd->pos += prtd->pcm_count;
+ snd_pcm_period_elapsed(substream);
+ if (prtd->state == Q6APM_STREAM_RUNNING)
+ q6apm_read(prtd->graph);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int q6apm_dai_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct audioreach_module_config cfg;
+ struct device *dev = component->dev;
+ struct q6apm_dai_data *pdata;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ if (!prtd || !prtd->graph) {
+ dev_err(dev, "%s: private data null or audio client freed\n", __func__);
+ return -EINVAL;
+ }
+
+ cfg.direction = substream->stream;
+ cfg.sample_rate = runtime->rate;
+ cfg.num_channels = runtime->channels;
+ cfg.bit_width = prtd->bits_per_sample;
+
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pos = 0;
+ /* rate and channels are sent to audio driver */
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
+ if (ret < 0) {
+ dev_err(dev, "%s: q6apm_open_write failed\n", __func__);
+ return ret;
+ }
+
+ ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
+ if (ret < 0)
+ dev_err(dev, "%s: CMD Format block failed\n", __func__);
+
+ ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys,
+ (prtd->pcm_size / prtd->periods), prtd->periods);
+
+ if (ret < 0) {
+ dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+
+ ret = q6apm_graph_prepare(prtd->graph);
+ if (ret) {
+ dev_err(dev, "Failed to prepare Graph %d\n", ret);
+ return ret;
+ }
+
+ ret = q6apm_graph_start(prtd->graph);
+ if (ret) {
+ dev_err(dev, "Failed to Start Graph %d\n", ret);
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ int i;
+ /* Queue the buffers for Capture ONLY after graph is started */
+ for (i = 0; i < runtime->periods; i++)
+ q6apm_read(prtd->graph);
+
+ }
+
+ /* Now that graph as been prepared and started update the internal state accordingly */
+ prtd->state = Q6APM_STREAM_RUNNING;
+
+ return 0;
+}
+
+static int q6apm_dai_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* start writing buffers for playback only as we already queued capture buffers */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* TODO support be handled via SoftPause Module */
+ prtd->state = Q6APM_STREAM_STOPPED;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0);
+ struct device *dev = component->dev;
+ struct q6apm_dai_data *pdata;
+ struct q6apm_dai_rtd *prtd;
+ int graph_id, ret;
+
+ graph_id = cpu_dai->driver->id;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata) {
+ dev_err(dev, "Drv data not found ..\n");
+ return -EINVAL;
+ }
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->substream = substream;
+ prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id);
+ if (IS_ERR(prtd->graph)) {
+ dev_err(dev, "%s: Could not allocate memory\n", __func__);
+ ret = PTR_ERR(prtd->graph);
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = q6apm_dai_hardware_playback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = q6apm_dai_hardware_capture;
+
+ /* 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) {
+ dev_err(dev, "snd_pcm_hw_constraint_integer failed\n");
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);
+ goto err;
+ }
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret < 0) {
+ dev_err(dev, "constraint for period bytes step ret = %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);
+ goto err;
+ }
+
+ runtime->private_data = prtd;
+ runtime->dma_bytes = BUFFER_BYTES_MAX;
+ if (pdata->sid < 0)
+ prtd->phys = substream->dma_buffer.addr;
+ else
+ prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
+
+ return 0;
+err:
+ kfree(prtd);
+
+ return ret;
+}
+
+static int q6apm_dai_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, substream->stream);
+ q6apm_graph_close(prtd->graph);
+ prtd->graph = NULL;
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ if (prtd->pos == prtd->pcm_size)
+ prtd->pos = 0;
+
+ return bytes_to_frames(runtime, prtd->pos);
+}
+
+static int q6apm_dai_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 q6apm_dai_rtd *prtd = runtime->private_data;
+
+ prtd->pcm_size = params_buffer_bytes(params);
+ prtd->periods = params_periods(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ prtd->bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ prtd->bits_per_sample = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)
+{
+ int size = BUFFER_BYTES_MAX;
+
+ return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
+}
+
+static const struct snd_soc_component_driver q6apm_fe_dai_component = {
+ .name = DRV_NAME,
+ .open = q6apm_dai_open,
+ .close = q6apm_dai_close,
+ .prepare = q6apm_dai_prepare,
+ .pcm_construct = q6apm_dai_pcm_new,
+ .hw_params = q6apm_dai_hw_params,
+ .pointer = q6apm_dai_pointer,
+ .trigger = q6apm_dai_trigger,
+};
+
+static int q6apm_dai_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct q6apm_dai_data *pdata;
+ struct of_phandle_args args;
+ int rc;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+ if (rc < 0)
+ pdata->sid = -1;
+ else
+ pdata->sid = args.args[0] & SID_MASK_DEFAULT;
+
+ dev_set_drvdata(dev, pdata);
+
+ return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6apm_dai_device_id[] = {
+ { .compatible = "qcom,q6apm-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6apm_dai_device_id);
+#endif
+
+static struct platform_driver q6apm_dai_platform_driver = {
+ .driver = {
+ .name = "q6apm-dai",
+ .of_match_table = of_match_ptr(q6apm_dai_device_id),
+ },
+ .probe = q6apm_dai_probe,
+};
+module_platform_driver(q6apm_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6APM dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
new file mode 100644
index 000000000000..ce9e5646d8f3
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "q6dsp-lpass-ports.h"
+#include "audioreach.h"
+#include "q6apm.h"
+
+#define AUDIOREACH_BE_PCM_BASE 16
+
+struct q6apm_lpass_dai_data {
+ struct q6apm_graph *graph[APM_PORT_MAX];
+ bool is_port_started[APM_PORT_MAX];
+ struct audioreach_module_config module_config[APM_PORT_MAX];
+};
+
+static int q6dma_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_ch_mask,
+ unsigned int rx_num, unsigned int *rx_ch_mask)
+{
+
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int ch_mask;
+
+ switch (dai->id) {
+ case WSA_CODEC_DMA_TX_0:
+ case WSA_CODEC_DMA_TX_1:
+ case WSA_CODEC_DMA_TX_2:
+ case VA_CODEC_DMA_TX_0:
+ case VA_CODEC_DMA_TX_1:
+ case VA_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ case TX_CODEC_DMA_TX_4:
+ case TX_CODEC_DMA_TX_5:
+ if (!tx_ch_mask) {
+ dev_err(dai->dev, "tx slot not found\n");
+ return -EINVAL;
+ }
+
+ if (tx_num > AR_PCM_MAX_NUM_CHANNEL) {
+ dev_err(dai->dev, "invalid tx num %d\n",
+ tx_num);
+ return -EINVAL;
+ }
+ ch_mask = *tx_ch_mask;
+
+ break;
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_2:
+ case RX_CODEC_DMA_RX_3:
+ case RX_CODEC_DMA_RX_4:
+ case RX_CODEC_DMA_RX_5:
+ case RX_CODEC_DMA_RX_6:
+ case RX_CODEC_DMA_RX_7:
+ /* rx */
+ if (!rx_ch_mask) {
+ dev_err(dai->dev, "rx slot not found\n");
+ return -EINVAL;
+ }
+ if (rx_num > APM_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid rx num %d\n",
+ rx_num);
+ return -EINVAL;
+ }
+ ch_mask = *rx_ch_mask;
+
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ cfg->active_channels_mask = ch_mask;
+
+ return 0;
+}
+
+static int q6dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = params_channels(params);
+
+ return 0;
+}
+
+static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc;
+
+ if (!dai_data->is_port_started[dai->id])
+ return;
+ rc = q6apm_graph_stop(dai_data->graph[dai->id]);
+ if (rc < 0)
+ dev_err(dai->dev, "fail to close APM port (%d)\n", rc);
+
+ q6apm_graph_close(dai_data->graph[dai->id]);
+ dai_data->is_port_started[dai->id] = false;
+}
+
+static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ struct q6apm_graph *graph;
+ int graph_id = dai->id;
+ int rc;
+
+ /**
+ * It is recommend to load DSP with source graph first and then sink
+ * graph, so sequence for playback and capture will be different
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
+ if (IS_ERR(graph)) {
+ dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
+ rc = PTR_ERR(graph);
+ return rc;
+ }
+ dai_data->graph[graph_id] = graph;
+ }
+
+ cfg->direction = substream->stream;
+ rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg);
+
+ if (rc) {
+ dev_err(dai->dev, "Failed to set media format %d\n", rc);
+ return rc;
+ }
+
+ rc = q6apm_graph_prepare(dai_data->graph[dai->id]);
+ if (rc) {
+ dev_err(dai->dev, "Failed to prepare Graph %d\n", rc);
+ return rc;
+ }
+
+ rc = q6apm_graph_start(dai_data->graph[dai->id]);
+ if (rc < 0) {
+ dev_err(dai->dev, "fail to start APM port %x\n", dai->id);
+ return rc;
+ }
+ dai_data->is_port_started[dai->id] = true;
+
+ return 0;
+}
+
+static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6apm_graph *graph;
+ int graph_id = dai->id;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
+ if (IS_ERR(graph)) {
+ dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
+ return PTR_ERR(graph);
+ }
+ dai_data->graph[graph_id] = graph;
+ }
+
+ return 0;
+}
+
+static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+
+ cfg->fmt = fmt;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops q6dma_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .set_channel_map = q6dma_set_channel_map,
+ .hw_params = q6dma_hw_params,
+};
+
+static const struct snd_soc_dai_ops q6i2s_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .set_channel_map = q6dma_set_channel_map,
+ .hw_params = q6dma_hw_params,
+ .set_fmt = q6i2s_set_fmt,
+};
+
+static const struct snd_soc_component_driver q6apm_lpass_dai_component = {
+ .name = "q6apm-be-dai-component",
+ .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
+ .be_pcm_base = AUDIOREACH_BE_PCM_BASE,
+ .use_dai_pcm_id = true,
+};
+
+static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
+{
+ struct q6dsp_audio_port_dai_driver_config cfg;
+ struct q6apm_lpass_dai_data *dai_data;
+ struct snd_soc_dai_driver *dais;
+ struct device *dev = &pdev->dev;
+ int num_dais;
+
+ dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
+ if (!dai_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dai_data);
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.q6i2s_ops = &q6i2s_ops;
+ cfg.q6dma_ops = &q6dma_ops;
+ dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
+
+ return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6apm_lpass_dai_device_id[] = {
+ { .compatible = "qcom,q6apm-lpass-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6apm_lpass_dai_device_id);
+#endif
+
+static struct platform_driver q6apm_lpass_dai_platform_driver = {
+ .driver = {
+ .name = "q6apm-lpass-dais",
+ .of_match_table = of_match_ptr(q6apm_lpass_dai_device_id),
+ },
+ .probe = q6apm_lpass_dai_dev_probe,
+};
+module_platform_driver(q6apm_lpass_dai_platform_driver);
+
+MODULE_DESCRIPTION("AUDIOREACH APM LPASS dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
new file mode 100644
index 000000000000..13598ef5bacb
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <dt-bindings/soc/qcom,gpr.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/wait.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include "audioreach.h"
+#include "q6apm.h"
+
+/* Graph Management */
+struct apm_graph_mgmt_cmd {
+ struct apm_module_param_data param_data;
+ uint32_t num_sub_graphs;
+ uint32_t sub_graph_id_list[];
+} __packed;
+
+#define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
+
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+ gpr_device_t *gdev = apm->gdev;
+
+ return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
+ NULL, &apm->wait, pkt, rsp_opcode);
+}
+
+static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id)
+{
+ struct audioreach_graph_info *info;
+ struct audioreach_graph *graph;
+ int id;
+
+ mutex_lock(&apm->lock);
+ graph = idr_find(&apm->graph_idr, graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (graph) {
+ kref_get(&graph->refcount);
+ return graph;
+ }
+
+ info = idr_find(&apm->graph_info_idr, graph_id);
+
+ if (!info)
+ return ERR_PTR(-ENODEV);
+
+ graph = kzalloc(sizeof(*graph), GFP_KERNEL);
+ if (!graph)
+ return ERR_PTR(-ENOMEM);
+
+ graph->apm = apm;
+ graph->info = info;
+ graph->id = graph_id;
+
+ graph->graph = audioreach_alloc_graph_pkt(apm, &info->sg_list, graph_id);
+ if (IS_ERR(graph->graph)) {
+ void *err = graph->graph;
+
+ kfree(graph);
+ return ERR_CAST(err);
+ }
+
+ mutex_lock(&apm->lock);
+ id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
+ kfree(graph);
+ mutex_unlock(&apm->lock);
+ return ERR_PTR(id);
+ }
+ mutex_unlock(&apm->lock);
+
+ kref_init(&graph->refcount);
+
+ q6apm_send_cmd_sync(apm, graph->graph, 0);
+
+ return graph;
+}
+
+static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
+{
+ struct audioreach_graph_info *info = graph->info;
+ int num_sub_graphs = info->num_sub_graphs;
+ struct apm_module_param_data *param_data;
+ struct apm_graph_mgmt_cmd *mgmt_cmd;
+ struct audioreach_sub_graph *sg;
+ struct q6apm *apm = graph->apm;
+ int i = 0, rc, payload_size;
+ struct gpr_pkt *pkt;
+
+ payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ mgmt_cmd->num_sub_graphs = num_sub_graphs;
+
+ param_data = &mgmt_cmd->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ list_for_each_entry(sg, &info->sg_list, node)
+ mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
+
+ rc = q6apm_send_cmd_sync(apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+static void q6apm_put_audioreach_graph(struct kref *ref)
+{
+ struct audioreach_graph *graph;
+ struct q6apm *apm;
+
+ graph = container_of(ref, struct audioreach_graph, refcount);
+ apm = graph->apm;
+
+ audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
+
+ mutex_lock(&apm->lock);
+ graph = idr_remove(&apm->graph_idr, graph->id);
+ mutex_unlock(&apm->lock);
+
+ kfree(graph->graph);
+ kfree(graph);
+}
+
+static int q6apm_get_apm_state(struct q6apm *apm)
+{
+ struct gpr_pkt *pkt;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
+
+ kfree(pkt);
+
+ return apm->state;
+}
+
+static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
+ struct audioreach_graph_info *info,
+ uint32_t mid)
+{
+ struct audioreach_container *container;
+ struct audioreach_sub_graph *sgs;
+ struct audioreach_module *module;
+
+ list_for_each_entry(sgs, &info->sg_list, node) {
+ list_for_each_entry(container, &sgs->container_list, node) {
+ list_for_each_entry(module, &container->modules_list, node) {
+ if (mid == module->module_id)
+ return module;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static struct audioreach_module *q6apm_graph_get_last_module(struct q6apm *apm, u32 sgid)
+{
+ struct audioreach_container *container;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sg;
+
+ mutex_lock(&apm->lock);
+ sg = idr_find(&apm->sub_graphs_idr, sgid);
+ mutex_unlock(&apm->lock);
+ if (!sg)
+ return NULL;
+
+ container = list_last_entry(&sg->container_list, struct audioreach_container, node);
+ module = audioreach_get_container_last_module(container);
+
+ return module;
+}
+
+static struct audioreach_module *q6apm_graph_get_first_module(struct q6apm *apm, u32 sgid)
+{
+ struct audioreach_container *container;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sg;
+
+ mutex_lock(&apm->lock);
+ sg = idr_find(&apm->sub_graphs_idr, sgid);
+ mutex_unlock(&apm->lock);
+ if (!sg)
+ return NULL;
+
+ container = list_first_entry(&sg->container_list, struct audioreach_container, node);
+ module = audioreach_get_container_first_module(container);
+
+ return module;
+}
+
+bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid, u32 dst_sgid)
+{
+ struct audioreach_module *module;
+ u32 iid;
+
+ module = q6apm_graph_get_last_module(apm, src_sgid);
+ if (!module)
+ return false;
+
+ iid = module->instance_id;
+ module = q6apm_graph_get_first_module(apm, dst_sgid);
+ if (!module)
+ return false;
+
+ if (module->src_mod_inst_id == iid)
+ return true;
+
+ return false;
+}
+
+int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid, bool connect)
+{
+ struct audioreach_module *module;
+ u32 iid;
+
+ if (connect) {
+ module = q6apm_graph_get_last_module(apm, src_sgid);
+ if (!module)
+ return -ENODEV;
+
+ iid = module->instance_id;
+ } else {
+ iid = 0;
+ }
+
+ module = q6apm_graph_get_first_module(apm, dst_sgid);
+ if (!module)
+ return -ENODEV;
+
+ /* set src module in dst subgraph first module */
+ module->src_mod_inst_id = iid;
+
+ return 0;
+}
+
+int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg)
+{
+ struct audioreach_module *module;
+
+ if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
+ else
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
+
+ if (!module)
+ return -ENODEV;
+
+ audioreach_set_media_format(graph, module, cfg);
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
+
+int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
+ size_t period_sz, unsigned int periods)
+{
+ struct audioreach_graph_data *data;
+ struct audio_buffer *buf;
+ int cnt;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ mutex_lock(&graph->lock);
+
+ if (data->buf) {
+ mutex_unlock(&graph->lock);
+ return 0;
+ }
+
+ buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL);
+ if (!buf) {
+ mutex_unlock(&graph->lock);
+ return -ENOMEM;
+ }
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ data->buf = buf;
+
+ buf[0].phys = phys;
+ buf[0].size = period_sz;
+
+ for (cnt = 1; cnt < periods; cnt++) {
+ if (period_sz > 0) {
+ buf[cnt].phys = buf[0].phys + (cnt * period_sz);
+ buf[cnt].size = period_sz;
+ }
+ }
+ data->num_periods = periods;
+
+ mutex_unlock(&graph->lock);
+
+ rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
+ if (rc < 0) {
+ dev_err(graph->dev, "Memory_map_regions failed\n");
+ audioreach_graph_free_buf(graph);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
+
+int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
+{
+ struct apm_cmd_shared_mem_unmap_regions *cmd;
+ struct audioreach_graph_data *data;
+ struct gpr_pkt *pkt;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ if (!data->mem_map_handle)
+ return 0;
+
+ pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
+ graph->port->id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cmd = (void *)pkt + GPR_HDR_SIZE;
+ cmd->mem_map_handle = data->mem_map_handle;
+
+ rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
+ kfree(pkt);
+
+ audioreach_graph_free_buf(graph);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
+
+int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
+{
+ struct audioreach_graph_info *info = graph->info;
+ struct audioreach_sub_graph *sgs;
+ struct audioreach_container *container;
+ struct audioreach_module *module;
+
+ list_for_each_entry(sgs, &info->sg_list, node) {
+ list_for_each_entry(container, &sgs->container_list, node) {
+ list_for_each_entry(module, &container->modules_list, node) {
+ if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
+ (module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
+ continue;
+
+ audioreach_set_media_format(graph, module, cfg);
+ }
+ }
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
+
+static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+
+int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
+
+int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t wflags)
+{
+ struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
+ struct audio_buffer *ab;
+ struct gpr_pkt *pkt;
+ int rc, iid;
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ pkt = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
+ graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
+ graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ write_buffer = (void *)pkt + GPR_HDR_SIZE;
+
+ mutex_lock(&graph->lock);
+ ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
+
+ write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
+ write_buffer->buf_addr_msw = upper_32_bits(ab->phys);
+ write_buffer->buf_size = len;
+ write_buffer->timestamp_lsw = lsw_ts;
+ write_buffer->timestamp_msw = msw_ts;
+ write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;
+ write_buffer->flags = wflags;
+
+ graph->rx_data.dsp_buf++;
+
+ if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
+ graph->rx_data.dsp_buf = 0;
+
+ mutex_unlock(&graph->lock);
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_write_async);
+
+int q6apm_read(struct q6apm_graph *graph)
+{
+ struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
+ struct audioreach_graph_data *port;
+ struct audio_buffer *ab;
+ struct gpr_pkt *pkt;
+ int rc, iid;
+
+ iid = q6apm_graph_get_tx_shmem_module_iid(graph);
+ pkt = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
+ graph->tx_data.dsp_buf, graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ read_buffer = (void *)pkt + GPR_HDR_SIZE;
+
+ mutex_lock(&graph->lock);
+ port = &graph->tx_data;
+ ab = &port->buf[port->dsp_buf];
+
+ read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
+ read_buffer->buf_addr_msw = upper_32_bits(ab->phys);
+ read_buffer->mem_map_handle = port->mem_map_handle;
+ read_buffer->buf_size = ab->size;
+
+ port->dsp_buf++;
+
+ if (port->dsp_buf >= port->num_periods)
+ port->dsp_buf = 0;
+
+ mutex_unlock(&graph->lock);
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_read);
+
+static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
+ struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
+ struct apm_cmd_rsp_shared_mem_map_regions *rsp;
+ struct gpr_ibasic_rsp_result_t *result;
+ struct q6apm_graph *graph = priv;
+ struct gpr_hdr *hdr = &data->hdr;
+ struct device *dev = graph->dev;
+ uint32_t client_event;
+ phys_addr_t phys;
+ int token;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
+ client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
+ mutex_lock(&graph->lock);
+ token = hdr->token & APM_WRITE_TOKEN_MASK;
+
+ done = data->payload;
+ phys = graph->rx_data.buf[token].phys;
+ mutex_unlock(&graph->lock);
+
+ if (lower_32_bits(phys) == done->buf_addr_lsw &&
+ upper_32_bits(phys) == done->buf_addr_msw) {
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = done->status;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ } else {
+ dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
+ done->buf_addr_msw);
+ }
+
+ break;
+ case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = 0;
+ rsp = data->payload;
+
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = rsp->mem_map_handle;
+ else
+ graph->tx_data.mem_map_handle = rsp->mem_map_handle;
+
+ wake_up(&graph->cmd_wait);
+ break;
+ case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
+ client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
+ mutex_lock(&graph->lock);
+ rd_done = data->payload;
+ phys = graph->tx_data.buf[hdr->token].phys;
+ mutex_unlock(&graph->lock);
+
+ if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
+ lower_32_bits(phys) == rd_done->buf_addr_lsw) {
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = rd_done->status;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ } else {
+ dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
+ rd_done->buf_addr_msw);
+ }
+ break;
+ case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
+ break;
+ case GPR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ graph->result.opcode = result->opcode;
+ graph->result.status = 0;
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = 0;
+ else
+ graph->tx_data.mem_map_handle = 0;
+
+ wake_up(&graph->cmd_wait);
+ break;
+ case APM_CMD_SHARED_MEM_MAP_REGIONS:
+ case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
+ case APM_CMD_SET_CFG:
+ graph->result.opcode = result->opcode;
+ graph->result.status = result->status;
+ if (result->status)
+ dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
+ result->status, result->opcode);
+ wake_up(&graph->cmd_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
+ void *priv, int graph_id)
+{
+ struct q6apm *apm = dev_get_drvdata(dev->parent);
+ struct audioreach_graph *ar_graph;
+ struct q6apm_graph *graph;
+ int ret;
+
+ ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
+ if (IS_ERR(ar_graph)) {
+ dev_err(dev, "No graph found with id %d\n", graph_id);
+ return ERR_CAST(ar_graph);
+ }
+
+ graph = kzalloc(sizeof(*graph), GFP_KERNEL);
+ if (!graph) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ graph->apm = apm;
+ graph->priv = priv;
+ graph->cb = cb;
+ graph->info = ar_graph->info;
+ graph->ar_graph = ar_graph;
+ graph->id = ar_graph->id;
+ graph->dev = dev;
+
+ mutex_init(&graph->lock);
+ init_waitqueue_head(&graph->cmd_wait);
+
+ graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
+ if (!graph->port) {
+ kfree(graph);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ return graph;
+err:
+ kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_open);
+
+int q6apm_graph_close(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+
+ gpr_free_port(graph->port);
+ kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
+ kfree(graph);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_close);
+
+int q6apm_graph_prepare(struct q6apm_graph *graph)
+{
+ return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
+
+int q6apm_graph_start(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+ int ret = 0;
+
+ if (ar_graph->start_count == 0)
+ ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
+
+ ar_graph->start_count++;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_start);
+
+int q6apm_graph_stop(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+
+ if (--ar_graph->start_count > 0)
+ return 0;
+
+ return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_stop);
+
+int q6apm_graph_flush(struct q6apm_graph *graph)
+{
+ return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_flush);
+
+static int q6apm_audio_probe(struct snd_soc_component *component)
+{
+ return audioreach_tplg_init(component);
+}
+
+static void q6apm_audio_remove(struct snd_soc_component *component)
+{
+ /* remove topology */
+ snd_soc_tplg_component_remove(component);
+}
+
+#define APM_AUDIO_DRV_NAME "q6apm-audio"
+
+static const struct snd_soc_component_driver q6apm_audio_component = {
+ .name = APM_AUDIO_DRV_NAME,
+ .probe = q6apm_audio_probe,
+ .remove = q6apm_audio_remove,
+};
+
+static int apm_probe(gpr_device_t *gdev)
+{
+ struct device *dev = &gdev->dev;
+ struct q6apm *apm;
+ int ret;
+
+ apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
+ if (!apm)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, apm);
+
+ mutex_init(&apm->lock);
+ apm->dev = dev;
+ apm->gdev = gdev;
+ init_waitqueue_head(&apm->wait);
+
+ idr_init(&apm->graph_idr);
+ idr_init(&apm->graph_info_idr);
+ idr_init(&apm->sub_graphs_idr);
+ idr_init(&apm->containers_idr);
+
+ idr_init(&apm->modules_idr);
+
+ q6apm_get_apm_state(apm);
+
+ 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);
+ return ret;
+ }
+
+ return of_platform_populate(dev->of_node, NULL, NULL, dev);
+}
+
+struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
+{
+ struct audioreach_graph_info *info = graph->info;
+ struct q6apm *apm = graph->apm;
+
+ return __q6apm_find_module_by_mid(apm, info, mid);
+
+}
+
+static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ gpr_device_t *gdev = priv;
+ struct q6apm *apm = dev_get_drvdata(&gdev->dev);
+ struct device *dev = &gdev->dev;
+ struct gpr_ibasic_rsp_result_t *result;
+ struct gpr_hdr *hdr = &data->hdr;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case APM_CMD_RSP_GET_SPF_STATE:
+ apm->result.opcode = hdr->opcode;
+ apm->result.status = 0;
+ /* First word of result it state */
+ apm->state = result->opcode;
+ wake_up(&apm->wait);
+ break;
+ case GPR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case APM_CMD_GRAPH_START:
+ case APM_CMD_GRAPH_OPEN:
+ case APM_CMD_GRAPH_PREPARE:
+ case APM_CMD_GRAPH_CLOSE:
+ case APM_CMD_GRAPH_FLUSH:
+ case APM_CMD_GRAPH_STOP:
+ case APM_CMD_SET_CFG:
+ apm->result.opcode = result->opcode;
+ apm->result.status = result->status;
+ if (result->status)
+ dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
+ result->opcode);
+ wake_up(&apm->wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id apm_device_id[] = {
+ { .compatible = "qcom,q6apm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, apm_device_id);
+#endif
+
+static gpr_driver_t apm_driver = {
+ .probe = apm_probe,
+ .gpr_callback = apm_callback,
+ .driver = {
+ .name = "qcom-apm",
+ .of_match_table = of_match_ptr(apm_device_id),
+ },
+};
+
+module_gpr_driver(apm_driver);
+MODULE_DESCRIPTION("Audio Process Manager");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
new file mode 100644
index 000000000000..54eadadf712c
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6APM_H__
+#define __Q6APM_H__
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <linux/of_platform.h>
+#include <linux/jiffies.h>
+#include <linux/soc/qcom/apr.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "audioreach.h"
+
+#define APM_PORT_MAX 127
+#define APM_PORT_MAX_AUDIO_CHAN_CNT 8
+#define PCM_CHANNEL_NULL 0
+#define PCM_CHANNEL_FL 1 /* Front left channel. */
+#define PCM_CHANNEL_FR 2 /* Front right channel. */
+#define PCM_CHANNEL_FC 3 /* Front center channel. */
+#define PCM_CHANNEL_LS 4 /* Left surround channel. */
+#define PCM_CHANNEL_RS 5 /* Right surround channel. */
+#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */
+#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center ch */
+#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */
+#define PCM_CHANNELS 10 /* Top surround channel. */
+
+#define APM_TIMESTAMP_FLAG 0x80000000
+#define FORMAT_LINEAR_PCM 0x0000
+/* APM client callback events */
+#define APM_CMD_EOS 0x0003
+#define APM_CLIENT_EVENT_CMD_EOS_DONE 0x1003
+#define APM_CMD_CLOSE 0x0004
+#define APM_CLIENT_EVENT_CMD_CLOSE_DONE 0x1004
+#define APM_CLIENT_EVENT_CMD_RUN_DONE 0x1008
+#define APM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009
+#define APM_CLIENT_EVENT_DATA_READ_DONE 0x100a
+#define APM_WRITE_TOKEN_MASK GENMASK(15, 0)
+#define APM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16)
+#define APM_WRITE_TOKEN_LEN_SHIFT 16
+
+#define APM_MAX_SESSIONS 8
+
+struct q6apm {
+ struct device *dev;
+ gpr_port_t *port;
+ gpr_device_t *gdev;
+ /* For Graph OPEN/START/STOP/CLOSE operations */
+ wait_queue_head_t wait;
+ struct gpr_ibasic_rsp_result_t result;
+
+ struct mutex cmd_lock;
+ struct mutex lock;
+ uint32_t state;
+
+ struct idr graph_idr;
+ struct idr graph_info_idr;
+ struct idr sub_graphs_idr;
+ struct idr containers_idr;
+ struct idr modules_idr;
+};
+
+struct audio_buffer {
+ phys_addr_t phys;
+ uint32_t size; /* size of buffer */
+};
+
+struct audioreach_graph_data {
+ struct audio_buffer *buf;
+ uint32_t num_periods;
+ uint32_t dsp_buf;
+ uint32_t mem_map_handle;
+};
+
+struct audioreach_graph {
+ struct audioreach_graph_info *info;
+ uint32_t id;
+ int state;
+ int start_count;
+ /* Cached Graph data */
+ void *graph;
+ struct kref refcount;
+ struct q6apm *apm;
+};
+
+typedef void (*q6apm_cb) (uint32_t opcode, uint32_t token,
+ void *payload, void *priv);
+struct q6apm_graph {
+ void *priv;
+ q6apm_cb cb;
+ uint32_t id;
+ struct device *dev;
+ struct q6apm *apm;
+ gpr_port_t *port;
+ struct audioreach_graph_data rx_data;
+ struct audioreach_graph_data tx_data;
+ struct gpr_ibasic_rsp_result_t result;
+ wait_queue_head_t cmd_wait;
+ struct mutex lock;
+ struct audioreach_graph *ar_graph;
+ struct audioreach_graph_info *info;
+};
+
+/* Graph Operations */
+struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
+ void *priv, int graph_id);
+int q6apm_graph_close(struct q6apm_graph *graph);
+int q6apm_graph_prepare(struct q6apm_graph *graph);
+int q6apm_graph_start(struct q6apm_graph *graph);
+int q6apm_graph_stop(struct q6apm_graph *graph);
+int q6apm_graph_flush(struct q6apm_graph *graph);
+
+/* Media Format */
+int q6apm_graph_media_format_pcm(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg);
+
+int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg);
+
+/* read/write related */
+int q6apm_send_eos_nowait(struct q6apm_graph *graph);
+int q6apm_read(struct q6apm_graph *graph);
+int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t wflags);
+
+/* Memory Map related */
+int q6apm_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, phys_addr_t phys,
+ size_t period_sz, unsigned int periods);
+int q6apm_unmap_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir);
+/* Helpers */
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode);
+
+/* Callback for graph specific */
+struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph,
+ uint32_t mid);
+
+void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv);
+int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid,
+ bool connect);
+bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid,
+ u32 dst_sgid);
+int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
+
+#endif /* __APM_GRAPH_ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
new file mode 100644
index 000000000000..4613867d1133
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6dsp-lpass-clocks.h"
+
+#define Q6DSP_MAX_CLK_ID 104
+#define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0
+
+
+struct q6dsp_clk {
+ struct device *dev;
+ int q6dsp_clk_id;
+ int attributes;
+ int rate;
+ uint32_t handle;
+ struct clk_hw hw;
+};
+
+#define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
+
+struct q6dsp_cc {
+ struct device *dev;
+ struct q6dsp_clk *clks[Q6DSP_MAX_CLK_ID];
+ const struct q6dsp_clk_desc *desc;
+};
+
+static int clk_q6dsp_prepare(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ return cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
+ Q6DSP_LPASS_CLK_ROOT_DEFAULT, clk->rate);
+}
+
+static void clk_q6dsp_unprepare(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
+ Q6DSP_LPASS_CLK_ROOT_DEFAULT, 0);
+}
+
+static int clk_q6dsp_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+
+ return clk->rate;
+}
+
+static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return rate;
+}
+
+static const struct clk_ops clk_q6dsp_ops = {
+ .prepare = clk_q6dsp_prepare,
+ .unprepare = clk_q6dsp_unprepare,
+ .set_rate = clk_q6dsp_set_rate,
+ .round_rate = clk_q6dsp_round_rate,
+ .recalc_rate = clk_q6dsp_recalc_rate,
+};
+
+static int clk_vote_q6dsp_block(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ return cc->desc->lpass_vote_clk(clk->dev, clk->q6dsp_clk_id,
+ clk_hw_get_name(&clk->hw), &clk->handle);
+}
+
+static void clk_unvote_q6dsp_block(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ cc->desc->lpass_unvote_clk(clk->dev, clk->q6dsp_clk_id, clk->handle);
+}
+
+static const struct clk_ops clk_vote_q6dsp_ops = {
+ .prepare = clk_vote_q6dsp_block,
+ .unprepare = clk_unvote_q6dsp_block,
+};
+
+
+static struct clk_hw *q6dsp_of_clk_hw_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct q6dsp_cc *cc = data;
+ unsigned int idx = clkspec->args[0];
+ unsigned int attr = clkspec->args[1];
+
+ if (idx >= Q6DSP_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
+ dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (cc->clks[idx]) {
+ cc->clks[idx]->attributes = attr;
+ return &cc->clks[idx]->hw;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+int q6dsp_clock_dev_probe(struct platform_device *pdev)
+{
+ struct q6dsp_cc *cc;
+ struct device *dev = &pdev->dev;
+ const struct q6dsp_clk_init *q6dsp_clks;
+ const struct q6dsp_clk_desc *desc;
+ int i, ret;
+
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ cc->desc = desc;
+ cc->dev = dev;
+ q6dsp_clks = desc->clks;
+
+ for (i = 0; i < desc->num_clks; i++) {
+ unsigned int id = q6dsp_clks[i].clk_id;
+ struct clk_init_data init = {
+ .name = q6dsp_clks[i].name,
+ };
+ struct q6dsp_clk *clk;
+
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+
+ clk->dev = dev;
+ clk->q6dsp_clk_id = q6dsp_clks[i].q6dsp_clk_id;
+ clk->rate = q6dsp_clks[i].rate;
+ clk->hw.init = &init;
+
+ if (clk->rate)
+ init.ops = &clk_q6dsp_ops;
+ else
+ init.ops = &clk_vote_q6dsp_ops;
+
+ cc->clks[id] = clk;
+
+ ret = devm_clk_hw_register(dev, &clk->hw);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, q6dsp_of_clk_hw_get, cc);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, cc);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe);
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h
new file mode 100644
index 000000000000..3770d81f2bd6
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_AUDIO_CLOCKS_H__
+#define __Q6DSP_AUDIO_CLOCKS_H__
+
+struct q6dsp_clk_init {
+ int clk_id;
+ int q6dsp_clk_id;
+ char *name;
+ int rate;
+};
+
+#define Q6DSP_VOTE_CLK(id, blkid, n) { \
+ .clk_id = id, \
+ .q6dsp_clk_id = blkid, \
+ .name = n, \
+ }
+
+struct q6dsp_clk_desc {
+ const struct q6dsp_clk_init *clks;
+ size_t num_clks;
+ int (*lpass_set_clk)(struct device *dev, int clk_id, int attr,
+ int root_clk, unsigned int freq);
+ int (*lpass_vote_clk)(struct device *dev, uint32_t hid, const char *n, uint32_t *h);
+ int (*lpass_unvote_clk)(struct device *dev, uint32_t hid, uint32_t h);
+};
+
+int q6dsp_clock_dev_probe(struct platform_device *pdev);
+
+#endif /* __Q6DSP_AUDIO_CLOCKS_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
new file mode 100644
index 000000000000..f67c16fd90b9
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include "q6dsp-lpass-ports.h"
+
+#define Q6AFE_TDM_PB_DAI(pre, num, did) { \
+ .playback = { \
+ .stream_name = pre" TDM"#num" Playback", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_TDM_CAP_DAI(pre, num, did) { \
+ .capture = { \
+ .stream_name = pre" TDM"#num" Capture", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_CDC_DMA_RX_DAI(did) { \
+ .playback = { \
+ .stream_name = #did" Playback", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_CDC_DMA_TX_DAI(did) { \
+ .capture = { \
+ .stream_name = #did" Capture", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+
+static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
+ {
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .id = HDMI_RX,
+ .name = "HDMI",
+ }, {
+ .name = "SLIMBUS_0_RX",
+ .id = SLIMBUS_0_RX,
+ .playback = {
+ .stream_name = "Slimbus Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .name = "SLIMBUS_0_TX",
+ .id = SLIMBUS_0_TX,
+ .capture = {
+ .stream_name = "Slimbus Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus1 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_1_RX",
+ .id = SLIMBUS_1_RX,
+ }, {
+ .name = "SLIMBUS_1_TX",
+ .id = SLIMBUS_1_TX,
+ .capture = {
+ .stream_name = "Slimbus1 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus2 Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_2_RX",
+ .id = SLIMBUS_2_RX,
+
+ }, {
+ .name = "SLIMBUS_2_TX",
+ .id = SLIMBUS_2_TX,
+ .capture = {
+ .stream_name = "Slimbus2 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus3 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_3_RX",
+ .id = SLIMBUS_3_RX,
+
+ }, {
+ .name = "SLIMBUS_3_TX",
+ .id = SLIMBUS_3_TX,
+ .capture = {
+ .stream_name = "Slimbus3 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus4 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_4_RX",
+ .id = SLIMBUS_4_RX,
+
+ }, {
+ .name = "SLIMBUS_4_TX",
+ .id = SLIMBUS_4_TX,
+ .capture = {
+ .stream_name = "Slimbus4 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus5 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_5_RX",
+ .id = SLIMBUS_5_RX,
+
+ }, {
+ .name = "SLIMBUS_5_TX",
+ .id = SLIMBUS_5_TX,
+ .capture = {
+ .stream_name = "Slimbus5 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus6 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_6_RX",
+ .id = SLIMBUS_6_RX,
+
+ }, {
+ .name = "SLIMBUS_6_TX",
+ .id = SLIMBUS_6_TX,
+ .capture = {
+ .stream_name = "Slimbus6 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Primary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = PRIMARY_MI2S_RX,
+ .name = "PRI_MI2S_RX",
+ }, {
+ .capture = {
+ .stream_name = "Primary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = PRIMARY_MI2S_TX,
+ .name = "PRI_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Secondary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "SEC_MI2S_RX",
+ .id = SECONDARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Secondary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = SECONDARY_MI2S_TX,
+ .name = "SEC_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Tertiary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "TERT_MI2S_RX",
+ .id = TERTIARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Tertiary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = TERTIARY_MI2S_TX,
+ .name = "TERT_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Quaternary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "QUAT_MI2S_RX",
+ .id = QUATERNARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Quaternary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = QUATERNARY_MI2S_TX,
+ .name = "QUAT_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Quinary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .id = QUINARY_MI2S_RX,
+ .name = "QUIN_MI2S_RX",
+ }, {
+ .capture = {
+ .stream_name = "Quinary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = QUINARY_MI2S_TX,
+ .name = "QUIN_MI2S_TX",
+ },
+ Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Primary", 3, PRIMARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Primary", 4, PRIMARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Primary", 5, PRIMARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Primary", 6, PRIMARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Primary", 7, PRIMARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Primary", 0, PRIMARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Primary", 1, PRIMARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Primary", 2, PRIMARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Primary", 3, PRIMARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Primary", 4, PRIMARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Primary", 5, PRIMARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Primary", 6, PRIMARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Primary", 7, PRIMARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Secondary", 0, SECONDARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Secondary", 1, SECONDARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Secondary", 2, SECONDARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Secondary", 3, SECONDARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Secondary", 4, SECONDARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Secondary", 5, SECONDARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Secondary", 6, SECONDARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Secondary", 7, SECONDARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Secondary", 0, SECONDARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Secondary", 1, SECONDARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Secondary", 2, SECONDARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Secondary", 3, SECONDARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Secondary", 4, SECONDARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Secondary", 5, SECONDARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Secondary", 6, SECONDARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Secondary", 7, SECONDARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Tertiary", 0, TERTIARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Tertiary", 1, TERTIARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Tertiary", 2, TERTIARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Tertiary", 3, TERTIARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Tertiary", 4, TERTIARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Tertiary", 5, TERTIARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Tertiary", 6, TERTIARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Tertiary", 7, TERTIARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 0, TERTIARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 1, TERTIARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 2, TERTIARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 3, TERTIARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 4, TERTIARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 5, TERTIARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 6, TERTIARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 7, TERTIARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Quaternary", 0, QUATERNARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Quaternary", 1, QUATERNARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Quaternary", 2, QUATERNARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Quaternary", 3, QUATERNARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Quaternary", 4, QUATERNARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Quaternary", 5, QUATERNARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Quaternary", 6, QUATERNARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Quaternary", 7, QUATERNARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 0, QUATERNARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 1, QUATERNARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 2, QUATERNARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 3, QUATERNARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 4, QUATERNARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 5, QUATERNARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 6, QUATERNARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 7, QUATERNARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Quinary", 0, QUINARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Quinary", 1, QUINARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Quinary", 2, QUINARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Quinary", 3, QUINARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Quinary", 4, QUINARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Quinary", 5, QUINARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Quinary", 6, QUINARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Quinary", 7, QUINARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Quinary", 0, QUINARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Quinary", 1, QUINARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Quinary", 2, QUINARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Quinary", 3, QUINARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Quinary", 4, QUINARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7),
+ {
+ .playback = {
+ .stream_name = "Display Port Playback",
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .id = DISPLAY_PORT_RX,
+ .name = "DISPLAY_PORT",
+ },
+ Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7),
+};
+
+int q6dsp_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) {
+ if (q6dsp_audio_fe_dais[i].id == id) {
+ *dai_name = q6dsp_audio_fe_dais[i].name;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6dsp_audio_ports_of_xlate_dai_name);
+
+struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
+ struct q6dsp_audio_port_dai_driver_config *cfg,
+ int *num_dais)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) {
+ q6dsp_audio_fe_dais[i].probe = cfg->probe;
+ q6dsp_audio_fe_dais[i].remove = cfg->remove;
+
+ switch (q6dsp_audio_fe_dais[i].id) {
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
+ break;
+ case SLIMBUS_0_RX ... SLIMBUS_6_TX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops;
+ break;
+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops;
+ break;
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6tdm_ops;
+ break;
+ case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6dma_ops;
+ break;
+ default:
+ break;
+ }
+ }
+
+ *num_dais = ARRAY_SIZE(q6dsp_audio_fe_dais);
+ return q6dsp_audio_fe_dais;
+}
+EXPORT_SYMBOL_GPL(q6dsp_audio_ports_set_config);
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
new file mode 100644
index 000000000000..7f052c8a1257
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_AUDIO_PORTS_H__
+#define __Q6DSP_AUDIO_PORTS_H__
+
+struct q6dsp_audio_port_dai_driver_config {
+ int (*probe)(struct snd_soc_dai *dai);
+ int (*remove)(struct snd_soc_dai *dai);
+ const struct snd_soc_dai_ops *q6hdmi_ops;
+ const struct snd_soc_dai_ops *q6slim_ops;
+ const struct snd_soc_dai_ops *q6i2s_ops;
+ const struct snd_soc_dai_ops *q6tdm_ops;
+ const struct snd_soc_dai_ops *q6dma_ops;
+};
+
+struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
+ struct q6dsp_audio_port_dai_driver_config *cfg,
+ int *num_dais);
+int q6dsp_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name);
+#endif /* __Q6DSP_AUDIO_PORTS_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c
new file mode 100644
index 000000000000..a26cda5140c1
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6dsp-lpass-clocks.h"
+#include "q6prm.h"
+
+#define Q6PRM_CLK(id) { \
+ .clk_id = id, \
+ .q6dsp_clk_id = Q6PRM_##id, \
+ .name = #id, \
+ .rate = 19200000, \
+ }
+
+static const struct q6dsp_clk_init q6prm_clks[] = {
+ Q6PRM_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_TER_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_TER_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_TX_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_TX_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK),
+ Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS,
+ "LPASS_HW_MACRO"),
+ Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC,
+ "LPASS_HW_DCODEC"),
+};
+
+static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = {
+ .clks = q6prm_clks,
+ .num_clks = ARRAY_SIZE(q6prm_clks),
+ .lpass_set_clk = q6prm_set_lpass_clock,
+ .lpass_vote_clk = q6prm_vote_lpass_core_hw,
+ .lpass_unvote_clk = q6prm_unvote_lpass_core_hw,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6prm_clock_device_id[] = {
+ { .compatible = "qcom,q6prm-lpass-clocks", .data = &q6dsp_clk_q6prm },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6prm_clock_device_id);
+#endif
+
+static struct platform_driver q6prm_clock_platform_driver = {
+ .driver = {
+ .name = "q6prm-lpass-clock",
+ .of_match_table = of_match_ptr(q6prm_clock_device_id),
+ },
+ .probe = q6dsp_clock_dev_probe,
+};
+module_platform_driver(q6prm_clock_platform_driver);
+
+MODULE_DESCRIPTION("Q6 Proxy Resource Manager LPASS clock driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
new file mode 100644
index 000000000000..82c40f2d4e1d
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/jiffies.h>
+#include <linux/soc/qcom/apr.h>
+#include <dt-bindings/soc/qcom,gpr.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6prm.h"
+#include "audioreach.h"
+
+struct q6prm {
+ struct device *dev;
+ gpr_device_t *gdev;
+ wait_queue_head_t wait;
+ struct gpr_ibasic_rsp_result_t result;
+ struct mutex lock;
+};
+
+#define PRM_CMD_REQUEST_HW_RSC 0x0100100F
+#define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002
+#define PRM_CMD_RELEASE_HW_RSC 0x01001010
+#define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003
+#define PARAM_ID_RSC_HW_CORE 0x08001032
+#define PARAM_ID_RSC_LPASS_CORE 0x0800102B
+#define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C
+
+struct prm_cmd_request_hw_core {
+ struct apm_module_param_data param_data;
+ uint32_t hw_clk_id;
+} __packed;
+
+struct prm_cmd_request_rsc {
+ struct apm_module_param_data param_data;
+ uint32_t num_clk_id;
+ struct audio_hw_clk_cfg clock_id;
+} __packed;
+
+static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+ return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
+ NULL, &prm->wait, pkt, rsp_opcode);
+}
+
+static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)
+{
+ struct q6prm *prm = dev_get_drvdata(dev->parent);
+ struct apm_module_param_data *param_data;
+ struct prm_cmd_request_hw_core *req;
+ gpr_device_t *gdev = prm->gdev;
+ uint32_t opcode, rsp_opcode;
+ struct gpr_pkt *pkt;
+ int rc;
+
+ if (enable) {
+ opcode = PRM_CMD_REQUEST_HW_RSC;
+ rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;
+ } else {
+ opcode = PRM_CMD_RELEASE_HW_RSC;
+ rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
+ }
+
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &req->param_data;
+
+ param_data->module_instance_id = GPR_PRM_MODULE_IID;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RSC_HW_CORE;
+ param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
+
+ req->hw_clk_id = hw_block_id;
+
+ rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle)
+{
+ return q6prm_set_hw_core_req(dev, hw_block_id, true);
+
+}
+EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);
+
+int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)
+{
+ return q6prm_set_hw_core_req(dev, hw_block_id, false);
+}
+EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);
+
+int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
+ unsigned int freq)
+{
+ struct q6prm *prm = dev_get_drvdata(dev->parent);
+ struct apm_module_param_data *param_data;
+ struct prm_cmd_request_rsc *req;
+ gpr_device_t *gdev = prm->gdev;
+ struct gpr_pkt *pkt;
+ int rc;
+
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
+ GPR_PRM_MODULE_IID);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &req->param_data;
+
+ param_data->module_instance_id = GPR_PRM_MODULE_IID;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
+ param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
+
+ req->num_clk_id = 1;
+ req->clock_id.clock_id = clk_id;
+ req->clock_id.clock_freq = freq;
+ req->clock_id.clock_attri = clk_attr;
+ req->clock_id.clock_root = clk_root;
+
+ rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);
+
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
+
+static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ gpr_device_t *gdev = priv;
+ struct q6prm *prm = dev_get_drvdata(&gdev->dev);
+ struct gpr_ibasic_rsp_result_t *result;
+ struct gpr_hdr *hdr = &data->hdr;
+
+ switch (hdr->opcode) {
+ case PRM_CMD_RSP_REQUEST_HW_RSC:
+ case PRM_CMD_RSP_RELEASE_HW_RSC:
+ result = data->payload;
+ prm->result.opcode = hdr->opcode;
+ prm->result.status = result->status;
+ wake_up(&prm->wait);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int prm_probe(gpr_device_t *gdev)
+{
+ struct device *dev = &gdev->dev;
+ struct q6prm *cc;
+
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->dev = dev;
+ cc->gdev = gdev;
+ mutex_init(&cc->lock);
+ init_waitqueue_head(&cc->wait);
+ dev_set_drvdata(dev, cc);
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id prm_device_id[] = {
+ { .compatible = "qcom,q6prm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, prm_device_id);
+#endif
+
+static gpr_driver_t prm_driver = {
+ .probe = prm_probe,
+ .gpr_callback = prm_callback,
+ .driver = {
+ .name = "qcom-prm",
+ .of_match_table = of_match_ptr(prm_device_id),
+ },
+};
+
+module_gpr_driver(prm_driver);
+MODULE_DESCRIPTION("Audio Process Manager");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h
new file mode 100644
index 000000000000..fea4d1954bc1
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6PRM_H__
+#define __Q6PRM_H__
+
+/* Clock ID for Primary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100
+/* Clock ID for Primary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101
+/* Clock ID for Secondary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102
+/* Clock ID for Secondary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103
+/* Clock ID for Tertiary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_TER_MI2S_IBIT 0x104
+/* Clock ID for Tertiary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_TER_MI2S_EBIT 0x105
+/* Clock ID for Quartnery I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106
+/* Clock ID for Quartnery I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107
+/* Clock ID for Speaker I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108
+/* Clock ID for Speaker I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109
+/* Clock ID for Speaker I2S OSR */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A
+
+/* Clock ID for QUINARY I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B
+/* Clock ID for QUINARY I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C
+/* Clock ID for SENARY I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D
+/* Clock ID for SENARY I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E
+/* Clock ID for INT0 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F
+/* Clock ID for INT1 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110
+/* Clock ID for INT2 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111
+/* Clock ID for INT3 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112
+/* Clock ID for INT4 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113
+/* Clock ID for INT5 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114
+/* Clock ID for INT6 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115
+
+/* Clock ID for QUINARY MI2S OSR CLK */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116
+
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306
+
+#define Q6PRM_LPASS_CLK_ID_VA_CORE_MCLK 0x307
+#define Q6PRM_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x308
+
+#define Q6PRM_LPASS_CLK_ID_TX_CORE_MCLK 0x30c
+#define Q6PRM_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d
+
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK 0x30e
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f
+
+#define Q6PRM_LPASS_CLK_SRC_INTERNAL 1
+#define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0
+#define Q6PRM_HW_CORE_ID_LPASS 1
+#define Q6PRM_HW_CORE_ID_DCODEC 2
+
+int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr,
+ int clk_root, unsigned int freq);
+int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle);
+int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ uint32_t client_handle);
+#endif /* __Q6PRM_H__ */
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
new file mode 100644
index 000000000000..bd649c232a06
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -0,0 +1,1113 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asound.h>
+#include <linux/firmware.h>
+#include <sound/soc-topology.h>
+#include <sound/soc-dpcm.h>
+#include <uapi/sound/snd_ar_tokens.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include "q6apm.h"
+#include "audioreach.h"
+
+struct snd_ar_control {
+ u32 sgid; /* Sub Graph ID */
+ struct snd_soc_component *scomp;
+};
+
+static struct audioreach_graph_info *audioreach_tplg_alloc_graph_info(struct q6apm *apm,
+ uint32_t graph_id,
+ bool *found)
+{
+ struct audioreach_graph_info *info;
+ int ret;
+
+ mutex_lock(&apm->lock);
+ info = idr_find(&apm->graph_info_idr, graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (info) {
+ *found = true;
+ return info;
+ }
+
+ *found = false;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&info->sg_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc(&apm->graph_info_idr, info, graph_id, graph_id + 1, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Graph ID (%x)\n", graph_id);
+ kfree(info);
+ return ERR_PTR(ret);
+ }
+
+ info->id = ret;
+
+ return info;
+}
+
+static void audioreach_tplg_add_sub_graph(struct audioreach_sub_graph *sg,
+ struct audioreach_graph_info *info)
+{
+ list_add_tail(&sg->node, &info->sg_list);
+ sg->info = info;
+ info->num_sub_graphs++;
+}
+
+static struct audioreach_sub_graph *audioreach_tplg_alloc_sub_graph(struct q6apm *apm,
+ uint32_t sub_graph_id,
+ bool *found)
+{
+ struct audioreach_sub_graph *sg;
+ int ret;
+
+ if (!sub_graph_id)
+ return ERR_PTR(-EINVAL);
+
+ /* Find if there is already a matching sub-graph */
+ mutex_lock(&apm->lock);
+ sg = idr_find(&apm->sub_graphs_idr, sub_graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (sg) {
+ *found = true;
+ return sg;
+ }
+
+ *found = false;
+ sg = kzalloc(sizeof(*sg), GFP_KERNEL);
+ if (!sg)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&sg->container_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc(&apm->sub_graphs_idr, sg, sub_graph_id, sub_graph_id + 1, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Sub-Graph Instance ID (%x)\n", sub_graph_id);
+ kfree(sg);
+ return ERR_PTR(ret);
+ }
+
+ sg->sub_graph_id = ret;
+
+ return sg;
+}
+
+static struct audioreach_container *audioreach_tplg_alloc_container(struct q6apm *apm,
+ struct audioreach_sub_graph *sg,
+ uint32_t container_id,
+ bool *found)
+{
+ struct audioreach_container *cont;
+ int ret;
+
+ if (!container_id)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&apm->lock);
+ cont = idr_find(&apm->containers_idr, container_id);
+ mutex_unlock(&apm->lock);
+
+ if (cont) {
+ *found = true;
+ return cont;
+ }
+ *found = false;
+
+ cont = kzalloc(sizeof(*cont), GFP_KERNEL);
+ if (!cont)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cont->modules_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc(&apm->containers_idr, cont, container_id, container_id + 1, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Container Instance ID (%x)\n", container_id);
+ kfree(cont);
+ return ERR_PTR(ret);
+ }
+
+ cont->container_id = ret;
+ cont->sub_graph = sg;
+ /* add to container list */
+ list_add_tail(&cont->node, &sg->container_list);
+ sg->num_containers++;
+
+ return cont;
+}
+
+static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm,
+ struct audioreach_container *cont,
+ struct snd_soc_dapm_widget *w,
+ uint32_t module_id, bool *found)
+{
+ struct audioreach_module *mod;
+ int ret;
+
+ mutex_lock(&apm->lock);
+ mod = idr_find(&apm->modules_idr, module_id);
+ mutex_unlock(&apm->lock);
+
+ if (mod) {
+ *found = true;
+ return mod;
+ }
+ *found = false;
+ mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+ if (!mod)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&apm->lock);
+ if (!module_id) { /* alloc module id dynamically */
+ ret = idr_alloc_cyclic(&apm->modules_idr, mod,
+ AR_MODULE_DYNAMIC_INSTANCE_ID_START,
+ AR_MODULE_DYNAMIC_INSTANCE_ID_END, GFP_KERNEL);
+ } else {
+ ret = idr_alloc(&apm->modules_idr, mod, module_id, module_id + 1, GFP_KERNEL);
+ }
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Module Instance ID (%x)\n", module_id);
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
+ mod->instance_id = ret;
+ /* add to module list */
+ list_add_tail(&mod->node, &cont->modules_list);
+ mod->container = cont;
+ mod->widget = w;
+ cont->num_modules++;
+
+ return mod;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *sg_array = NULL;
+ bool found = false;
+ int sz;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ int tkn_count = 0;
+
+ sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ sg_elem = sg_array->value;
+ sz = sz + le32_to_cpu(sg_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {
+ switch (le32_to_cpu(sg_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+ }
+
+ if (found)
+ return sg_array;
+
+ return NULL;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *cont_array = NULL;
+ bool found = false;
+ int sz;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ int tkn_count = 0;
+
+ cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ cont_elem = cont_array->value;
+ sz = sz + le32_to_cpu(cont_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {
+ switch (le32_to_cpu(cont_elem->token)) {
+ case AR_TKN_U32_CONTAINER_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+ }
+
+ if (found)
+ return cont_array;
+
+ return NULL;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *mod_array = NULL;
+ bool found = false;
+ int sz = 0;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ mod_elem = mod_array->value;
+ sz = sz + le32_to_cpu(mod_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+ }
+
+ if (found)
+ return mod_array;
+
+ return NULL;
+}
+
+static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ struct snd_soc_tplg_vendor_array *sg_array;
+ struct audioreach_graph_info *info = NULL;
+ int graph_id, sub_graph_id, tkn_count = 0;
+ struct audioreach_sub_graph *sg;
+ bool found;
+
+ sg_array = audioreach_get_sg_array(private);
+ sg_elem = sg_array->value;
+
+ while (tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {
+ switch (le32_to_cpu(sg_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ sub_graph_id = le32_to_cpu(sg_elem->value);
+ sg = audioreach_tplg_alloc_sub_graph(apm, sub_graph_id, &found);
+ if (IS_ERR(sg)) {
+ return sg;
+ } else if (found) {
+ /* Already parsed data for this sub-graph */
+ return sg;
+ }
+ break;
+ case AR_TKN_DAI_INDEX:
+ /* Sub graph is associated with predefined graph */
+ graph_id = le32_to_cpu(sg_elem->value);
+ info = audioreach_tplg_alloc_graph_info(apm, graph_id, &found);
+ if (IS_ERR(info))
+ return ERR_CAST(info);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_PERF_MODE:
+ sg->perf_mode = le32_to_cpu(sg_elem->value);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_DIRECTION:
+ sg->direction = le32_to_cpu(sg_elem->value);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_SCENARIO_ID:
+ sg->scenario_id = le32_to_cpu(sg_elem->value);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n", sg_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+
+ /* Sub graph is associated with predefined graph */
+ if (info)
+ audioreach_tplg_add_sub_graph(sg, info);
+
+ return sg;
+}
+
+static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm,
+ struct audioreach_sub_graph *sg,
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ struct snd_soc_tplg_vendor_array *cont_array;
+ struct audioreach_container *cont;
+ int container_id, tkn_count = 0;
+ bool found = false;
+
+ cont_array = audioreach_get_cont_array(private);
+ cont_elem = cont_array->value;
+
+ while (tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {
+ switch (le32_to_cpu(cont_elem->token)) {
+ case AR_TKN_U32_CONTAINER_INSTANCE_ID:
+ container_id = le32_to_cpu(cont_elem->value);
+ cont = audioreach_tplg_alloc_container(apm, sg, container_id, &found);
+ if (IS_ERR(cont) || found)/* Error or Already parsed container data */
+ return cont;
+ break;
+ case AR_TKN_U32_CONTAINER_CAPABILITY_ID:
+ cont->capability_id = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_STACK_SIZE:
+ cont->stack_size = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_GRAPH_POS:
+ cont->graph_pos = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_PROC_DOMAIN:
+ cont->proc_domain = le32_to_cpu(cont_elem->value);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n", cont_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+
+ return cont;
+}
+
+static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm,
+ struct audioreach_container *cont,
+ struct snd_soc_tplg_private *private,
+ struct snd_soc_dapm_widget *w)
+{
+ uint32_t max_ip_port = 0, max_op_port = 0, in_port = 0, out_port = 0;
+ uint32_t src_mod_inst_id = 0, src_mod_op_port_id = 0;
+ uint32_t dst_mod_inst_id = 0, dst_mod_ip_port_id = 0;
+ int module_id = 0, instance_id = 0, tkn_count = 0;
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod = NULL;
+ bool found;
+
+ mod_array = audioreach_get_module_array(private);
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ /* common module info */
+ case AR_TKN_U32_MODULE_ID:
+ module_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_INSTANCE_ID:
+ instance_id = le32_to_cpu(mod_elem->value);
+ mod = audioreach_tplg_alloc_module(apm, cont, w,
+ instance_id, &found);
+ if (IS_ERR(mod)) {
+ return mod;
+ } else if (found) {
+ dev_err(apm->dev, "Duplicate Module Instance ID 0x%08x found\n",
+ instance_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ break;
+ case AR_TKN_U32_MODULE_MAX_IP_PORTS:
+ max_ip_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_MAX_OP_PORTS:
+ max_op_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_IN_PORTS:
+ in_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_OUT_PORTS:
+ out_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID:
+ src_mod_op_port_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_INSTANCE_ID:
+ src_mod_inst_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID:
+ dst_mod_inst_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID:
+ dst_mod_ip_port_id = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ if (mod) {
+ mod->module_id = module_id;
+ mod->max_ip_port = max_ip_port;
+ mod->max_op_port = max_op_port;
+ mod->in_port = in_port;
+ mod->out_port = out_port;
+ mod->src_mod_inst_id = src_mod_inst_id;
+ mod->src_mod_op_port_id = src_mod_op_port_id;
+ mod->dst_mod_inst_id = dst_mod_inst_id;
+ mod->dst_mod_ip_port_id = dst_mod_ip_port_id;
+ }
+
+ return mod;
+}
+
+static int audioreach_widget_load_module_common(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct q6apm *apm = dev_get_drvdata(component->dev);
+ struct audioreach_container *cont;
+ struct audioreach_sub_graph *sg;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+
+ sg = audioreach_parse_sg_tokens(apm, &tplg_w->priv);
+ if (IS_ERR(sg))
+ return PTR_ERR(sg);
+
+ cont = audioreach_parse_cont_tokens(apm, sg, &tplg_w->priv);
+ if (IS_ERR(cont))
+ return PTR_ERR(cont);
+
+ mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w);
+ if (IS_ERR(mod))
+ return PTR_ERR(mod);
+
+ dobj = &w->dobj;
+ dobj->private = mod;
+
+ return 0;
+}
+
+static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int tkn_count = 0;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+ mod_array = audioreach_get_module_array(&tplg_w->priv);
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_FMT_INTERLEAVE:
+ mod->interleave_type = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_SAMPLE_RATE:
+ mod->rate = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_BIT_DEPTH:
+ mod->bit_depth = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_log_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+
+ case AR_TKN_U32_MODULE_LOG_CODE:
+ mod->log_code = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_LOG_TAP_POINT_ID:
+ mod->log_tap_point_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_LOG_MODE:
+ mod->log_mode = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_HW_IF_IDX:
+ mod->hw_interface_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_HW_IF_TYPE:
+ mod->hw_interface_type = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_HW_IF_IDX:
+ mod->hw_interface_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_HW_IF_TYPE:
+ mod->hw_interface_type = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SD_LINE_IDX:
+ mod->sd_line_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_WS_SRC:
+ mod->ws_src = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_load_buffer(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+
+ mod_array = audioreach_get_module_array(&tplg_w->priv);
+
+ switch (mod->module_id) {
+ case MODULE_ID_CODEC_DMA_SINK:
+ case MODULE_ID_CODEC_DMA_SOURCE:
+ audioreach_widget_dma_module_load(mod, mod_array);
+ break;
+ case MODULE_ID_DATA_LOGGING:
+ audioreach_widget_log_module_load(mod, mod_array);
+ break;
+ case MODULE_ID_I2S_SINK:
+ case MODULE_ID_I2S_SOURCE:
+ audioreach_widget_i2s_module_load(mod, mod_array);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_load_mixer(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_value_elem *w_elem;
+ struct snd_soc_tplg_vendor_array *w_array;
+ struct snd_ar_control *scontrol;
+ struct snd_soc_dobj *dobj;
+ int tkn_count = 0;
+
+ w_array = &tplg_w->priv.array[0];
+
+ scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);
+ if (!scontrol)
+ return -ENOMEM;
+
+ scontrol->scomp = component;
+ dobj = &w->dobj;
+ dobj->private = scontrol;
+
+ w_elem = w_array->value;
+ while (tkn_count <= (le32_to_cpu(w_array->num_elems) - 1)) {
+ switch (le32_to_cpu(w_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ scontrol->sgid = le32_to_cpu(w_elem->value);
+ break;
+ default: /* ignore other tokens */
+ break;
+ }
+ tkn_count++;
+ w_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct audioreach_module *mod = w->dobj.private;
+ struct q6apm *apm = dev_get_drvdata(c->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply gain after power up of widget */
+ audioreach_gain_set_vol_ctrl(apm, mod, mod->gain);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = {
+ { AR_PGA_DAPM_EVENT, audioreach_pga_event },
+};
+
+static int audioreach_widget_load_pga(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+ mod->gain = VOL_CTRL_DEFAULT_GAIN;
+
+ ret = snd_soc_tplg_widget_bind_event(w, audioreach_widget_ops,
+ ARRAY_SIZE(audioreach_widget_ops),
+ le16_to_cpu(tplg_w->event_type));
+ if (ret) {
+ dev_err(component->dev, "matching event handlers NOT found for %d\n",
+ le16_to_cpu(tplg_w->event_type));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_ready(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ audioreach_widget_load_buffer(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_decoder:
+ case snd_soc_dapm_encoder:
+ case snd_soc_dapm_src:
+ audioreach_widget_load_enc_dec_cnv(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_buffer:
+ audioreach_widget_load_buffer(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_mixer:
+ return audioreach_widget_load_mixer(component, index, w, tplg_w);
+ case snd_soc_dapm_pga:
+ return audioreach_widget_load_pga(component, index, w, tplg_w);
+ case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_scheduler:
+ case snd_soc_dapm_out_drv:
+ default:
+ dev_err(component->dev, "Widget type (0x%x) not yet supported\n", w->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+ struct q6apm *apm = dev_get_drvdata(scomp->dev);
+ struct audioreach_container *cont;
+ struct audioreach_module *mod;
+
+ mod = dobj->private;
+ cont = mod->container;
+
+ if (w->id == snd_soc_dapm_mixer) {
+ /* virtual widget */
+ kfree(dobj->private);
+ return 0;
+ }
+
+ mutex_lock(&apm->lock);
+ idr_remove(&apm->modules_idr, mod->instance_id);
+ cont->num_modules--;
+
+ list_del(&mod->node);
+ kfree(mod);
+ /* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */
+ if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */
+ struct audioreach_sub_graph *sg = cont->sub_graph;
+
+ idr_remove(&apm->containers_idr, cont->container_id);
+ list_del(&cont->node);
+ sg->num_containers--;
+ kfree(cont);
+ /* check if there are no more containers in the sub graph and remove it */
+ if (list_empty(&sg->container_list)) {
+ struct audioreach_graph_info *info = sg->info;
+
+ idr_remove(&apm->sub_graphs_idr, sg->sub_graph_id);
+ list_del(&sg->node);
+ info->num_sub_graphs--;
+ kfree(sg);
+ /* Check if there are no more sub-graphs left then remove graph info */
+ if (list_empty(&info->sg_list)) {
+ idr_remove(&apm->graph_info_idr, info->id);
+ kfree(info);
+ }
+ }
+ }
+
+ mutex_unlock(&apm->lock);
+
+ return 0;
+}
+
+static struct audioreach_module *audioreach_find_widget(struct snd_soc_component *comp,
+ const char *name)
+{
+ struct q6apm *apm = dev_get_drvdata(comp->dev);
+ struct audioreach_module *module;
+ int id;
+
+ idr_for_each_entry(&apm->modules_idr, module, id) {
+ if (!strcmp(name, module->widget->name))
+ return module;
+ }
+
+ return NULL;
+}
+
+static int audioreach_route_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dapm_route *route)
+{
+ struct audioreach_module *src, *sink;
+
+ src = audioreach_find_widget(scomp, route->source);
+ sink = audioreach_find_widget(scomp, route->sink);
+
+ if (src && sink) {
+ src->dst_mod_inst_id = sink->instance_id;
+ sink->src_mod_inst_id = src->instance_id;
+ }
+
+ return 0;
+}
+
+static int audioreach_route_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ return 0;
+}
+
+static int audioreach_tplg_complete(struct snd_soc_component *component)
+{
+ /* TBD */
+ return 0;
+}
+
+/* DAI link - used for any driver specific init */
+static int audioreach_link_load(struct snd_soc_component *component, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg)
+{
+ link->nonatomic = true;
+ link->dynamic = true;
+ link->platforms->name = NULL;
+ link->platforms->of_node = of_get_compatible_child(component->dev->of_node,
+ "qcom,q6apm-dais");
+ return 0;
+}
+
+static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ struct snd_ar_control *scontrol = mc->dobj.private;
+ struct q6apm *data = dev_get_drvdata(c->dev);
+ bool connected;
+
+ connected = q6apm_is_sub_graphs_connected(data, scontrol->sgid, dapm_scontrol->sgid);
+ if (connected)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ struct snd_ar_control *scontrol = mc->dobj.private;
+ struct q6apm *data = dev_get_drvdata(c->dev);
+
+ if (ucontrol->value.integer.value[0]) {
+ q6apm_connect_sub_graphs(data, scontrol->sgid, dapm_scontrol->sgid, true);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, NULL);
+ } else {
+ q6apm_connect_sub_graphs(data, scontrol->sgid, dapm_scontrol->sgid, false);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, NULL);
+ }
+ return 0;
+}
+
+static int audioreach_get_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct audioreach_module *mod = dw->dobj.private;
+
+ ucontrol->value.integer.value[0] = mod->gain;
+
+ return 0;
+}
+
+static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct audioreach_module *mod = dw->dobj.private;
+
+ mod->gain = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int audioreach_control_load_mix(struct snd_soc_component *scomp,
+ struct snd_ar_control *scontrol,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_soc_tplg_vendor_value_elem *c_elem;
+ struct snd_soc_tplg_vendor_array *c_array;
+ struct snd_soc_tplg_mixer_control *mc;
+ int tkn_count = 0;
+
+ mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
+ c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data;
+
+ c_elem = c_array->value;
+
+ while (tkn_count <= (le32_to_cpu(c_array->num_elems) - 1)) {
+ switch (le32_to_cpu(c_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ scontrol->sgid = le32_to_cpu(c_elem->value);
+ break;
+ default:
+ /* Ignore other tokens */
+ break;
+ }
+ c_elem++;
+ tkn_count++;
+ }
+
+ return 0;
+}
+
+static int audioreach_control_load(struct snd_soc_component *scomp, int index,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_ar_control *scontrol;
+ struct soc_mixer_control *sm;
+ struct snd_soc_dobj *dobj;
+ int ret = 0;
+
+ scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);
+ if (!scontrol)
+ return -ENOMEM;
+
+ scontrol->scomp = scomp;
+
+ switch (le32_to_cpu(hdr->ops.get)) {
+ case SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ dobj = &sm->dobj;
+ ret = audioreach_control_load_mix(scomp, scontrol, kc, hdr);
+ break;
+ case SND_SOC_AR_TPLG_VOL_CTL:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ dobj = &sm->dobj;
+ break;
+ default:
+ dev_warn(scomp->dev, "control type not supported %d:%d:%d\n",
+ hdr->ops.get, hdr->ops.put, hdr->ops.info);
+ kfree(scontrol);
+ return -EINVAL;
+ }
+
+ dobj->private = scontrol;
+ return ret;
+}
+
+static int audioreach_control_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ struct snd_ar_control *scontrol = dobj->private;
+
+ kfree(scontrol);
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = {
+ {SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX, audioreach_get_audio_mixer,
+ audioreach_put_audio_mixer, snd_soc_info_volsw},
+ {SND_SOC_AR_TPLG_VOL_CTL, audioreach_get_vol_ctrl_audio_mixer,
+ audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw},
+};
+
+static struct snd_soc_tplg_ops audioreach_tplg_ops = {
+ .io_ops = audioreach_io_ops,
+ .io_ops_count = ARRAY_SIZE(audioreach_io_ops),
+
+ .control_load = audioreach_control_load,
+ .control_unload = audioreach_control_unload,
+
+ .widget_ready = audioreach_widget_ready,
+ .widget_unload = audioreach_widget_unload,
+
+ .complete = audioreach_tplg_complete,
+ .link_load = audioreach_link_load,
+
+ .dapm_route_load = audioreach_route_load,
+ .dapm_route_unload = audioreach_route_unload,
+};
+
+int audioreach_tplg_init(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+ struct device *dev = component->dev;
+ const struct firmware *fw;
+ char *tplg_fw_name;
+ int ret;
+
+ /* Inline with Qualcomm UCM configs and linux-firmware path */
+ tplg_fw_name = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", card->driver_name, card->name);
+ if (!tplg_fw_name)
+ return -ENOMEM;
+
+ ret = request_firmware(&fw, tplg_fw_name, dev);
+ if (ret < 0) {
+ dev_err(dev, "tplg firmware loading %s failed %d \n", tplg_fw_name, ret);
+ goto err;
+ }
+
+ ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw);
+ if (ret < 0) {
+ dev_err(dev, "tplg component load failed%d\n", ret);
+ ret = -EINVAL;
+ }
+
+ release_firmware(fw);
+err:
+ kfree(tplg_fw_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audioreach_tplg_init);
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index fe8fd7367e21..b2ca2579810b 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -8,6 +8,8 @@
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <linux/input-event-codes.h>
#include "qdsp6/q6afe.h"
#include "common.h"
@@ -18,8 +20,66 @@ struct sm8250_snd_data {
bool stream_prepared[AFE_PORT_MAX];
struct snd_soc_card *card;
struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
+ struct snd_soc_jack jack;
+ bool jack_setup;
};
+static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ int rval, i;
+
+ if (!data->jack_setup) {
+ struct snd_jack *jack;
+
+ rval = snd_soc_card_jack_new(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,
+ &data->jack, NULL, 0);
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headphone Jack\n");
+ return rval;
+ }
+
+ jack = data->jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ data->jack_setup = true;
+ }
+
+ switch (cpu_dai->id) {
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ &data->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+
+
+ return 0;
+}
+
static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -69,6 +129,12 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream,
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
for_each_rtd_codec_dais(rtd, i, codec_dai) {
sruntime = snd_soc_dai_get_sdw_stream(codec_dai,
substream->stream);
@@ -129,6 +195,12 @@ static int sm8250_snd_prepare(struct snd_pcm_substream *substream)
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
return sm8250_snd_wsa_dma_prepare(substream);
default:
break;
@@ -147,6 +219,12 @@ static int sm8250_snd_hw_free(struct snd_pcm_substream *substream)
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
if (sruntime && data->stream_prepared[cpu_dai->id]) {
sdw_disable_stream(sruntime);
sdw_deprepare_stream(sruntime);
@@ -174,6 +252,7 @@ static void sm8250_add_be_ops(struct snd_soc_card *card)
for_each_card_prelinks(card, i, link) {
if (link->no_pcm == 1) {
+ link->init = sm8250_snd_init;
link->be_hw_params_fixup = sm8250_be_hw_params_fixup;
link->ops = &sm8250_be_ops;
}
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 053097b73e28..42f76bc0fb02 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -16,6 +16,17 @@ config SND_SOC_ROCKCHIP_I2S
Rockchip I2S device. The device supports upto maximum of
8 channels each for play and record.
+config SND_SOC_ROCKCHIP_I2S_TDM
+ tristate "Rockchip I2S/TDM Device Driver"
+ depends on HAVE_CLK && SND_SOC_ROCKCHIP
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the I2S/TDM driver for
+ Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices
+ interface between the AHB bus and the I2S bus, and support up to a
+ maximum of 8 channels each for playback and recording.
+
+
config SND_SOC_ROCKCHIP_PDM
tristate "Rockchip PDM Controller Driver"
depends on HAVE_CLK && SND_SOC_ROCKCHIP
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 65e814d46006..30c57c0d7660 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -1,13 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
# ROCKCHIP Platform Support
snd-soc-rockchip-i2s-objs := rockchip_i2s.o
-snd-soc-rockchip-pcm-objs := rockchip_pcm.o
+snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o
snd-soc-rockchip-pdm-objs := rockchip_pdm.o
snd-soc-rockchip-spdif-objs := rockchip_spdif.o
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 7e89f5b0c237..a6d7656c206e 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -20,7 +20,6 @@
#include <sound/dmaengine_pcm.h>
#include "rockchip_i2s.h"
-#include "rockchip_pcm.h"
#define DRV_NAME "rockchip-i2s"
@@ -756,7 +755,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_suspend;
}
- ret = rockchip_pcm_platform_register(&pdev->dev);
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
goto err_suspend;
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c
new file mode 100644
index 000000000000..17b9b287853a
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -0,0 +1,1762 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
+
+// Copyright (c) 2018 Rockchip Electronics Co. Ltd.
+// Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+// Author: Nicolas Frattaroli <frattaroli.nicolas@gmail.com>
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "rockchip_i2s_tdm.h"
+
+#define DRV_NAME "rockchip-i2s-tdm"
+
+#define DEFAULT_MCLK_FS 256
+#define CH_GRP_MAX 4 /* The max channel 8 / 2 */
+#define MULTIPLEX_CH_MAX 10
+#define CLK_PPM_MIN -1000
+#define CLK_PPM_MAX 1000
+
+#define TRCM_TXRX 0
+#define TRCM_TX 1
+#define TRCM_RX 2
+
+struct txrx_config {
+ u32 addr;
+ u32 reg;
+ u32 txonly;
+ u32 rxonly;
+};
+
+struct rk_i2s_soc_data {
+ u32 softrst_offset;
+ u32 grf_reg_offset;
+ u32 grf_shift;
+ int config_count;
+ const struct txrx_config *configs;
+ int (*init)(struct device *dev, u32 addr);
+};
+
+struct rk_i2s_tdm_dev {
+ struct device *dev;
+ struct clk *hclk;
+ struct clk *mclk_tx;
+ struct clk *mclk_rx;
+ /* The mclk_tx_src is parent of mclk_tx */
+ struct clk *mclk_tx_src;
+ /* The mclk_rx_src is parent of mclk_rx */
+ struct clk *mclk_rx_src;
+ /*
+ * The mclk_root0 and mclk_root1 are root parent and supplies for
+ * the different FS.
+ *
+ * e.g:
+ * mclk_root0 is VPLL0, used for FS=48000Hz
+ * mclk_root1 is VPLL1, used for FS=44100Hz
+ */
+ struct clk *mclk_root0;
+ struct clk *mclk_root1;
+ struct regmap *regmap;
+ struct regmap *grf;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct reset_control *tx_reset;
+ struct reset_control *rx_reset;
+ struct rk_i2s_soc_data *soc_data;
+ bool is_master_mode;
+ bool io_multiplex;
+ bool mclk_calibrate;
+ bool tdm_mode;
+ unsigned int mclk_rx_freq;
+ unsigned int mclk_tx_freq;
+ unsigned int mclk_root0_freq;
+ unsigned int mclk_root1_freq;
+ unsigned int mclk_root0_initial_freq;
+ unsigned int mclk_root1_initial_freq;
+ unsigned int frame_width;
+ unsigned int clk_trcm;
+ unsigned int i2s_sdis[CH_GRP_MAX];
+ unsigned int i2s_sdos[CH_GRP_MAX];
+ int clk_ppm;
+ int refcount;
+ spinlock_t lock; /* xfer lock */
+ bool has_playback;
+ bool has_capture;
+};
+
+static int to_ch_num(unsigned int val)
+{
+ switch (val) {
+ case I2S_CHN_4:
+ return 4;
+ case I2S_CHN_6:
+ return 6;
+ case I2S_CHN_8:
+ return 8;
+ default:
+ return 2;
+ }
+}
+
+static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+ clk_disable_unprepare(i2s_tdm->mclk_rx);
+ if (i2s_tdm->mclk_calibrate) {
+ clk_disable_unprepare(i2s_tdm->mclk_tx_src);
+ clk_disable_unprepare(i2s_tdm->mclk_rx_src);
+ clk_disable_unprepare(i2s_tdm->mclk_root0);
+ clk_disable_unprepare(i2s_tdm->mclk_root1);
+ }
+}
+
+/**
+ * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on
+ * failure.
+ * @i2s_tdm: rk_i2s_tdm_dev struct
+ *
+ * This function attempts to enable all mclk clocks, but cleans up after
+ * itself on failure. Guarantees to balance its calls.
+ *
+ * Returns success (0) or negative errno.
+ */
+static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(i2s_tdm->mclk_tx);
+ if (ret)
+ goto err_mclk_tx;
+ ret = clk_prepare_enable(i2s_tdm->mclk_rx);
+ if (ret)
+ goto err_mclk_rx;
+ if (i2s_tdm->mclk_calibrate) {
+ ret = clk_prepare_enable(i2s_tdm->mclk_tx_src);
+ if (ret)
+ goto err_mclk_rx;
+ ret = clk_prepare_enable(i2s_tdm->mclk_rx_src);
+ if (ret)
+ goto err_mclk_rx_src;
+ ret = clk_prepare_enable(i2s_tdm->mclk_root0);
+ if (ret)
+ goto err_mclk_root0;
+ ret = clk_prepare_enable(i2s_tdm->mclk_root1);
+ if (ret)
+ goto err_mclk_root1;
+ }
+
+ return 0;
+
+err_mclk_root1:
+ clk_disable_unprepare(i2s_tdm->mclk_root0);
+err_mclk_root0:
+ clk_disable_unprepare(i2s_tdm->mclk_rx_src);
+err_mclk_rx_src:
+ clk_disable_unprepare(i2s_tdm->mclk_tx_src);
+err_mclk_rx:
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+err_mclk_tx:
+ return ret;
+}
+
+static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s_tdm->regmap, true);
+ i2s_tdm_disable_unprepare_mclk(i2s_tdm);
+
+ clk_disable_unprepare(i2s_tdm->hclk);
+
+ return 0;
+}
+
+static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2s_tdm->hclk);
+ if (ret)
+ goto err_hclk;
+
+ ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
+ if (ret)
+ goto err_mclk;
+
+ regcache_cache_only(i2s_tdm->regmap, false);
+ regcache_mark_dirty(i2s_tdm->regmap);
+
+ ret = regcache_sync(i2s_tdm->regmap);
+ if (ret)
+ goto err_regcache;
+
+ return 0;
+
+err_regcache:
+ i2s_tdm_disable_unprepare_mclk(i2s_tdm);
+err_mclk:
+ clk_disable_unprepare(i2s_tdm->hclk);
+err_hclk:
+ return ret;
+}
+
+static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai)
+{
+ return snd_soc_dai_get_drvdata(dai);
+}
+
+/*
+ * Makes sure that both tx and rx are reset at the same time to sync lrck
+ * when clk_trcm > 0.
+ */
+static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ /* This is technically race-y.
+ *
+ * In an ideal world, we could atomically assert both resets at the
+ * same time, through an atomic bulk reset API. This API however does
+ * not exist, so what the downstream vendor code used to do was
+ * implement half a reset controller here and require the CRU to be
+ * passed to the driver as a device tree node. Violating abstractions
+ * like that is bad, especially when it influences something like the
+ * bindings which are supposed to describe the hardware, not whatever
+ * workarounds the driver needs, so it was dropped.
+ *
+ * In practice, asserting the resets one by one appears to work just
+ * fine for playback. During duplex (playback + capture) operation,
+ * this might become an issue, but that should be solved by the
+ * implementation of the aforementioned API, not by shoving a reset
+ * controller into an audio driver.
+ */
+
+ reset_control_assert(i2s_tdm->tx_reset);
+ reset_control_assert(i2s_tdm->rx_reset);
+ udelay(10);
+ reset_control_deassert(i2s_tdm->tx_reset);
+ reset_control_deassert(i2s_tdm->rx_reset);
+ udelay(10);
+}
+
+static void rockchip_snd_reset(struct reset_control *rc)
+{
+ reset_control_assert(rc);
+ udelay(10);
+ reset_control_deassert(rc);
+ udelay(10);
+}
+
+static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm,
+ unsigned int clr)
+{
+ unsigned int xfer_mask = 0;
+ unsigned int xfer_val = 0;
+ unsigned int val;
+ int retry = 10;
+ bool tx = clr & I2S_CLR_TXC;
+ bool rx = clr & I2S_CLR_RXC;
+
+ if (!(rx || tx))
+ return;
+
+ if (tx) {
+ xfer_mask = I2S_XFER_TXS_START;
+ xfer_val = I2S_XFER_TXS_STOP;
+ }
+ if (rx) {
+ xfer_mask |= I2S_XFER_RXS_START;
+ xfer_val |= I2S_XFER_RXS_STOP;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, xfer_mask, xfer_val);
+ udelay(150);
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr);
+
+ regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
+ /* Wait on the clear operation to finish */
+ while (val) {
+ udelay(15);
+ regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
+ retry--;
+ if (!retry) {
+ dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n",
+ tx ? "tx" : "", rx ? "rx" : "");
+ if (rx && tx)
+ rockchip_snd_xfer_sync_reset(i2s_tdm);
+ else if (tx)
+ rockchip_snd_reset(i2s_tdm->tx_reset);
+ else if (rx)
+ rockchip_snd_reset(i2s_tdm->rx_reset);
+ break;
+ }
+ }
+}
+
+static inline void rockchip_enable_tde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_ENABLE);
+}
+
+static inline void rockchip_disable_tde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_DISABLE);
+}
+
+static inline void rockchip_enable_rde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_ENABLE);
+}
+
+static inline void rockchip_disable_rde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_DISABLE);
+}
+
+/* only used when clk_trcm > 0 */
+static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai, int on)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (on) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_enable_tde(i2s_tdm->regmap);
+ else
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ if (++i2s_tdm->refcount == 1) {
+ rockchip_snd_xfer_sync_reset(i2s_tdm);
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START);
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_disable_tde(i2s_tdm->regmap);
+ else
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ if (--i2s_tdm->refcount == 0) {
+ rockchip_snd_xfer_clear(i2s_tdm,
+ I2S_CLR_TXC | I2S_CLR_RXC);
+ }
+ }
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+}
+
+static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+{
+ if (on) {
+ rockchip_enable_tde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START,
+ I2S_XFER_TXS_START);
+ } else {
+ rockchip_disable_tde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC);
+ }
+}
+
+static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+{
+ if (on) {
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_RXS_START,
+ I2S_XFER_RXS_START);
+ } else {
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC);
+ }
+}
+
+static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
+ unsigned int mask, val, tdm_val, txcr_val, rxcr_val;
+ int ret;
+ bool is_tdm = i2s_tdm->tdm_mode;
+
+ ret = pm_runtime_get_sync(cpu_dai->dev);
+ if (ret < 0 && ret != -EACCES) {
+ pm_runtime_put_noidle(cpu_dai->dev);
+ return ret;
+ }
+
+ mask = I2S_CKR_MSS_MASK;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ val = I2S_CKR_MSS_MASTER;
+ i2s_tdm->is_master_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val = I2S_CKR_MSS_SLAVE;
+ i2s_tdm->is_master_mode = false;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
+
+ mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ txcr_val = I2S_TXCR_IBM_RSJM;
+ rxcr_val = I2S_RXCR_IBM_RSJM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ txcr_val = I2S_TXCR_IBM_LSJM;
+ rxcr_val = I2S_RXCR_IBM_LSJM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ txcr_val = I2S_TXCR_IBM_NORMAL;
+ rxcr_val = I2S_RXCR_IBM_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */
+ txcr_val = I2S_TXCR_TFS_PCM;
+ rxcr_val = I2S_RXCR_TFS_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */
+ txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
+ rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, txcr_val);
+
+ mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, rxcr_val);
+
+ if (is_tdm) {
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(2);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(1);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(0);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val = I2S_TXCR_TFS_TDM_PCM;
+ tdm_val = TDM_SHIFT_CTRL(0);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ val = I2S_TXCR_TFS_TDM_PCM;
+ tdm_val = TDM_SHIFT_CTRL(2);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ tdm_val |= TDM_FSYNC_WIDTH_SEL1(1);
+ tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME;
+
+ mask = I2S_TXCR_TFS_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val);
+
+ mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK |
+ TDM_SHIFT_CTRL_MSK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
+ mask, tdm_val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
+ mask, tdm_val);
+ }
+
+err_pm_put:
+ pm_runtime_put(cpu_dai->dev);
+
+ return ret;
+}
+
+static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int stream;
+
+ stream = SNDRV_PCM_STREAM_LAST - substream->stream;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_disable_tde(i2s_tdm->regmap);
+ else
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC);
+}
+
+static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int stream;
+
+ stream = SNDRV_PCM_STREAM_LAST - substream->stream;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_enable_tde(i2s_tdm->regmap);
+ else
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START);
+}
+
+static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct clk *clk, unsigned long rate,
+ int ppm)
+{
+ unsigned long rate_target;
+ int delta, ret;
+
+ if (ppm == i2s_tdm->clk_ppm)
+ return 0;
+
+ if (ppm < 0)
+ delta = -1;
+ else
+ delta = 1;
+
+ delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000,
+ 1000000);
+
+ rate_target = rate + delta;
+
+ if (!rate_target)
+ return -EINVAL;
+
+ ret = clk_set_rate(clk, rate_target);
+ if (ret)
+ return ret;
+
+ i2s_tdm->clk_ppm = ppm;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct snd_pcm_substream *substream,
+ unsigned int lrck_freq)
+{
+ struct clk *mclk_root;
+ struct clk *mclk_parent;
+ unsigned int mclk_root_freq;
+ unsigned int mclk_root_initial_freq;
+ unsigned int mclk_parent_freq;
+ unsigned int div, delta;
+ u64 ppm;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mclk_parent = i2s_tdm->mclk_tx_src;
+ else
+ mclk_parent = i2s_tdm->mclk_rx_src;
+
+ switch (lrck_freq) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ case 192000:
+ mclk_root = i2s_tdm->mclk_root0;
+ mclk_root_freq = i2s_tdm->mclk_root0_freq;
+ mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq;
+ mclk_parent_freq = DEFAULT_MCLK_FS * 192000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ mclk_root = i2s_tdm->mclk_root1;
+ mclk_root_freq = i2s_tdm->mclk_root1_freq;
+ mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq;
+ mclk_parent_freq = DEFAULT_MCLK_FS * 176400;
+ break;
+ default:
+ dev_err(i2s_tdm->dev, "Invalid LRCK frequency: %u Hz\n",
+ lrck_freq);
+ return -EINVAL;
+ }
+
+ ret = clk_set_parent(mclk_parent, mclk_root);
+ if (ret)
+ return ret;
+
+ ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, mclk_root,
+ mclk_root_freq, 0);
+ if (ret)
+ return ret;
+
+ delta = abs(mclk_root_freq % mclk_parent_freq - mclk_parent_freq);
+ ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)mclk_root_freq);
+
+ if (ppm) {
+ div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq);
+ if (!div)
+ return -EINVAL;
+
+ mclk_root_freq = mclk_parent_freq * round_up(div, 2);
+
+ ret = clk_set_rate(mclk_root, mclk_root_freq);
+ if (ret)
+ return ret;
+
+ i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0);
+ i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1);
+ }
+
+ return clk_set_rate(mclk_parent, mclk_parent_freq);
+}
+
+static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct snd_pcm_substream *substream,
+ struct clk **mclk)
+{
+ unsigned int mclk_freq;
+ int ret;
+
+ if (i2s_tdm->clk_trcm) {
+ if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) {
+ dev_err(i2s_tdm->dev,
+ "clk_trcm, tx: %d and rx: %d should be the same\n",
+ i2s_tdm->mclk_tx_freq,
+ i2s_tdm->mclk_rx_freq);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq);
+ if (ret)
+ return ret;
+
+ ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq);
+ if (ret)
+ return ret;
+
+ /* mclk_rx is also ok. */
+ *mclk = i2s_tdm->mclk_tx;
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ *mclk = i2s_tdm->mclk_tx;
+ mclk_freq = i2s_tdm->mclk_tx_freq;
+ } else {
+ *mclk = i2s_tdm->mclk_rx;
+ mclk_freq = i2s_tdm->mclk_rx_freq;
+ }
+
+ ret = clk_set_rate(*mclk, mclk_freq);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture)
+{
+ if (substream_capture) {
+ switch (ch) {
+ case I2S_CHN_4:
+ return I2S_IO_6CH_OUT_4CH_IN;
+ case I2S_CHN_6:
+ return I2S_IO_4CH_OUT_6CH_IN;
+ case I2S_CHN_8:
+ return I2S_IO_2CH_OUT_8CH_IN;
+ default:
+ return I2S_IO_8CH_OUT_2CH_IN;
+ }
+ } else {
+ switch (ch) {
+ case I2S_CHN_4:
+ return I2S_IO_4CH_OUT_6CH_IN;
+ case I2S_CHN_6:
+ return I2S_IO_6CH_OUT_4CH_IN;
+ case I2S_CHN_8:
+ return I2S_IO_8CH_OUT_2CH_IN;
+ default:
+ return I2S_IO_2CH_OUT_8CH_IN;
+ }
+ }
+}
+
+static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ int usable_chs = MULTIPLEX_CH_MAX;
+ unsigned int val = 0;
+
+ if (!i2s_tdm->io_multiplex)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ struct snd_pcm_str *playback_str =
+ &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (playback_str->substream_opened) {
+ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
+ val &= I2S_TXCR_CSR_MASK;
+ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
+ }
+
+ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
+ val &= I2S_RXCR_CSR_MASK;
+
+ if (to_ch_num(val) > usable_chs) {
+ dev_err(i2s_tdm->dev,
+ "Capture channels (%d) > usable channels (%d)\n",
+ to_ch_num(val), usable_chs);
+ return -EINVAL;
+ }
+
+ rockchip_i2s_ch_to_io(val, true);
+ } else {
+ struct snd_pcm_str *capture_str =
+ &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (capture_str->substream_opened) {
+ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
+ val &= I2S_RXCR_CSR_MASK;
+ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
+ }
+
+ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
+ val &= I2S_TXCR_CSR_MASK;
+
+ if (to_ch_num(val) > usable_chs) {
+ dev_err(i2s_tdm->dev,
+ "Playback channels (%d) > usable channels (%d)\n",
+ to_ch_num(val), usable_chs);
+ return -EINVAL;
+ }
+ }
+
+ val <<= i2s_tdm->soc_data->grf_shift;
+ val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16;
+ regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val);
+
+ return 0;
+}
+
+static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ unsigned int div_bclk,
+ unsigned int div_lrck,
+ unsigned int fmt)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ unsigned long flags;
+
+ if (!i2s_tdm->clk_trcm)
+ return 0;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (i2s_tdm->refcount)
+ rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK,
+ I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
+ I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ fmt);
+ else
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ fmt);
+
+ if (i2s_tdm->refcount)
+ rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm);
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ struct clk *mclk;
+ int ret = 0;
+ unsigned int val = 0;
+ unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64;
+
+ if (i2s_tdm->is_master_mode) {
+ if (i2s_tdm->mclk_calibrate)
+ rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream,
+ params_rate(params));
+
+ ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk);
+ if (ret)
+ return ret;
+
+ mclk_rate = clk_get_rate(mclk);
+ bclk_rate = i2s_tdm->frame_width * params_rate(params);
+ if (!bclk_rate)
+ return -EINVAL;
+
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ div_lrck = bclk_rate / params_rate(params);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ val |= I2S_TXCR_VDW(8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= I2S_TXCR_VDW(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= I2S_TXCR_VDW(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= I2S_TXCR_VDW(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val |= I2S_TXCR_VDW(32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 8:
+ val |= I2S_CHN_8;
+ break;
+ case 6:
+ val |= I2S_CHN_6;
+ break;
+ case 4:
+ val |= I2S_CHN_4;
+ break;
+ case 2:
+ val |= I2S_CHN_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (i2s_tdm->clk_trcm) {
+ rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val);
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_TXM_MASK,
+ I2S_CLKDIV_TXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_TSD_MASK,
+ I2S_CKR_TSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ val);
+ } else {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_RXM_MASK,
+ I2S_CLKDIV_RXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_RSD_MASK,
+ I2S_CKR_RSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ val);
+ }
+
+ return rockchip_i2s_io_multiplex(substream, dai);
+}
+
+static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (i2s_tdm->clk_trcm)
+ rockchip_snd_txrxctrl(substream, dai, 1);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_snd_rxctrl(i2s_tdm, 1);
+ else
+ rockchip_snd_txctrl(i2s_tdm, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (i2s_tdm->clk_trcm)
+ rockchip_snd_txrxctrl(substream, dai, 0);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_snd_rxctrl(i2s_tdm, 0);
+ else
+ rockchip_snd_txctrl(i2s_tdm, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream,
+ unsigned int freq, int dir)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
+
+ /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */
+ if (i2s_tdm->clk_trcm) {
+ i2s_tdm->mclk_tx_freq = freq;
+ i2s_tdm->mclk_rx_freq = freq;
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_tdm->mclk_tx_freq = freq;
+ else
+ i2s_tdm->mclk_rx_freq = freq;
+ }
+
+ dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n",
+ stream ? "rx" : "tx", freq);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_clk_compensation_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = CLK_PPM_MIN;
+ uinfo->value.integer.max = CLK_PPM_MAX;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+ int ret = 0, ppm = 0;
+ int changed = 0;
+ unsigned long old_rate;
+
+ if (ucontrol->value.integer.value[0] < CLK_PPM_MIN ||
+ ucontrol->value.integer.value[0] > CLK_PPM_MAX)
+ return -EINVAL;
+
+ ppm = ucontrol->value.integer.value[0];
+
+ old_rate = clk_get_rate(i2s_tdm->mclk_root0);
+ ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0,
+ i2s_tdm->mclk_root0_freq, ppm);
+ if (ret)
+ return ret;
+ if (old_rate != clk_get_rate(i2s_tdm->mclk_root0))
+ changed = 1;
+
+ if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1))
+ return changed;
+
+ old_rate = clk_get_rate(i2s_tdm->mclk_root1);
+ ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1,
+ i2s_tdm->mclk_root1_freq, ppm);
+ if (ret)
+ return ret;
+ if (old_rate != clk_get_rate(i2s_tdm->mclk_root1))
+ changed = 1;
+
+ return changed;
+}
+
+static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Clock Compensation in PPM",
+ .info = rockchip_i2s_tdm_clk_compensation_info,
+ .get = rockchip_i2s_tdm_clk_compensation_get,
+ .put = rockchip_i2s_tdm_clk_compensation_put,
+};
+
+static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ if (i2s_tdm->has_capture)
+ dai->capture_dma_data = &i2s_tdm->capture_dma_data;
+ if (i2s_tdm->has_playback)
+ dai->playback_dma_data = &i2s_tdm->playback_dma_data;
+
+ if (i2s_tdm->mclk_calibrate)
+ snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1);
+
+ return 0;
+}
+
+static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ i2s_tdm->tdm_mode = true;
+ i2s_tdm->frame_width = slots * slot_width;
+ mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK;
+ val = TDM_SLOT_BIT_WIDTH(slot_width) |
+ TDM_FRAME_WIDTH(slots * slot_width);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
+ mask, val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
+ mask, val);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ if (ratio < 32 || ratio > 512 || ratio % 2 == 1)
+ return -EINVAL;
+
+ i2s_tdm->frame_width = ratio;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
+ .hw_params = rockchip_i2s_tdm_hw_params,
+ .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio,
+ .set_sysclk = rockchip_i2s_tdm_set_sysclk,
+ .set_fmt = rockchip_i2s_tdm_set_fmt,
+ .set_tdm_slot = rockchip_dai_tdm_slot,
+ .trigger = rockchip_i2s_tdm_trigger,
+};
+
+static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
+ .name = DRV_NAME,
+};
+
+static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXCR:
+ case I2S_RXCR:
+ case I2S_CKR:
+ case I2S_DMACR:
+ case I2S_INTCR:
+ case I2S_XFER:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_TDM_TXCR:
+ case I2S_TDM_RXCR:
+ case I2S_CLKDIV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXCR:
+ case I2S_RXCR:
+ case I2S_CKR:
+ case I2S_DMACR:
+ case I2S_INTCR:
+ case I2S_XFER:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_RXDR:
+ case I2S_TXFIFOLR:
+ case I2S_INTSR:
+ case I2S_RXFIFOLR:
+ case I2S_TDM_TXCR:
+ case I2S_TDM_RXCR:
+ case I2S_CLKDIV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXFIFOLR:
+ case I2S_INTSR:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_RXDR:
+ case I2S_RXFIFOLR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg)
+{
+ if (reg == I2S_RXDR)
+ return true;
+ return false;
+}
+
+static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = {
+ {0x00, 0x7200000f},
+ {0x04, 0x01c8000f},
+ {0x08, 0x00001f1f},
+ {0x10, 0x001f0000},
+ {0x14, 0x01f00000},
+ {0x30, 0x00003eff},
+ {0x34, 0x00003eff},
+ {0x38, 0x00000707},
+};
+
+static const struct regmap_config rockchip_i2s_tdm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = I2S_CLKDIV,
+ .reg_defaults = rockchip_i2s_tdm_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults),
+ .writeable_reg = rockchip_i2s_tdm_wr_reg,
+ .readable_reg = rockchip_i2s_tdm_rd_reg,
+ .volatile_reg = rockchip_i2s_tdm_volatile_reg,
+ .precious_reg = rockchip_i2s_tdm_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int common_soc_init(struct device *dev, u32 addr)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ const struct txrx_config *configs = i2s_tdm->soc_data->configs;
+ u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm;
+ int i;
+
+ if (trcm == TRCM_TXRX)
+ return 0;
+
+ for (i = 0; i < i2s_tdm->soc_data->config_count; i++) {
+ if (addr != configs[i].addr)
+ continue;
+ reg = configs[i].reg;
+ if (trcm == TRCM_TX)
+ val = configs[i].txonly;
+ else
+ val = configs[i].rxonly;
+
+ if (reg)
+ regmap_write(i2s_tdm->grf, reg, val);
+ }
+
+ return 0;
+}
+
+static const struct txrx_config px30_txrx_config[] = {
+ { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY },
+};
+
+static const struct txrx_config rk1808_txrx_config[] = {
+ { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY },
+};
+
+static const struct txrx_config rk3308_txrx_config[] = {
+ { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY },
+ { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY },
+};
+
+static const struct txrx_config rk3568_txrx_config[] = {
+ { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY },
+ { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE },
+ { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE },
+ { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY },
+ { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY },
+ { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE },
+};
+
+static const struct txrx_config rv1126_txrx_config[] = {
+ { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY },
+};
+
+static struct rk_i2s_soc_data px30_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = px30_txrx_config,
+ .config_count = ARRAY_SIZE(px30_txrx_config),
+ .init = common_soc_init,
+};
+
+static struct rk_i2s_soc_data rk1808_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = rk1808_txrx_config,
+ .config_count = ARRAY_SIZE(rk1808_txrx_config),
+ .init = common_soc_init,
+};
+
+static struct rk_i2s_soc_data rk3308_i2s_soc_data = {
+ .softrst_offset = 0x0400,
+ .grf_reg_offset = 0x0308,
+ .grf_shift = 5,
+ .configs = rk3308_txrx_config,
+ .config_count = ARRAY_SIZE(rk3308_txrx_config),
+ .init = common_soc_init,
+};
+
+static struct rk_i2s_soc_data rk3568_i2s_soc_data = {
+ .softrst_offset = 0x0400,
+ .configs = rk3568_txrx_config,
+ .config_count = ARRAY_SIZE(rk3568_txrx_config),
+ .init = common_soc_init,
+};
+
+static struct rk_i2s_soc_data rv1126_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = rv1126_txrx_config,
+ .config_count = ARRAY_SIZE(rv1126_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct of_device_id rockchip_i2s_tdm_match[] = {
+ { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data },
+ { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data },
+ { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data },
+ { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data },
+ { .compatible = "rockchip,rv1126-i2s-tdm", .data = &rv1126_i2s_soc_data },
+ {},
+};
+
+static struct snd_soc_dai_driver i2s_tdm_dai = {
+ .probe = rockchip_i2s_tdm_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ },
+ .capture = {
+ .stream_name = "Capture",
+ },
+ .ops = &rockchip_i2s_tdm_dai_ops,
+};
+
+static void rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ struct property *dma_names;
+ const char *dma_name;
+ u64 formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE);
+ struct device_node *node = i2s_tdm->dev->of_node;
+
+ of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
+ if (!strcmp(dma_name, "tx"))
+ i2s_tdm->has_playback = true;
+ if (!strcmp(dma_name, "rx"))
+ i2s_tdm->has_capture = true;
+ }
+
+ if (i2s_tdm->has_playback) {
+ i2s_tdm_dai.playback.channels_min = 2;
+ i2s_tdm_dai.playback.channels_max = 8;
+ i2s_tdm_dai.playback.rates = SNDRV_PCM_RATE_8000_192000;
+ i2s_tdm_dai.playback.formats = formats;
+ }
+
+ if (i2s_tdm->has_capture) {
+ i2s_tdm_dai.capture.channels_min = 2;
+ i2s_tdm_dai.capture.channels_max = 8;
+ i2s_tdm_dai.capture.rates = SNDRV_PCM_RATE_8000_192000;
+ i2s_tdm_dai.capture.formats = formats;
+ }
+}
+
+static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num,
+ bool is_rx_path)
+{
+ unsigned int *i2s_data;
+ int i, j;
+
+ if (is_rx_path)
+ i2s_data = i2s_tdm->i2s_sdis;
+ else
+ i2s_data = i2s_tdm->i2s_sdos;
+
+ for (i = 0; i < num; i++) {
+ if (i2s_data[i] > CH_GRP_MAX - 1) {
+ dev_err(i2s_tdm->dev,
+ "%s path i2s_data[%d]: %d is too high, max is: %d\n",
+ is_rx_path ? "RX" : "TX",
+ i, i2s_data[i], CH_GRP_MAX);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < num; j++) {
+ if (i == j)
+ continue;
+
+ if (i2s_data[i] == i2s_data[j]) {
+ dev_err(i2s_tdm->dev,
+ "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n",
+ is_rx_path ? "RX" : "TX",
+ i, i2s_data[i],
+ j, i2s_data[j]);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num)
+{
+ int idx;
+
+ for (idx = 0; idx < num; idx++) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_PATH_MASK(idx),
+ I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx]));
+ }
+}
+
+static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num)
+{
+ int idx;
+
+ for (idx = 0; idx < num; idx++) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_PATH_MASK(idx),
+ I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx]));
+ }
+}
+
+static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num, bool is_rx_path)
+{
+ if (is_rx_path)
+ rockchip_i2s_tdm_rx_path_config(i2s_tdm, num);
+ else
+ rockchip_i2s_tdm_tx_path_config(i2s_tdm, num);
+}
+
+static int rockchip_i2s_tdm_get_calibrate_mclks(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int num_mclks = 0;
+
+ i2s_tdm->mclk_tx_src = devm_clk_get(i2s_tdm->dev, "mclk_tx_src");
+ if (!IS_ERR(i2s_tdm->mclk_tx_src))
+ num_mclks++;
+
+ i2s_tdm->mclk_rx_src = devm_clk_get(i2s_tdm->dev, "mclk_rx_src");
+ if (!IS_ERR(i2s_tdm->mclk_rx_src))
+ num_mclks++;
+
+ i2s_tdm->mclk_root0 = devm_clk_get(i2s_tdm->dev, "mclk_root0");
+ if (!IS_ERR(i2s_tdm->mclk_root0))
+ num_mclks++;
+
+ i2s_tdm->mclk_root1 = devm_clk_get(i2s_tdm->dev, "mclk_root1");
+ if (!IS_ERR(i2s_tdm->mclk_root1))
+ num_mclks++;
+
+ if (num_mclks < 4 && num_mclks != 0)
+ return -ENOENT;
+
+ if (num_mclks == 4)
+ i2s_tdm->mclk_calibrate = 1;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np,
+ bool is_rx_path)
+{
+ char *i2s_tx_path_prop = "rockchip,i2s-tx-route";
+ char *i2s_rx_path_prop = "rockchip,i2s-rx-route";
+ char *i2s_path_prop;
+ unsigned int *i2s_data;
+ int num, ret = 0;
+
+ if (is_rx_path) {
+ i2s_path_prop = i2s_rx_path_prop;
+ i2s_data = i2s_tdm->i2s_sdis;
+ } else {
+ i2s_path_prop = i2s_tx_path_prop;
+ i2s_data = i2s_tdm->i2s_sdos;
+ }
+
+ num = of_count_phandle_with_args(np, i2s_path_prop, NULL);
+ if (num < 0) {
+ if (num != -ENOENT) {
+ dev_err(i2s_tdm->dev,
+ "Failed to read '%s' num: %d\n",
+ i2s_path_prop, num);
+ ret = num;
+ }
+ return ret;
+ } else if (num != CH_GRP_MAX) {
+ dev_err(i2s_tdm->dev,
+ "The num: %d should be: %d\n", num, CH_GRP_MAX);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(np, i2s_path_prop,
+ i2s_data, num);
+ if (ret < 0) {
+ dev_err(i2s_tdm->dev,
+ "Failed to read '%s': %d\n",
+ i2s_path_prop, ret);
+ return ret;
+ }
+
+ ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path);
+ if (ret < 0) {
+ dev_err(i2s_tdm->dev,
+ "Failed to check i2s data bus: %d\n", ret);
+ return ret;
+ }
+
+ rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np)
+{
+ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0);
+}
+
+static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np)
+{
+ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1);
+}
+
+static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ const struct of_device_id *of_id;
+ struct rk_i2s_tdm_dev *i2s_tdm;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL);
+ if (!i2s_tdm)
+ return -ENOMEM;
+
+ i2s_tdm->dev = &pdev->dev;
+
+ of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
+ if (!of_id || !of_id->data)
+ return -EINVAL;
+
+ spin_lock_init(&i2s_tdm->lock);
+ i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data;
+
+ rockchip_i2s_tdm_init_dai(i2s_tdm);
+
+ i2s_tdm->frame_width = 64;
+
+ i2s_tdm->clk_trcm = TRCM_TXRX;
+ if (of_property_read_bool(node, "rockchip,trcm-sync-tx-only"))
+ i2s_tdm->clk_trcm = TRCM_TX;
+ if (of_property_read_bool(node, "rockchip,trcm-sync-rx-only")) {
+ if (i2s_tdm->clk_trcm) {
+ dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n");
+ return -EINVAL;
+ }
+ i2s_tdm->clk_trcm = TRCM_RX;
+ }
+ if (i2s_tdm->clk_trcm != TRCM_TXRX)
+ i2s_tdm_dai.symmetric_rate = 1;
+
+ i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+ if (IS_ERR(i2s_tdm->grf))
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->grf),
+ "Error in rockchip,grf\n");
+
+ i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ "tx-m");
+ if (IS_ERR(i2s_tdm->tx_reset)) {
+ ret = PTR_ERR(i2s_tdm->tx_reset);
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Error in tx-m reset control\n");
+ }
+
+ i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ "rx-m");
+ if (IS_ERR(i2s_tdm->rx_reset)) {
+ ret = PTR_ERR(i2s_tdm->rx_reset);
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Error in rx-m reset control\n");
+ }
+
+ i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(i2s_tdm->hclk)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->hclk),
+ "Failed to get clock hclk\n");
+ }
+
+ i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx");
+ if (IS_ERR(i2s_tdm->mclk_tx)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_tx),
+ "Failed to get clock mclk_tx\n");
+ }
+
+ i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx");
+ if (IS_ERR(i2s_tdm->mclk_rx)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_rx),
+ "Failed to get clock mclk_rx\n");
+ }
+
+ i2s_tdm->io_multiplex =
+ of_property_read_bool(node, "rockchip,io-multiplex");
+
+ ret = rockchip_i2s_tdm_get_calibrate_mclks(i2s_tdm);
+ if (ret)
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "mclk-calibrate clocks missing");
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs),
+ "Failed to get resource IORESOURCE_MEM\n");
+ }
+
+ i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &rockchip_i2s_tdm_regmap_config);
+ if (IS_ERR(i2s_tdm->regmap)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->regmap),
+ "Failed to initialise regmap\n");
+ }
+
+ if (i2s_tdm->has_playback) {
+ i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
+ i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s_tdm->playback_dma_data.maxburst = 8;
+ }
+
+ if (i2s_tdm->has_capture) {
+ i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;
+ i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s_tdm->capture_dma_data.maxburst = 8;
+ }
+
+ ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&pdev->dev, i2s_tdm);
+
+ ret = clk_prepare_enable(i2s_tdm->hclk);
+ if (ret) {
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Failed to enable clock hclk\n");
+ }
+
+ ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
+ if (ret) {
+ ret = dev_err_probe(i2s_tdm->dev, ret,
+ "Failed to enable one or more mclks\n");
+ goto err_disable_hclk;
+ }
+
+ if (i2s_tdm->mclk_calibrate) {
+ i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0);
+ i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1);
+ i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq;
+ i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
+ I2S_DMACR_TDL(16));
+ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
+ I2S_DMACR_RDL(16));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK,
+ i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT);
+
+ if (i2s_tdm->soc_data && i2s_tdm->soc_data->init)
+ i2s_tdm->soc_data->init(&pdev->dev, res->start);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rockchip_i2s_tdm_component,
+ &i2s_tdm_dai, 1);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_suspend;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM\n");
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ i2s_tdm_runtime_suspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+err_disable_hclk:
+ clk_disable_unprepare(i2s_tdm->hclk);
+
+ return ret;
+}
+
+static int rockchip_i2s_tdm_remove(struct platform_device *pdev)
+{
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ i2s_tdm_runtime_suspend(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(i2s_tdm->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return ret;
+ ret = regcache_sync(i2s_tdm->regmap);
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend,
+ rockchip_i2s_tdm_resume)
+};
+
+static struct platform_driver rockchip_i2s_tdm_driver = {
+ .probe = rockchip_i2s_tdm_probe,
+ .remove = rockchip_i2s_tdm_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(rockchip_i2s_tdm_match),
+ .pm = &rockchip_i2s_tdm_pm_ops,
+ },
+};
+module_platform_driver(rockchip_i2s_tdm_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface");
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match);
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h
new file mode 100644
index 000000000000..0aa1c6da1e2c
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.h
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
+ *
+ * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
+ * Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+ *
+ */
+
+#ifndef _ROCKCHIP_I2S_TDM_H
+#define _ROCKCHIP_I2S_TDM_H
+
+/*
+ * TXCR
+ * transmit operation control register
+ */
+#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2)
+#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x))
+#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x))
+#define I2S_TXCR_RCNT_SHIFT 17
+#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT)
+#define I2S_TXCR_CSR_SHIFT 15
+#define I2S_TXCR_CSR(x) ((x) << I2S_TXCR_CSR_SHIFT)
+#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT)
+#define I2S_TXCR_HWT BIT(14)
+#define I2S_TXCR_SJM_SHIFT 12
+#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT)
+#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT)
+#define I2S_TXCR_FBM_SHIFT 11
+#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT)
+#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT)
+#define I2S_TXCR_IBM_SHIFT 9
+#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_PBM_SHIFT 7
+#define I2S_TXCR_PBM_MODE(x) ((x) << I2S_TXCR_PBM_SHIFT)
+#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT)
+#define I2S_TXCR_TFS_SHIFT 5
+#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_VDW_SHIFT 0
+#define I2S_TXCR_VDW(x) (((x) - 1) << I2S_TXCR_VDW_SHIFT)
+#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
+
+/*
+ * RXCR
+ * receive operation control register
+ */
+#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2)
+#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x))
+#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x))
+#define I2S_RXCR_CSR_SHIFT 15
+#define I2S_RXCR_CSR(x) ((x) << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_HWT BIT(14)
+#define I2S_RXCR_SJM_SHIFT 12
+#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
+#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT)
+#define I2S_RXCR_FBM_SHIFT 11
+#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT)
+#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT)
+#define I2S_RXCR_IBM_SHIFT 9
+#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_PBM_SHIFT 7
+#define I2S_RXCR_PBM_MODE(x) ((x) << I2S_RXCR_PBM_SHIFT)
+#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT)
+#define I2S_RXCR_TFS_SHIFT 5
+#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_VDW_SHIFT 0
+#define I2S_RXCR_VDW(x) (((x) - 1) << I2S_RXCR_VDW_SHIFT)
+#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
+
+/*
+ * CKR
+ * clock generation register
+ */
+#define I2S_CKR_TRCM_SHIFT 28
+#define I2S_CKR_TRCM(x) ((x) << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_MSS_SHIFT 27
+#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_CKP_SHIFT 26
+#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_RLP_SHIFT 25
+#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_TLP_SHIFT 24
+#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_MDIV_SHIFT 16
+#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT)
+#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
+#define I2S_CKR_RSD_SHIFT 8
+#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT)
+#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT)
+#define I2S_CKR_TSD_SHIFT 0
+#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT)
+#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT)
+
+/*
+ * FIFOLR
+ * FIFO level register
+ */
+#define I2S_FIFOLR_RFL_SHIFT 24
+#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT)
+#define I2S_FIFOLR_TFL3_SHIFT 18
+#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT)
+#define I2S_FIFOLR_TFL2_SHIFT 12
+#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT)
+#define I2S_FIFOLR_TFL1_SHIFT 6
+#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT)
+#define I2S_FIFOLR_TFL0_SHIFT 0
+#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT)
+
+/*
+ * DMACR
+ * DMA control register
+ */
+#define I2S_DMACR_RDE_SHIFT 24
+#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT)
+#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT)
+#define I2S_DMACR_RDL_SHIFT 16
+#define I2S_DMACR_RDL(x) (((x) - 1) << I2S_DMACR_RDL_SHIFT)
+#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT)
+#define I2S_DMACR_TDE_SHIFT 8
+#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT)
+#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT)
+#define I2S_DMACR_TDL_SHIFT 0
+#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT)
+#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT)
+
+/*
+ * INTCR
+ * interrupt control register
+ */
+#define I2S_INTCR_RFT_SHIFT 20
+#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT)
+#define I2S_INTCR_RXOIC BIT(18)
+#define I2S_INTCR_RXOIE_SHIFT 17
+#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
+#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
+#define I2S_INTCR_RXFIE_SHIFT 16
+#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
+#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
+#define I2S_INTCR_TFT_SHIFT 4
+#define I2S_INTCR_TFT(x) (((x) - 1) << I2S_INTCR_TFT_SHIFT)
+#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
+#define I2S_INTCR_TXUIC BIT(2)
+#define I2S_INTCR_TXUIE_SHIFT 1
+#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
+#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
+
+/*
+ * INTSR
+ * interrupt status register
+ */
+#define I2S_INTSR_TXEIE_SHIFT 0
+#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT)
+#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT)
+#define I2S_INTSR_RXOI_SHIFT 17
+#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT)
+#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT)
+#define I2S_INTSR_RXFI_SHIFT 16
+#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT)
+#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT)
+#define I2S_INTSR_TXUI_SHIFT 1
+#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT)
+#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT)
+#define I2S_INTSR_TXEI_SHIFT 0
+#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT)
+#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT)
+
+/*
+ * XFER
+ * Transfer start register
+ */
+#define I2S_XFER_RXS_SHIFT 1
+#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT)
+#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT)
+#define I2S_XFER_TXS_SHIFT 0
+#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT)
+#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT)
+
+/*
+ * CLR
+ * clear SCLK domain logic register
+ */
+#define I2S_CLR_RXC BIT(1)
+#define I2S_CLR_TXC BIT(0)
+
+/*
+ * TXDR
+ * Transimt FIFO data register, write only.
+ */
+#define I2S_TXDR_MASK (0xff)
+
+/*
+ * RXDR
+ * Receive FIFO data register, write only.
+ */
+#define I2S_RXDR_MASK (0xff)
+
+/*
+ * TDM_CTRL
+ * TDM ctrl register
+ */
+#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18)
+#define TDM_FSYNC_WIDTH_SEL1(x) (((x) - 1) << 18)
+#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17)
+#define TDM_FSYNC_WIDTH_HALF_FRAME 0
+#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17)
+#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14)
+#define TDM_SHIFT_CTRL(x) ((x) << 14)
+#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9)
+#define TDM_SLOT_BIT_WIDTH(x) (((x) - 1) << 9)
+#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0)
+#define TDM_FRAME_WIDTH(x) (((x) - 1) << 0)
+
+/*
+ * CLKDIV
+ * Mclk div register
+ */
+#define I2S_CLKDIV_TXM_SHIFT 0
+#define I2S_CLKDIV_TXM(x) (((x) - 1) << I2S_CLKDIV_TXM_SHIFT)
+#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT)
+#define I2S_CLKDIV_RXM_SHIFT 8
+#define I2S_CLKDIV_RXM(x) (((x) - 1) << I2S_CLKDIV_RXM_SHIFT)
+#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT)
+
+/* Clock divider id */
+enum {
+ ROCKCHIP_DIV_MCLK = 0,
+ ROCKCHIP_DIV_BCLK,
+};
+
+/* channel select */
+#define I2S_CSR_SHIFT 15
+#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
+
+/* io direction cfg register */
+#define I2S_IO_DIRECTION_MASK (7)
+#define I2S_IO_8CH_OUT_2CH_IN (7)
+#define I2S_IO_6CH_OUT_4CH_IN (3)
+#define I2S_IO_4CH_OUT_6CH_IN (1)
+#define I2S_IO_2CH_OUT_8CH_IN (0)
+
+/* I2S REGS */
+#define I2S_TXCR (0x0000)
+#define I2S_RXCR (0x0004)
+#define I2S_CKR (0x0008)
+#define I2S_TXFIFOLR (0x000c)
+#define I2S_DMACR (0x0010)
+#define I2S_INTCR (0x0014)
+#define I2S_INTSR (0x0018)
+#define I2S_XFER (0x001c)
+#define I2S_CLR (0x0020)
+#define I2S_TXDR (0x0024)
+#define I2S_RXDR (0x0028)
+#define I2S_RXFIFOLR (0x002c)
+#define I2S_TDM_TXCR (0x0030)
+#define I2S_TDM_RXCR (0x0034)
+#define I2S_CLKDIV (0x0038)
+
+#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
+
+/* PX30 GRF CONFIGS */
+#define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12)
+#define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12)
+#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
+#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
+
+#define PX30_I2S0_CLK_TXONLY \
+ (PX30_I2S0_MCLK_OUT_SRC_FROM_TX | PX30_I2S0_CLK_IN_SRC_FROM_TX)
+
+#define PX30_I2S0_CLK_RXONLY \
+ (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX)
+
+/* RK1808 GRF CONFIGS */
+#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
+#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
+#define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0)
+#define RK1808_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 1, 0)
+
+#define RK1808_I2S0_CLK_TXONLY \
+ (RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | RK1808_I2S0_CLK_IN_SRC_FROM_TX)
+
+#define RK1808_I2S0_CLK_RXONLY \
+ (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX)
+
+/* RK3308 GRF CONFIGS */
+#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10)
+#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10)
+#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9)
+#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 9, 9)
+#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 8, 8)
+#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 8, 8)
+#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
+#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
+#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 1, 1)
+#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 1, 1)
+#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 0, 0)
+#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 0, 0)
+
+#define RK3308_I2S0_CLK_TXONLY \
+ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \
+ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \
+ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX)
+
+#define RK3308_I2S0_CLK_RXONLY \
+ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \
+ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \
+ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX)
+
+#define RK3308_I2S1_CLK_TXONLY \
+ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \
+ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \
+ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX)
+
+#define RK3308_I2S1_CLK_RXONLY \
+ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \
+ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \
+ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX)
+
+/* RK3568 GRF CONFIGS */
+#define RK3568_I2S1_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
+#define RK3568_I2S1_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
+
+#define RK3568_I2S1_CLK_TXONLY \
+ RK3568_I2S1_MCLK_OUT_SRC_FROM_TX
+
+#define RK3568_I2S1_CLK_RXONLY \
+ RK3568_I2S1_MCLK_OUT_SRC_FROM_RX
+
+#define RK3568_I2S3_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 15, 15)
+#define RK3568_I2S3_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 15, 15)
+#define RK3568_I2S3_SCLK_SRC_FROM_TX HIWORD_UPDATE(1, 7, 7)
+#define RK3568_I2S3_SCLK_SRC_FROM_RX HIWORD_UPDATE(0, 7, 7)
+#define RK3568_I2S3_LRCK_SRC_FROM_TX HIWORD_UPDATE(1, 6, 6)
+#define RK3568_I2S3_LRCK_SRC_FROM_RX HIWORD_UPDATE(0, 6, 6)
+
+#define RK3568_I2S3_MCLK_TXONLY \
+ RK3568_I2S3_MCLK_OUT_SRC_FROM_TX
+
+#define RK3568_I2S3_CLK_TXONLY \
+ (RK3568_I2S3_SCLK_SRC_FROM_TX | \
+ RK3568_I2S3_LRCK_SRC_FROM_TX)
+
+#define RK3568_I2S3_MCLK_RXONLY \
+ RK3568_I2S3_MCLK_OUT_SRC_FROM_RX
+
+#define RK3568_I2S3_CLK_RXONLY \
+ (RK3568_I2S3_SCLK_SRC_FROM_RX | \
+ RK3568_I2S3_LRCK_SRC_FROM_RX)
+
+#define RK3568_I2S3_MCLK_IE HIWORD_UPDATE(0, 3, 3)
+#define RK3568_I2S3_MCLK_OE HIWORD_UPDATE(1, 3, 3)
+#define RK3568_I2S2_MCLK_IE HIWORD_UPDATE(0, 2, 2)
+#define RK3568_I2S2_MCLK_OE HIWORD_UPDATE(1, 2, 2)
+#define RK3568_I2S1_MCLK_TX_IE HIWORD_UPDATE(0, 1, 1)
+#define RK3568_I2S1_MCLK_TX_OE HIWORD_UPDATE(1, 1, 1)
+#define RK3568_I2S1_MCLK_RX_IE HIWORD_UPDATE(0, 0, 0)
+#define RK3568_I2S1_MCLK_RX_OE HIWORD_UPDATE(1, 0, 0)
+
+/* RV1126 GRF CONFIGS */
+#define RV1126_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 9, 9)
+#define RV1126_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 9, 9)
+
+#define RV1126_I2S0_CLK_TXONLY \
+ RV1126_I2S0_MCLK_OUT_SRC_FROM_TX
+
+#define RV1126_I2S0_CLK_RXONLY \
+ RV1126_I2S0_MCLK_OUT_SRC_FROM_RX
+
+#endif /* _ROCKCHIP_I2S_TDM_H */
diff --git a/sound/soc/rockchip/rockchip_pcm.c b/sound/soc/rockchip/rockchip_pcm.c
deleted file mode 100644
index 02254e42135e..000000000000
--- a/sound/soc/rockchip/rockchip_pcm.c
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
- */
-
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "rockchip_pcm.h"
-
-static const struct snd_pcm_hardware snd_rockchip_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_INTERLEAVED,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 52,
- .buffer_bytes_max = 64 * 1024,
- .fifo_size = 32,
-};
-
-static const struct snd_dmaengine_pcm_config rk_dmaengine_pcm_config = {
- .pcm_hardware = &snd_rockchip_hardware,
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .prealloc_buffer_size = 32 * 1024,
-};
-
-int rockchip_pcm_platform_register(struct device *dev)
-{
- return devm_snd_dmaengine_pcm_register(dev, &rk_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_COMPAT);
-}
-EXPORT_SYMBOL_GPL(rockchip_pcm_platform_register);
-
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rockchip_pcm.h b/sound/soc/rockchip/rockchip_pcm.h
deleted file mode 100644
index 7f00e2ce3603..000000000000
--- a/sound/soc/rockchip/rockchip_pcm.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
- */
-
-#ifndef _ROCKCHIP_PCM_H
-#define _ROCKCHIP_PCM_H
-
-int rockchip_pcm_platform_register(struct device *dev);
-
-#endif
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index 38bd603eeb45..64d9891b6434 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -20,10 +20,12 @@
#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */
#define PDM_SIGNOFF_CLK_RATE (100000000)
+#define PDM_PATH_MAX (4)
enum rk_pdm_version {
RK_PDM_RK3229,
RK_PDM_RK3308,
+ RK_PDM_RV1126,
};
struct rk_pdm_dev {
@@ -121,6 +123,55 @@ static unsigned int get_pdm_ds_ratio(unsigned int sr)
return ratio;
}
+static unsigned int get_pdm_cic_ratio(unsigned int clk)
+{
+ switch (clk) {
+ case 4096000:
+ case 5644800:
+ case 6144000:
+ return 0;
+ case 2048000:
+ case 2822400:
+ case 3072000:
+ return 1;
+ case 1024000:
+ case 1411200:
+ case 1536000:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static unsigned int samplerate_to_bit(unsigned int samplerate)
+{
+ switch (samplerate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ return 0;
+ case 16000:
+ case 22050:
+ case 24000:
+ return 1;
+ case 32000:
+ return 2;
+ case 44100:
+ case 48000:
+ return 3;
+ case 64000:
+ case 88200:
+ case 96000:
+ return 4;
+ case 128000:
+ case 176400:
+ case 192000:
+ return 5;
+ default:
+ return 1;
+ }
+}
+
static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)
{
return snd_soc_dai_get_drvdata(dai);
@@ -166,7 +217,8 @@ static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
if (ret)
return -EINVAL;
- if (pdm->version == RK_PDM_RK3308) {
+ if (pdm->version == RK_PDM_RK3308 ||
+ pdm->version == RK_PDM_RV1126) {
rational_best_approximation(clk_out, clk_src,
GENMASK(16 - 1, 0),
GENMASK(16 - 1, 0),
@@ -194,8 +246,18 @@ static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
PDM_CLK_FD_RATIO_MSK,
val);
}
- val = get_pdm_ds_ratio(samplerate);
- regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);
+
+ if (pdm->version == RK_PDM_RV1126) {
+ val = get_pdm_cic_ratio(clk_out);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val);
+ val = samplerate_to_bit(samplerate);
+ regmap_update_bits(pdm->regmap, PDM_CTRL0,
+ PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val));
+ } else {
+ val = get_pdm_ds_ratio(samplerate);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);
+ }
+
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_CF_MSK, PDM_HPF_60HZ);
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
@@ -441,9 +503,10 @@ static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg)
}
static const struct reg_default rockchip_pdm_reg_defaults[] = {
- {0x04, 0x78000017},
- {0x08, 0x0bb8ea60},
- {0x18, 0x0000001f},
+ { PDM_CTRL0, 0x78000017 },
+ { PDM_CTRL1, 0x0bb8ea60 },
+ { PDM_CLK_CTRL, 0x0000e401 },
+ { PDM_DMA_CTRL, 0x0000001f },
};
static const struct regmap_config rockchip_pdm_regmap_config = {
@@ -469,12 +532,44 @@ static const struct of_device_id rockchip_pdm_match[] __maybe_unused = {
.data = (void *)RK_PDM_RK3308 },
{ .compatible = "rockchip,rk3308-pdm",
.data = (void *)RK_PDM_RK3308 },
+ { .compatible = "rockchip,rk3568-pdm",
+ .data = (void *)RK_PDM_RV1126 },
+ { .compatible = "rockchip,rv1126-pdm",
+ .data = (void *)RK_PDM_RV1126 },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_pdm_match);
+static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node)
+{
+ unsigned int path[PDM_PATH_MAX];
+ int cnt = 0, ret = 0, i = 0, val = 0, msk = 0;
+
+ cnt = of_count_phandle_with_args(node, "rockchip,path-map",
+ NULL);
+ if (cnt != PDM_PATH_MAX)
+ return cnt;
+
+ ret = of_property_read_u32_array(node, "rockchip,path-map",
+ path, cnt);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < cnt; i++) {
+ if (path[i] >= PDM_PATH_MAX)
+ return -EINVAL;
+ msk |= PDM_PATH_MASK(i);
+ val |= PDM_PATH(i, path[i]);
+ }
+
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val);
+
+ return 0;
+}
+
static int rockchip_pdm_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
struct rk_pdm_dev *pdm;
struct resource *res;
@@ -540,6 +635,11 @@ static int rockchip_pdm_probe(struct platform_device *pdev)
}
rockchip_pdm_rxctrl(pdm, 0);
+
+ ret = rockchip_pdm_path_parse(pdm, node);
+ if (ret != 0 && ret != -ENOENT)
+ goto err_suspend;
+
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h
index 8e5bbafef7bb..cab977272ee6 100644
--- a/sound/soc/rockchip/rockchip_pdm.h
+++ b/sound/soc/rockchip/rockchip_pdm.h
@@ -41,6 +41,8 @@
#define PDM_PATH1_EN BIT(28)
#define PDM_PATH0_EN BIT(27)
#define PDM_HWT_EN BIT(26)
+#define PDM_SAMPLERATE_MSK GENMASK(7, 5)
+#define PDM_SAMPLERATE(x) ((x) << 5)
#define PDM_VDW_MSK (0x1f << 0)
#define PDM_VDW(X) ((X - 1) << 0)
@@ -51,6 +53,9 @@
#define PDM_FD_DENOMINATOR_MSK GENMASK(15, 0)
/* PDM CLK CTRL */
+#define PDM_PATH_SHIFT(x) (8 + (x) * 2)
+#define PDM_PATH_MASK(x) (0x3 << PDM_PATH_SHIFT(x))
+#define PDM_PATH(x, v) ((v) << PDM_PATH_SHIFT(x))
#define PDM_CLK_FD_RATIO_MSK BIT(6)
#define PDM_CLK_FD_RATIO_40 (0X0 << 6)
#define PDM_CLK_FD_RATIO_35 BIT(6)
@@ -66,6 +71,7 @@
#define PDM_CLK_1280FS (0x2 << 0)
#define PDM_CLK_2560FS (0x3 << 0)
#define PDM_CLK_5120FS (0x4 << 0)
+#define PDM_CIC_RATIO_MSK (0x3 << 0)
/* PDM HPF CTRL */
#define PDM_HPF_LE BIT(3)
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index e9481187a08c..de66cc422e6e 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -397,6 +397,8 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
/* clear again, just in case */
writel(0x0, i2s->regs + S3C2412_IISFIC);
+ fallthrough;
+
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!i2s->master) {
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 978bd0406729..6a8fe0da7670 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -1225,6 +1225,7 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name
if (i < 0) {
dev_err(dev, "strange node numbering (%s)",
of_node_full_name(node));
+ of_node_put(np);
return 0;
}
i++;
diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c
index 395229bf5c51..2ae99b49d3f5 100644
--- a/sound/soc/soc-acpi.c
+++ b/sound/soc/soc-acpi.c
@@ -8,14 +8,34 @@
#include <linux/module.h>
#include <sound/soc-acpi.h>
+static bool snd_soc_acpi_id_present(struct snd_soc_acpi_mach *machine)
+{
+ const struct snd_soc_acpi_codecs *comp_ids = machine->comp_ids;
+ int i;
+
+ if (machine->id[0]) {
+ if (acpi_dev_present(machine->id, NULL, -1))
+ return true;
+ }
+
+ if (comp_ids) {
+ for (i = 0; i < comp_ids->num_codecs; i++) {
+ if (acpi_dev_present(comp_ids->codecs[i], NULL, -1))
+ return true;
+ }
+ }
+
+ return false;
+}
+
struct snd_soc_acpi_mach *
snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
{
struct snd_soc_acpi_mach *mach;
struct snd_soc_acpi_mach *mach_alt;
- for (mach = machines; mach->id[0]; mach++) {
- if (acpi_dev_present(mach->id, NULL, -1)) {
+ for (mach = machines; mach->id[0] || mach->comp_ids; mach++) {
+ if (snd_soc_acpi_id_present(mach)) {
if (mach->machine_quirk) {
mach_alt = mach->machine_quirk(mach);
if (!mach_alt)
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 8e8d917d22f8..c76ff9c59dfb 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -13,9 +13,10 @@
#include <sound/soc.h>
#include <linux/bitops.h>
-#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret)
+#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret, -1)
+#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret(dai, __func__, ret, reg)
static inline int _soc_component_ret(struct snd_soc_component *component,
- const char *func, int ret)
+ const char *func, int ret, int reg)
{
/* Positive/Zero values are not errors */
if (ret >= 0)
@@ -27,9 +28,14 @@ static inline int _soc_component_ret(struct snd_soc_component *component,
case -ENOTSUPP:
break;
default:
- dev_err(component->dev,
- "ASoC: error at %s on %s: %d\n",
- func, component->name, ret);
+ if (reg == -1)
+ dev_err(component->dev,
+ "ASoC: error at %s on %s: %d\n",
+ func, component->name, ret);
+ else
+ dev_err(component->dev,
+ "ASoC: error at %s on %s for register: [0x%08x] %d\n",
+ func, component->name, reg, ret);
}
return ret;
@@ -251,8 +257,7 @@ int snd_soc_component_set_jack(struct snd_soc_component *component,
EXPORT_SYMBOL_GPL(snd_soc_component_set_jack);
int snd_soc_component_module_get(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- int upon_open)
+ void *mark, int upon_open)
{
int ret = 0;
@@ -260,25 +265,24 @@ int snd_soc_component_module_get(struct snd_soc_component *component,
!try_module_get(component->dev->driver->owner))
ret = -ENODEV;
- /* mark substream if succeeded */
+ /* mark module if succeeded */
if (ret == 0)
- soc_component_mark_push(component, substream, module);
+ soc_component_mark_push(component, mark, module);
return soc_component_ret(component, ret);
}
void snd_soc_component_module_put(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- int upon_open, int rollback)
+ void *mark, int upon_open, int rollback)
{
- if (rollback && !soc_component_mark_match(component, substream, module))
+ if (rollback && !soc_component_mark_match(component, mark, module))
return;
if (component->driver->module_get_upon_open == !!upon_open)
module_put(component->dev->driver->owner);
- /* remove marked substream */
- soc_component_mark_pop(component, substream, module);
+ /* remove the mark from module */
+ soc_component_mark_pop(component, mark, module);
}
int snd_soc_component_open(struct snd_soc_component *component,
@@ -425,43 +429,36 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
#endif
-int snd_soc_component_compr_open(struct snd_compr_stream *cstream)
+int snd_soc_component_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_component *component;
- int i, ret;
+ int ret = 0;
- for_each_rtd_components(rtd, i, component) {
- if (component->driver->compress_ops &&
- component->driver->compress_ops->open) {
- ret = component->driver->compress_ops->open(component, cstream);
- if (ret < 0)
- return soc_component_ret(component, ret);
- }
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->open)
+ ret = component->driver->compress_ops->open(component, cstream);
+
+ /* mark substream if succeeded */
+ if (ret == 0)
soc_component_mark_push(component, cstream, compr_open);
- }
- return 0;
+ return soc_component_ret(component, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_component_compr_open);
-void snd_soc_component_compr_free(struct snd_compr_stream *cstream,
+void snd_soc_component_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
int rollback)
{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_component *component;
- int i;
-
- for_each_rtd_components(rtd, i, component) {
- if (rollback && !soc_component_mark_match(component, cstream, compr_open))
- continue;
+ if (rollback && !soc_component_mark_match(component, cstream, compr_open))
+ return;
- if (component->driver->compress_ops &&
- component->driver->compress_ops->free)
- component->driver->compress_ops->free(component, cstream);
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->free)
+ component->driver->compress_ops->free(component, cstream);
- soc_component_mark_pop(component, cstream, compr_open);
- }
+ /* remove marked substream */
+ soc_component_mark_pop(component, cstream, compr_open);
}
EXPORT_SYMBOL_GPL(snd_soc_component_compr_free);
@@ -696,7 +693,7 @@ static unsigned int soc_component_read_no_lock(
ret = -EIO;
if (ret < 0)
- return soc_component_ret(component, ret);
+ return soc_component_ret_reg_rw(component, ret, reg);
return val;
}
@@ -732,7 +729,7 @@ static int soc_component_write_no_lock(
else if (component->driver->write)
ret = component->driver->write(component, reg, val);
- return soc_component_ret(component, ret);
+ return soc_component_ret_reg_rw(component, ret, reg);
}
/**
@@ -774,7 +771,7 @@ static int snd_soc_component_update_bits_legacy(
mutex_unlock(&component->io_mutex);
- return soc_component_ret(component, ret);
+ return soc_component_ret_reg_rw(component, ret, reg);
}
/**
@@ -802,7 +799,7 @@ int snd_soc_component_update_bits(struct snd_soc_component *component,
mask, val, &change);
if (ret < 0)
- return soc_component_ret(component, ret);
+ return soc_component_ret_reg_rw(component, ret, reg);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
@@ -838,7 +835,7 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component,
mask, val, &change);
if (ret < 0)
- return soc_component_ret(component, ret);
+ return soc_component_ret_reg_rw(component, ret, reg);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 36060800e9bd..8e2494a9f3a7 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -22,6 +22,39 @@
#include <sound/soc-link.h>
#include <linux/pm_runtime.h>
+static int snd_soc_compr_components_open(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int ret = 0;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ ret = snd_soc_component_module_get_when_open(component, cstream);
+ if (ret < 0)
+ break;
+
+ ret = snd_soc_component_compr_open(component, cstream);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void snd_soc_compr_components_free(struct snd_compr_stream *cstream,
+ int rollback)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ snd_soc_component_compr_free(component, cstream, rollback);
+ snd_soc_component_module_put_when_close(component, cstream, rollback);
+ }
+}
+
static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -44,7 +77,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
snd_soc_link_compr_shutdown(cstream, rollback);
- snd_soc_component_compr_free(cstream, rollback);
+ snd_soc_compr_components_free(cstream, rollback);
snd_soc_dai_compr_shutdown(cpu_dai, cstream, rollback);
@@ -80,7 +113,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
if (ret < 0)
goto err;
- ret = snd_soc_component_compr_open(cstream);
+ ret = snd_soc_compr_components_open(cstream);
if (ret < 0)
goto err;
@@ -137,7 +170,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (ret < 0)
goto out;
- ret = snd_soc_component_compr_open(cstream);
+ ret = snd_soc_compr_components_open(cstream);
if (ret < 0)
goto open_err;
@@ -160,7 +193,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
return 0;
machine_err:
- snd_soc_component_compr_free(cstream, 1);
+ snd_soc_compr_components_free(cstream, 1);
open_err:
snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1);
out:
@@ -205,7 +238,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
snd_soc_link_compr_shutdown(cstream, 0);
- snd_soc_component_compr_free(cstream, 0);
+ snd_soc_compr_components_free(cstream, 0);
snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 80ca260595fd..dcf6be4c4aaa 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -229,31 +229,12 @@ static void snd_soc_debugfs_exit(void)
#else
-static inline void soc_init_component_debugfs(
- struct snd_soc_component *component)
-{
-}
-
-static inline void soc_cleanup_component_debugfs(
- struct snd_soc_component *component)
-{
-}
-
-static inline void soc_init_card_debugfs(struct snd_soc_card *card)
-{
-}
-
-static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
-{
-}
-
-static inline void snd_soc_debugfs_init(void)
-{
-}
-
-static inline void snd_soc_debugfs_exit(void)
-{
-}
+static inline void soc_init_component_debugfs(struct snd_soc_component *component) { }
+static inline void soc_cleanup_component_debugfs(struct snd_soc_component *component) { }
+static inline void soc_init_card_debugfs(struct snd_soc_card *card) { }
+static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { }
+static inline void snd_soc_debugfs_init(void) { }
+static inline void snd_soc_debugfs_exit(void) { }
#endif
@@ -739,9 +720,7 @@ static void soc_resume_init(struct snd_soc_card *card)
#else
#define snd_soc_suspend NULL
#define snd_soc_resume NULL
-static inline void soc_resume_init(struct snd_soc_card *card)
-{
-}
+static inline void soc_resume_init(struct snd_soc_card *card) { }
#endif
static struct device_node
@@ -1239,6 +1218,9 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
unsigned int i;
int ret;
+ if (!dai_fmt)
+ return 0;
+
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
@@ -1247,14 +1229,13 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
/*
* Flip the polarity for the "CPU" end of a CODEC<->CODEC link
- * the component which has non_legacy_dai_naming is Codec
*/
inv_dai_fmt = snd_soc_daifmt_clock_provider_fliped(dai_fmt);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
unsigned int fmt = dai_fmt;
- if (cpu_dai->component->driver->non_legacy_dai_naming)
+ if (snd_soc_component_is_codec(cpu_dai->component))
fmt = inv_dai_fmt;
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
@@ -1283,11 +1264,9 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card,
return ret;
snd_soc_runtime_get_dai_fmt(rtd);
- if (dai_link->dai_fmt) {
- ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
- if (ret)
- return ret;
- }
+ ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
+ if (ret)
+ return ret;
/* add DPCM sysfs entries */
soc_dpcm_debugfs_add(rtd);
@@ -1363,9 +1342,6 @@ static void soc_remove_component(struct snd_soc_component *component,
if (probed)
snd_soc_component_remove(component);
- /* For framework level robustness */
- snd_soc_component_set_jack(component, NULL, NULL);
-
list_del_init(&component->card_list);
snd_soc_dapm_free(snd_soc_component_get_dapm(component));
soc_cleanup_component_debugfs(component);
@@ -2521,7 +2497,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
for (i = 0; i < count; i++) {
dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
- !component->driver->non_legacy_dai_naming);
+ !snd_soc_component_is_codec(component));
if (dai == NULL) {
ret = -ENOMEM;
goto err;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 59d07648a7e7..2892b0aba151 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1331,11 +1331,13 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
return paths;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets);
void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list)
{
dapm_widget_list_free(list);
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets);
/*
* Handler for regulator supply widget.
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 4aa48c74f21a..c54c8ca8d715 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -15,6 +15,10 @@
#include <sound/dmaengine_pcm.h>
+static unsigned int prealloc_buffer_size_kbytes = 512;
+module_param(prealloc_buffer_size_kbytes, uint, 0444);
+MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB).");
+
/*
* The platforms dmaengine driver does not support reporting the amount of
* bytes that are still left to transfer.
@@ -237,7 +241,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
prealloc_buffer_size = config->prealloc_buffer_size;
max_buffer_size = config->pcm_hardware->buffer_bytes_max;
} else {
- prealloc_buffer_size = 512 * 1024;
+ prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
max_buffer_size = SIZE_MAX;
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 48f71bb81a2f..4d41ad302802 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -879,12 +879,10 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
/* clear the corresponding DAIs parameters when going to be inactive */
for_each_rtd_dais(rtd, i, dai) {
- int active = snd_soc_dai_stream_active(dai, substream->stream);
-
if (snd_soc_dai_active(dai) == 1)
soc_pcm_set_dai_params(dai, NULL);
- if (active == 1)
+ if (snd_soc_dai_stream_active(dai, substream->stream) == 1)
snd_soc_dai_digital_mute(dai, 1, substream->stream);
}
@@ -898,12 +896,9 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
snd_soc_pcm_component_hw_free(substream, rollback);
/* now free hw params for the DAIs */
- for_each_rtd_dais(rtd, i, dai) {
- if (!snd_soc_dai_stream_valid(dai, substream->stream))
- continue;
-
- snd_soc_dai_hw_free(dai, substream, rollback);
- }
+ for_each_rtd_dais(rtd, i, dai)
+ if (snd_soc_dai_stream_valid(dai, substream->stream))
+ snd_soc_dai_hw_free(dai, substream, rollback);
mutex_unlock(&rtd->card->pcm_mutex);
return 0;
@@ -1262,8 +1257,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0;
}
-static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
- enum snd_soc_dapm_direction dir)
+bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{
struct snd_soc_card *card = widget->dapm->card;
struct snd_soc_pcm_runtime *rtd;
@@ -1281,6 +1275,7 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
return false;
}
+EXPORT_SYMBOL_GPL(dpcm_end_walk_at_be);
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list)
@@ -1395,6 +1390,16 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
if (!fe->dpcm[stream].runtime && !fe->fe_compr)
continue;
+ /*
+ * Filter for systems with 'component_chaining' enabled.
+ * This helps to avoid unnecessary re-configuration of an
+ * already active BE on such systems.
+ */
+ if (fe->card->component_chaining &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
+ continue;
+
/* newly connected FE and BE */
err = dpcm_be_connect(fe, be, stream);
if (err < 0) {
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index f6e5ac3e0314..557e22c5254c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -78,7 +78,7 @@ struct soc_tplg {
};
static int soc_tplg_process_headers(struct soc_tplg *tplg);
-static void soc_tplg_complete(struct soc_tplg *tplg);
+static int soc_tplg_complete(struct soc_tplg *tplg);
/* check we dont overflow the data for this control chunk */
static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
@@ -312,10 +312,12 @@ static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
}
/* tell the component driver that all firmware has been loaded in this request */
-static void soc_tplg_complete(struct soc_tplg *tplg)
+static int soc_tplg_complete(struct soc_tplg *tplg)
{
if (tplg->ops && tplg->ops->complete)
- tplg->ops->complete(tplg->comp);
+ return tplg->ops->complete(tplg->comp);
+
+ return 0;
}
/* add a dynamic kcontrol */
@@ -349,7 +351,7 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
struct snd_soc_component *comp = tplg->comp;
return soc_tplg_add_dcontrol(comp->card->snd_card,
- comp->dev, k, comp->name_prefix, comp, kcontrol);
+ tplg->dev, k, comp->name_prefix, comp, kcontrol);
}
/* remove a mixer kcontrol */
@@ -1473,10 +1475,6 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
goto widget;
}
- control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
- dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n",
- w->name, w->num_kcontrols, control_hdr->type);
-
template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
kc = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(*kc), GFP_KERNEL);
if (!kc)
@@ -1487,7 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
if (!kcontrol_type)
goto err;
- for (i = 0; i < w->num_kcontrols; i++) {
+ for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) {
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
switch (le32_to_cpu(control_hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
@@ -1536,6 +1534,8 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
}
template.kcontrol_news = kc;
+ dev_dbg(tplg->dev, "ASoC: template %s with %d/%d/%d (mixer/enum/bytes) control\n",
+ w->name, mixer_count, enum_count, bytes_count);
widget:
ret = soc_tplg_widget_load(tplg, &template, w);
@@ -1591,11 +1591,28 @@ static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_dapm_widget *widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
int ret;
+ /*
+ * check if widget itself fits within topology file
+ * use sizeof instead of widget->size, as we can't be sure
+ * it is set properly yet (file may end before it is present)
+ */
+ if (soc_tplg_get_offset(tplg) + sizeof(*widget) >= tplg->fw->size) {
+ dev_err(tplg->dev, "ASoC: invalid widget data size\n");
+ return -EINVAL;
+ }
+
+ /* check if widget has proper size */
if (le32_to_cpu(widget->size) != sizeof(*widget)) {
dev_err(tplg->dev, "ASoC: invalid widget size\n");
return -EINVAL;
}
+ /* check if widget private data fits within topology file */
+ if (soc_tplg_get_offset(tplg) + le32_to_cpu(widget->priv.size) >= tplg->fw->size) {
+ dev_err(tplg->dev, "ASoC: invalid widget private data size\n");
+ return -EINVAL;
+ }
+
ret = soc_tplg_dapm_widget_create(tplg, widget);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to load widget %s\n",
@@ -2438,6 +2455,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
_manifest = manifest;
} else {
abi_match = false;
+
ret = manifest_new_ver(tplg, manifest, &_manifest);
if (ret < 0)
return ret;
@@ -2468,6 +2486,14 @@ static int soc_valid_header(struct soc_tplg *tplg,
return -EINVAL;
}
+ if (soc_tplg_get_hdr_offset(tplg) + hdr->payload_size >= tplg->fw->size) {
+ dev_err(tplg->dev,
+ "ASoC: invalid header of type %d at offset %ld payload_size %d\n",
+ le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg),
+ hdr->payload_size);
+ return -EINVAL;
+ }
+
/* big endian firmware objects not supported atm */
if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) {
dev_err(tplg->dev,
@@ -2627,7 +2653,7 @@ static int soc_tplg_load(struct soc_tplg *tplg)
ret = soc_tplg_process_headers(tplg);
if (ret == 0)
- soc_tplg_complete(tplg);
+ return soc_tplg_complete(tplg);
return ret;
}
@@ -2642,17 +2668,17 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp,
/*
* check if we have sane parameters:
* comp - needs to exist to keep and reference data while parsing
- * comp->dev - used for resource management and prints
* comp->card - used for setting card related parameters
+ * comp->card->dev - used for resource management and prints
* fw - we need it, as it is the very thing we parse
*/
- if (!comp || !comp->dev || !comp->card || !fw)
+ if (!comp || !comp->card || !comp->card->dev || !fw)
return -EINVAL;
/* setup parsing context */
memset(&tplg, 0, sizeof(tplg));
tplg.fw = fw;
- tplg.dev = comp->dev;
+ tplg.dev = comp->card->dev;
tplg.comp = comp;
if (ops) {
tplg.ops = ops;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 299b5d6ebfd1..a4efe7e52a8b 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -63,10 +63,23 @@ static const struct snd_pcm_hardware dummy_dma_hardware = {
.periods_max = 128,
};
+
+static const struct snd_soc_component_driver dummy_platform;
+
static int dummy_dma_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int i;
+
+ /*
+ * If there are other components associated with rtd, we shouldn't
+ * override their hwparams
+ */
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver == &dummy_platform)
+ return 0;
+ }
/* BE's dont need dummy params */
if (!rtd->dai_link->no_pcm)
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index cd659493b5df..6bb4db87af03 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_SOC_SOF_TOPLEVEL
+menuconfig SND_SOC_SOF_TOPLEVEL
bool "Sound Open Firmware Support"
help
This adds support for Sound Open Firmware (SOF). SOF is free and
@@ -46,6 +46,10 @@ config SND_SOC_SOF_OF
required to enable i.MX8 devices.
Say Y if you need this option. If unsure select "N".
+config SND_SOC_SOF_COMPRESS
+ tristate
+ select SND_SOC_COMPRESS
+
config SND_SOC_SOF_DEBUG_PROBES
bool "SOF enable data probing"
select SND_SOC_COMPRESS
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 606d8137cd98..06e5f49f7ee8 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -1,8 +1,10 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
- control.o trace.o utils.o sof-audio.o
-snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o compress.o
+ control.o trace.o utils.o sof-audio.o stream-ipc.o
+
+snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o
+snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o
snd-sof-pci-objs := sof-pci-dev.o
snd-sof-acpi-objs := sof-acpi-dev.o
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 57d5bf0a171e..01ca85f0b87f 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -1,147 +1,51 @@
// 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) 2019-2020 Intel Corporation. All rights reserved.
-//
-// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+// Copyright 2021 NXP
//
+// Author: Daniel Baluta <daniel.baluta@nxp.com>
#include <sound/soc.h>
-#include "compress.h"
-#include "ops.h"
-#include "probe.h"
-
-const struct snd_compress_ops sof_probe_compressed_ops = {
- .copy = sof_probe_compr_copy,
-};
-EXPORT_SYMBOL(sof_probe_compressed_ops);
-
-int sof_probe_compr_open(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
- int ret;
-
- ret = snd_sof_probe_compr_assign(sdev, cstream, dai);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
- return ret;
- }
+#include <sound/sof.h>
+#include <sound/compress_driver.h>
+#include "sof-audio.h"
+#include "sof-priv.h"
- sdev->extractor_stream_tag = ret;
- return 0;
-}
-EXPORT_SYMBOL(sof_probe_compr_open);
-
-int sof_probe_compr_free(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
+static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
{
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
- struct sof_probe_point_desc *desc;
- size_t num_desc;
- int i, ret;
-
- /* disconnect all probe points */
- ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
- goto exit;
- }
-
- for (i = 0; i < num_desc; i++)
- sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1);
- kfree(desc);
-
-exit:
- ret = sof_ipc_probe_deinit(sdev);
- if (ret < 0)
- dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
-
- sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
- snd_compr_free_pages(cstream);
+ struct snd_sof_pcm_stream *sps =
+ container_of(work, struct snd_sof_pcm_stream,
+ period_elapsed_work);
- return snd_sof_probe_compr_free(sdev, cstream, dai);
+ snd_compr_fragment_elapsed(sps->cstream);
}
-EXPORT_SYMBOL(sof_probe_compr_free);
-int sof_probe_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params, struct snd_soc_dai *dai)
+void snd_sof_compr_init_elapsed_work(struct work_struct *work)
{
- struct snd_compr_runtime *rtd = cstream->runtime;
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
- int ret;
-
- cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
- cstream->dma_buffer.dev.dev = sdev->dev;
- ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
- if (ret < 0)
- return ret;
-
- ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai);
- if (ret < 0)
- return ret;
-
- ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag,
- rtd->dma_bytes);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to init probe: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(sof_probe_compr_set_params);
-
-int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
-
- return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai);
-}
-EXPORT_SYMBOL(sof_probe_compr_trigger);
-
-int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
-
- return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai);
+ INIT_WORK(work, snd_sof_compr_fragment_elapsed_work);
}
-EXPORT_SYMBOL(sof_probe_compr_pointer);
-int sof_probe_compr_copy(struct snd_soc_component *component,
- struct snd_compr_stream *cstream,
- char __user *buf, size_t count)
+/*
+ * sof compr fragment elapse, this could be called in irq thread context
+ */
+void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
{
- struct snd_compr_runtime *rtd = cstream->runtime;
- unsigned int offset, n;
- void *ptr;
- int ret;
+ struct snd_soc_component *component;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_sof_pcm *spcm;
- if (count > rtd->buffer_size)
- count = rtd->buffer_size;
+ if (!cstream)
+ return;
- div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
- ptr = rtd->dma_area + offset;
- n = rtd->buffer_size - offset;
+ rtd = cstream->private_data;
+ component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
- if (count < n) {
- ret = copy_to_user(buf, ptr, count);
- } else {
- ret = copy_to_user(buf, ptr, n);
- ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm) {
+ dev_err(component->dev,
+ "fragment elapsed called for unknown stream!\n");
+ return;
}
- if (ret)
- return count - ret;
- return count;
+ /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
+ schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);
}
-EXPORT_SYMBOL(sof_probe_compr_copy);
diff --git a/sound/soc/sof/compress.h b/sound/soc/sof/compress.h
deleted file mode 100644
index 4448c799e14b..000000000000
--- a/sound/soc/sof/compress.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 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) 2019-2020 Intel Corporation. All rights reserved.
- *
- * Author: Cezary Rojewski <cezary.rojewski@intel.com>
- */
-
-#ifndef __SOF_COMPRESS_H
-#define __SOF_COMPRESS_H
-
-#include <sound/compress_driver.h>
-
-extern const struct snd_compress_ops sof_probe_compressed_ops;
-
-int sof_probe_compr_open(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int sof_probe_compr_free(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int sof_probe_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params, struct snd_soc_dai *dai);
-int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai);
-int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai);
-int sof_probe_compr_copy(struct snd_soc_component *component,
- struct snd_compr_stream *cstream,
- char __user *buf, size_t count);
-
-#endif
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index a5dd728c580a..58bb89af4de1 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -65,6 +65,40 @@ static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
return i - 1;
}
+static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ enum sof_ipc_ctrl_type ctrl_type;
+ int ret;
+
+ if (!scontrol->comp_data_dirty)
+ return;
+
+ if (!pm_runtime_active(scomp->dev))
+ return;
+
+ if (scontrol->cmd == SOF_CTRL_CMD_BINARY)
+ ctrl_type = SOF_IPC_COMP_GET_DATA;
+ else
+ ctrl_type = SOF_IPC_COMP_GET_VALUE;
+
+ /* set the ABI header values */
+ cdata->data->magic = SOF_ABI_MAGIC;
+ cdata->data->abi = SOF_ABI_VERSION;
+
+ /* refresh the component data from DSP */
+ scontrol->comp_data_dirty = false;
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ scontrol->cmd, false);
+ if (ret < 0) {
+ dev_err(scomp->dev, "error: failed to get control data: %d\n", ret);
+ /* Set the flag to re-try next time to get the data */
+ scontrol->comp_data_dirty = true;
+ }
+}
+
int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -74,6 +108,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
+ snd_sof_refresh_control(scontrol);
+
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.integer.value[i] =
@@ -108,7 +144,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
if (pm_runtime_active(scomp->dev))
snd_sof_ipc_set_get_comp_data(scontrol,
SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_TYPE_VALUE_CHAN_SET,
SOF_CTRL_CMD_VOLUME,
true);
return change;
@@ -145,6 +181,8 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
+ snd_sof_refresh_control(scontrol);
+
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.integer.value[i] = cdata->chanv[i].value;
@@ -179,7 +217,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
if (pm_runtime_active(scomp->dev))
snd_sof_ipc_set_get_comp_data(scontrol,
SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_TYPE_VALUE_CHAN_SET,
SOF_CTRL_CMD_SWITCH,
true);
@@ -195,6 +233,8 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
+ snd_sof_refresh_control(scontrol);
+
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
@@ -226,7 +266,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
if (pm_runtime_active(scomp->dev))
snd_sof_ipc_set_get_comp_data(scontrol,
SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_TYPE_VALUE_CHAN_SET,
SOF_CTRL_CMD_ENUM,
true);
@@ -244,6 +284,8 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct sof_abi_hdr *data = cdata->data;
size_t size;
+ snd_sof_refresh_control(scontrol);
+
if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(scomp->dev,
"error: data max %d exceeds ucontrol data array size\n",
@@ -475,6 +517,8 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
(struct snd_ctl_tlv __user *)binary_data;
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.
@@ -511,3 +555,145 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
return 0;
}
+
+static void snd_sof_update_control(struct snd_sof_control *scontrol,
+ struct sof_ipc_ctrl_data *cdata)
+{
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc_ctrl_data *local_cdata;
+ int i;
+
+ local_cdata = scontrol->control_data;
+
+ if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
+ if (cdata->num_elems != local_cdata->data->size) {
+ dev_err(scomp->dev,
+ "error: cdata binary size mismatch %u - %u\n",
+ cdata->num_elems, local_cdata->data->size);
+ return;
+ }
+
+ /* copy the new binary data */
+ memcpy(local_cdata->data, cdata->data, cdata->num_elems);
+ } else if (cdata->num_elems != scontrol->num_channels) {
+ dev_err(scomp->dev,
+ "error: cdata channel count mismatch %u - %d\n",
+ cdata->num_elems, scontrol->num_channels);
+ } else {
+ /* copy the new values */
+ for (i = 0; i < cdata->num_elems; i++)
+ local_cdata->chanv[i].value = cdata->chanv[i].value;
+ }
+}
+
+void snd_sof_control_notify(struct snd_sof_dev *sdev,
+ struct sof_ipc_ctrl_data *cdata)
+{
+ struct snd_soc_dapm_widget *widget;
+ struct snd_sof_control *scontrol;
+ struct snd_sof_widget *swidget;
+ struct snd_kcontrol *kc = NULL;
+ struct soc_mixer_control *sm;
+ struct soc_bytes_ext *be;
+ size_t expected_size;
+ struct soc_enum *se;
+ bool found = false;
+ int i, type;
+
+ /* Find the swidget first */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == cdata->comp_id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ /* Translate SOF cmd to TPLG type */
+ switch (cdata->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_SWITCH:
+ type = SND_SOC_TPLG_TYPE_MIXER;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ type = SND_SOC_TPLG_TYPE_BYTES;
+ break;
+ case SOF_CTRL_CMD_ENUM:
+ type = SND_SOC_TPLG_TYPE_ENUM;
+ break;
+ default:
+ dev_err(sdev->dev, "error: unknown cmd %u\n", cdata->cmd);
+ return;
+ }
+
+ widget = swidget->widget;
+ for (i = 0; i < widget->num_kcontrols; i++) {
+ /* skip non matching types or non matching indexes within type */
+ if (widget->dobj.widget.kcontrol_type[i] == type &&
+ widget->kcontrol_news[i].index == cdata->index) {
+ kc = widget->kcontrols[i];
+ break;
+ }
+ }
+
+ if (!kc)
+ return;
+
+ switch (cdata->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_SWITCH:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ scontrol = sm->dobj.private;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ be = (struct soc_bytes_ext *)kc->private_value;
+ scontrol = be->dobj.private;
+ break;
+ case SOF_CTRL_CMD_ENUM:
+ se = (struct soc_enum *)kc->private_value;
+ scontrol = se->dobj.private;
+ break;
+ default:
+ return;
+ }
+
+ expected_size = sizeof(struct sof_ipc_ctrl_data);
+ switch (cdata->type) {
+ case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+ case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+ expected_size += cdata->num_elems *
+ sizeof(struct sof_ipc_ctrl_value_chan);
+ break;
+ case SOF_CTRL_TYPE_VALUE_COMP_GET:
+ case SOF_CTRL_TYPE_VALUE_COMP_SET:
+ expected_size += cdata->num_elems *
+ sizeof(struct sof_ipc_ctrl_value_comp);
+ break;
+ case SOF_CTRL_TYPE_DATA_GET:
+ case SOF_CTRL_TYPE_DATA_SET:
+ expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
+ break;
+ default:
+ return;
+ }
+
+ if (cdata->rhdr.hdr.size != expected_size) {
+ dev_err(sdev->dev, "error: component notification size mismatch\n");
+ return;
+ }
+
+ if (cdata->num_elems)
+ /*
+ * The message includes the updated value/data, update the
+ * control's local cache using the received notification
+ */
+ snd_sof_update_control(scontrol, cdata);
+ else
+ /* Mark the scontrol that the value/data is changed in SOF */
+ scontrol->comp_data_dirty = true;
+
+ snd_ctl_notify_one(swidget->scomp->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
+}
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 59d0d7b2b55c..2c3de295f11f 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -15,11 +15,11 @@
#include "sof-priv.h"
#include "ops.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "probe.h"
+#include "sof-probes.h"
#endif
/* see SOF_DBG_ flags */
-int sof_core_debug;
+int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
module_param_named(sof_debug, sof_core_debug, int, 0444);
MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
@@ -67,7 +67,7 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
/* is firmware dead ? */
if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
- dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n",
+ dev_err(sdev->dev, "unexpected fault %#010x trace %#010x\n",
panic_code, tracep_code);
return; /* no fault ? */
}
@@ -76,20 +76,20 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
for (i = 0; i < ARRAY_SIZE(panic_msg); i++) {
if (panic_msg[i].id == code) {
- dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg);
- dev_err(sdev->dev, "error: trace point %8.8x\n",
- tracep_code);
+ dev_err(sdev->dev, "reason: %s (%#x)\n", panic_msg[i].msg,
+ code & SOF_IPC_PANIC_CODE_MASK);
+ dev_err(sdev->dev, "trace point: %#010x\n", tracep_code);
goto out;
}
}
/* unknown error */
- dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code);
- dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code);
+ dev_err(sdev->dev, "unknown panic code: %#x\n", code & SOF_IPC_PANIC_CODE_MASK);
+ dev_err(sdev->dev, "trace point: %#010x\n", tracep_code);
out:
- dev_err(sdev->dev, "error: panic at %s:%d\n",
- panic_info->filename, panic_info->linenum);
+ dev_err(sdev->dev, "panic at %s:%d\n", panic_info->filename,
+ panic_info->linenum);
sof_oops(sdev, oops);
sof_stack(sdev, oops, stack, stack_words);
}
@@ -147,7 +147,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return ret;
}
- sdev->fw_state = SOF_FW_BOOT_PREPARE;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* check machine info */
ret = sof_machine_check(sdev);
@@ -189,7 +189,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_load_err;
}
- sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
/*
* Boot the firmware. The FW boot status will be modified
@@ -202,8 +202,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_run_err;
}
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) ||
- (sof_core_debug & SOF_DBG_ENABLE_TRACE)) {
+ if (sof_core_debug & SOF_DBG_ENABLE_TRACE) {
sdev->dtrace_is_supported = true;
/* init DMA trace */
@@ -266,7 +265,7 @@ dsp_err:
snd_sof_remove(sdev);
/* all resources freed, update state to match */
- sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
sdev->first_boot = true;
return ret;
@@ -301,7 +300,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->pdata = plat_data;
sdev->first_boot = true;
- sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
#endif
@@ -326,9 +325,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
spin_lock_init(&sdev->hw_lock);
mutex_init(&sdev->power_state_access);
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
- INIT_WORK(&sdev->probe_work, sof_probe_work);
-
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
@@ -340,6 +336,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->boot_timeout = plat_data->desc->boot_timeout;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
+ INIT_WORK(&sdev->probe_work, sof_probe_work);
schedule_work(&sdev->probe_work);
return 0;
}
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index a51a928ea40a..dc1df5fb7b4c 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -20,7 +20,7 @@
#include "ops.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "probe.h"
+#include "sof-probes.h"
/**
* strsplit_u32 - Split string into sequence of u32 tokens
@@ -546,10 +546,10 @@ static const struct file_operations sof_dfs_fops = {
};
/* create FS entry for debug files that can expose DSP memories, registers */
-int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
- void __iomem *base, size_t size,
- const char *name,
- enum sof_debugfs_access_type access_type)
+static int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
+ void __iomem *base, size_t size,
+ const char *name,
+ enum sof_debugfs_access_type access_type)
{
struct snd_sof_dfsentry *dfse;
@@ -586,7 +586,21 @@ int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
return 0;
}
-EXPORT_SYMBOL_GPL(snd_sof_debugfs_io_item);
+
+int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type, u32 offset,
+ size_t size, const char *name,
+ enum sof_debugfs_access_type access_type)
+{
+ int bar = snd_sof_dsp_get_bar_index(sdev, blk_type);
+
+ if (bar < 0)
+ return bar;
+
+ return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name,
+ access_type);
+}
+EXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem);
/* create FS entry for debug files to expose kernel memory */
int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
@@ -808,18 +822,75 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_GPL(snd_sof_free_debug);
+static const struct soc_fw_state_info {
+ enum snd_sof_fw_state state;
+ const char *name;
+} fw_state_dbg[] = {
+ {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
+ {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"},
+ {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"},
+ {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"},
+};
+
+static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) {
+ if (sdev->fw_state == fw_state_dbg[i].state) {
+ dev_err(sdev->dev, "fw_state: %s (%d)\n", fw_state_dbg[i].name, i);
+ return;
+ }
+ }
+
+ dev_err(sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state);
+}
+
+void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ bool print_all = !!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS);
+
+ if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all)
+ return;
+
+ if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) {
+ dev_err(sdev->dev, "------------[ DSP dump start ]------------\n");
+ snd_sof_dbg_print_fw_state(sdev);
+ sof_ops(sdev)->dbg_dump(sdev, flags);
+ dev_err(sdev->dev, "------------[ DSP dump end ]------------\n");
+ if (!print_all)
+ sdev->dbg_dump_printed = true;
+ }
+}
+EXPORT_SYMBOL(snd_sof_dsp_dbg_dump);
+
+static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) {
+ dev_err(sdev->dev, "------------[ IPC dump start ]------------\n");
+ sof_ops(sdev)->ipc_dump(sdev);
+ dev_err(sdev->dev, "------------[ IPC dump end ]------------\n");
+ if (!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS))
+ sdev->ipc_dump_printed = true;
+ }
+}
+
void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev)
{
if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
(sof_core_debug & SOF_DBG_RETAIN_CTX)) {
/* should we prevent DSP entering D3 ? */
- dev_info(sdev->dev, "info: preventing DSP entering D3 state to preserve context\n");
+ if (!sdev->ipc_dump_printed)
+ dev_info(sdev->dev,
+ "preventing DSP entering D3 state to preserve context\n");
pm_runtime_get_noresume(sdev->dev);
}
/* dump vital information to the logs */
- snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
snd_sof_ipc_dump(sdev);
+ snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
snd_sof_trace_notify_for_error(sdev);
}
EXPORT_SYMBOL(snd_sof_handle_fw_exception);
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index 49d605cb09a5..34cf228c188f 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -38,6 +38,7 @@ config SND_SOC_SOF_IMX8
tristate
select SND_SOC_SOF_IMX_COMMON
select SND_SOC_SOF_XTENSA
+ select SND_SOC_SOF_COMPRESS
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
@@ -54,6 +55,7 @@ config SND_SOC_SOF_IMX8M
tristate
select SND_SOC_SOF_IMX_COMMON
select SND_SOC_SOF_XTENSA
+ select SND_SOC_SOF_COMPRESS
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
diff --git a/sound/soc/sof/imx/imx-ops.h b/sound/soc/sof/imx/imx-ops.h
new file mode 100644
index 000000000000..24235ef8c8fa
--- /dev/null
+++ b/sound/soc/sof/imx/imx-ops.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef __IMX_OPS_H__
+#define __IMX_OPS_H__
+
+extern struct snd_sof_dsp_ops sof_imx8_ops;
+extern struct snd_sof_dsp_ops sof_imx8x_ops;
+extern struct snd_sof_dsp_ops sof_imx8m_ops;
+
+#endif
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 7e9723a10d02..dd59a74480d6 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -22,6 +22,7 @@
#include <dt-bindings/firmware/imx/rsrc.h>
#include "../ops.h"
#include "imx-common.h"
+#include "imx-ops.h"
/* DSP memories */
#define IRAM_OFFSET 0x10000
@@ -375,20 +376,6 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
}
}
-static void imx8_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
-{
- sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
-}
-
-static int imx8_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
-{
- return 0;
-}
-
static struct snd_soc_dai_driver imx8_dai[] = {
{
.name = "esai0",
@@ -426,8 +413,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
- /* Module IO */
- .read64 = sof_io_read64,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
/* ipc */
.send_msg = imx8_send_msg,
@@ -435,8 +423,8 @@ struct snd_sof_dsp_ops sof_imx8_ops = {
.get_mailbox_offset = imx8_get_mailbox_offset,
.get_window_offset = imx8_get_window_offset,
- .ipc_msg_data = imx8_ipc_msg_data,
- .ipc_pcm_params = imx8_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -446,9 +434,14 @@ struct snd_sof_dsp_ops sof_imx8_ops = {
/* Debug information */
.dbg_dump = imx8_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* Firmware ops */
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
/* DAI drivers */
.drv = imx8_dai,
@@ -475,8 +468,9 @@ struct snd_sof_dsp_ops sof_imx8x_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
- /* Module IO */
- .read64 = sof_io_read64,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
/* ipc */
.send_msg = imx8_send_msg,
@@ -484,8 +478,8 @@ struct snd_sof_dsp_ops sof_imx8x_ops = {
.get_mailbox_offset = imx8_get_mailbox_offset,
.get_window_offset = imx8_get_window_offset,
- .ipc_msg_data = imx8_ipc_msg_data,
- .ipc_pcm_params = imx8_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -495,9 +489,14 @@ struct snd_sof_dsp_ops sof_imx8x_ops = {
/* Debug information */
.dbg_dump = imx8_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* Firmware ops */
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
/* DAI drivers */
.drv = imx8_dai,
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index 892e1482f97f..e4618980cf8b 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -18,6 +18,7 @@
#include "../ops.h"
#include "imx-common.h"
+#include "imx-ops.h"
#define MBOX_OFFSET 0x800000
#define MBOX_SIZE 0x1000
@@ -238,22 +239,19 @@ static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type)
}
}
-static void imx8m_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
-{
- sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
-}
-
-static int imx8m_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
-{
- return 0;
-}
-
static struct snd_soc_dai_driver imx8m_dai[] = {
{
+ .name = "sai1",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+},
+{
.name = "sai3",
.playback = {
.channels_min = 1,
@@ -278,8 +276,9 @@ struct snd_sof_dsp_ops sof_imx8m_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
- /* Module IO */
- .read64 = sof_io_read64,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
/* ipc */
.send_msg = imx8m_send_msg,
@@ -287,8 +286,8 @@ struct snd_sof_dsp_ops sof_imx8m_ops = {
.get_mailbox_offset = imx8m_get_mailbox_offset,
.get_window_offset = imx8m_get_window_offset,
- .ipc_msg_data = imx8m_ipc_msg_data,
- .ipc_pcm_params = imx8m_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -298,9 +297,13 @@ struct snd_sof_dsp_ops sof_imx8m_ops = {
/* Debug information */
.dbg_dump = imx8_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* Firmware ops */
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
/* DAI drivers */
.drv = imx8m_dai,
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index feae487f0227..1f473d4d8416 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -3,13 +3,11 @@
snd-sof-acpi-intel-byt-objs := byt.o
snd-sof-acpi-intel-bdw-objs := bdw.o
-snd-sof-intel-ipc-objs := intel-ipc.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 \
apl.o cnl.o tgl.o icl.o
-snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o
+snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
snd-sof-intel-hda-objs := hda-codec.o
@@ -18,7 +16,6 @@ snd-sof-intel-atom-objs := atom.o
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_INTEL_HIFI_EP_IPC) += snd-sof-intel-ipc.o
obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index c7ed2b3d6abc..917f78cf6daf 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -42,6 +42,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_thread = hda_dsp_ipc_irq_thread,
@@ -65,6 +69,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.debug_map_count = ARRAY_SIZE(apl_dsp_debugfs),
.dbg_dump = hda_dsp_dump,
.ipc_dump = hda_ipc_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
.pcm_open = hda_dsp_pcm_open,
@@ -125,7 +130,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
index d8804efede5e..74c630bb9847 100644
--- a/sound/soc/sof/intel/atom.c
+++ b/sound/soc/sof/intel/atom.c
@@ -283,11 +283,8 @@ int atom_run(struct snd_sof_dev *sdev)
break;
msleep(100);
}
- if (tries < 0) {
- dev_err(sdev->dev, "error: unable to run DSP firmware\n");
- atom_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
+ if (tries < 0)
return -ENODEV;
- }
/* return init core mask */
return 1;
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 89a6c1f04a55..2c09a523288e 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -535,8 +535,8 @@ static int bdw_probe(struct snd_sof_dev *sdev)
return ret;
}
- /* set default mailbox */
- snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0);
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
return ret;
}
@@ -616,14 +616,18 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* ipc */
.send_msg = bdw_send_msg,
.fw_ready = sof_fw_ready,
.get_mailbox_offset = bdw_get_mailbox_offset,
.get_window_offset = bdw_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* machine driver */
.machine_select = bdw_machine_select,
@@ -635,10 +639,11 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
.debug_map = bdw_debugfs,
.debug_map_count = ARRAY_SIZE(bdw_debugfs),
.dbg_dump = bdw_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* Module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -657,7 +662,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
static const struct sof_intel_dsp_desc bdw_chip_info = {
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 8edaf6fdd218..e2fa08f1ae74 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -226,6 +226,10 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_handler = atom_irq_handler,
.irq_thread = atom_irq_thread,
@@ -236,8 +240,8 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* machine driver */
.machine_select = atom_machine_select,
@@ -249,10 +253,11 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.debug_map = byt_debugfs,
.debug_map_count = ARRAY_SIZE(byt_debugfs),
.dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -275,7 +280,7 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
static const struct sof_intel_dsp_desc byt_chip_info = {
@@ -303,6 +308,10 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_handler = atom_irq_handler,
.irq_thread = atom_irq_thread,
@@ -313,8 +322,8 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* machine driver */
.machine_select = atom_machine_select,
@@ -326,10 +335,11 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.debug_map = cht_debugfs,
.debug_map_count = ARRAY_SIZE(cht_debugfs),
.dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -353,7 +363,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
static const struct sof_intel_dsp_desc cht_chip_info = {
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index e115e12a856f..3957e2b3db32 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -247,6 +247,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_thread = cnl_ipc_irq_thread,
@@ -270,6 +274,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs),
.dbg_dump = hda_dsp_dump,
.ipc_dump = cnl_ipc_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
.pcm_open = hda_dsp_pcm_open,
@@ -330,7 +335,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index c1f9f0f58464..76579383d290 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -16,6 +16,10 @@
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
+#include "../sof-probes.h"
+#endif
+
struct hda_pipe_params {
u8 host_dma_id;
u8 link_dma_id;
@@ -68,6 +72,7 @@ static struct hdac_ext_stream *
return NULL;
}
+ spin_lock_irq(&bus->reg_lock);
list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream =
stream_to_hdac_ext_stream(stream);
@@ -107,12 +112,12 @@ static struct hdac_ext_stream *
* is updated in snd_hdac_ext_stream_decouple().
*/
if (!res->decoupled)
- snd_hdac_ext_stream_decouple(bus, res, true);
- spin_lock_irq(&bus->reg_lock);
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
res->link_locked = 1;
res->link_substream = substream;
- spin_unlock_irq(&bus->reg_lock);
}
+ spin_unlock_irq(&bus->reg_lock);
return res;
}
@@ -151,49 +156,68 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
return 0;
}
-/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
-static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
- const char *dai_name, int channel, int dir)
+/* Update config for the DAI widget */
+static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
+ int channel)
{
+ struct snd_sof_widget *swidget = w->dobj.private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret = 0;
- list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
- if (!sof_dai->cpu_dai_name)
- continue;
+ if (!swidget)
+ return NULL;
- if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
- dir == sof_dai->comp_dai.direction) {
- config = sof_dai->dai_config;
+ sof_dai = swidget->private;
- if (!config) {
- dev_err(hda_stream->sdev->dev,
- "error: no config for DAI %s\n",
- sof_dai->name);
- return -EINVAL;
- }
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
+ return NULL;
+ }
- /* update config with stream tag */
- config->hda.link_dma_ch = channel;
+ config = &sof_dai->dai_config[sof_dai->current_config];
- /* send IPC */
- ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
- config->hdr.cmd,
- config,
- config->hdr.size,
- &reply, sizeof(reply));
+ /* update config with stream tag */
+ config->hda.link_dma_ch = channel;
- if (ret < 0)
- dev_err(hda_stream->sdev->dev,
- "error: failed to set dai config for %s\n",
- sof_dai->name);
- return ret;
- }
+ return config;
+}
+
+static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
+ struct snd_soc_dapm_widget *w, int channel)
+{
+ struct snd_sof_dev *sdev = hda_stream->sdev;
+ struct sof_ipc_dai_config *config;
+ struct sof_ipc_reply reply;
+
+ config = hda_dai_update_config(w, channel);
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
+ return -ENOENT;
}
- return -EINVAL;
+ /* send DAI_CONFIG IPC */
+ return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+}
+
+static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
+ struct snd_soc_dapm_widget *w,
+ int channel, bool widget_setup)
+{
+ struct snd_sof_dev *sdev = hda_stream->sdev;
+ struct sof_ipc_dai_config *config;
+
+ config = hda_dai_update_config(w, channel);
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
+ return -ENOENT;
+ }
+
+ /* set up/free DAI widget and send DAI_CONFIG IPC */
+ if (widget_setup)
+ return hda_ctrl_dai_widget_setup(w);
+
+ return hda_ctrl_dai_widget_free(w);
}
static int hda_link_hw_params(struct snd_pcm_substream *substream,
@@ -207,6 +231,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct sof_intel_hda_stream *hda_stream;
struct hda_pipe_params p_params = {0};
+ struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
int stream_tag;
int ret;
@@ -225,9 +250,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
- /* update the DSP with the new tag */
- ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
- substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ /* set up the DAI widget and send the DAI_CONFIG with the new tag */
+ ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
if (ret < 0)
return ret;
@@ -283,6 +312,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
snd_soc_dai_get_dma_data(dai, substream);
struct sof_intel_hda_stream *hda_stream;
struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
struct hdac_stream *hstream;
struct hdac_bus *bus;
@@ -317,12 +347,16 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
/*
* clear link DMA channel. It will be assigned when
* hw_params is set up again after resume.
*/
- ret = hda_link_config_ipc(hda_stream, dai->name,
- DMA_CHAN_INVALID, substream->stream);
+ ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID);
if (ret < 0)
return ret;
@@ -353,6 +387,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct hdac_stream *hstream;
struct snd_soc_pcm_runtime *rtd;
struct hdac_ext_stream *link_dev;
+ struct snd_soc_dapm_widget *w;
int ret;
hstream = substream->runtime->private_data;
@@ -368,9 +403,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
- /* free the link DMA channel in the FW */
- ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
- substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ /* free the link DMA channel in the FW and the DAI widget */
+ ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
if (ret < 0)
return ret;
@@ -400,61 +439,131 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
.prepare = hda_link_pcm_prepare,
};
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-#include "../compress.h"
-
-static struct snd_soc_cdai_ops sof_probe_compr_ops = {
- .startup = sof_probe_compr_open,
- .shutdown = sof_probe_compr_free,
- .set_params = sof_probe_compr_set_params,
- .trigger = sof_probe_compr_trigger,
- .pointer = sof_probe_compr_pointer,
+#endif
+
+/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
+struct ssp_dai_dma_data {
+ bool setup;
};
-#endif
-#endif
+static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ bool setup)
+{
+ struct snd_soc_component *component;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ struct sof_ipc_fw_version *v;
+ struct snd_sof_dev *sdev;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ swidget = w->dobj.private;
+ component = swidget->scomp;
+ sdev = snd_soc_component_get_drvdata(component);
+ v = &sdev->fw_ready.version;
+
+ /* DAI_CONFIG IPC during hw_params is not supported in older firmware */
+ if (v->abi_version < SOF_ABI_VER(3, 18, 0))
+ return 0;
+
+ if (setup)
+ return hda_ctrl_dai_widget_setup(w);
+
+ return hda_ctrl_dai_widget_free(w);
+}
+
+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)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret;
+ /* params are ignored for now */
+ return ssp_dai_setup(substream, dai, true);
+}
- /* DAI_CONFIG IPC during hw_params is not supported in older firmware */
- if (v->abi_version < SOF_ABI_VER(3, 18, 0))
+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 ssp_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
return 0;
- list_for_each_entry(sof_dai, &sdev->dai_list, list) {
- if (!sof_dai->cpu_dai_name || !sof_dai->dai_config)
- continue;
+ return ssp_dai_setup(substream, dai, false);
+}
- if (!strcmp(dai->name, sof_dai->cpu_dai_name) &&
- substream->stream == sof_dai->comp_dai.direction) {
- config = &sof_dai->dai_config[sof_dai->current_config];
+static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return ssp_dai_setup(substream, dai, false);
+}
- /* send IPC */
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config,
- config->hdr.size, &reply, sizeof(reply));
+static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ssp_dai_dma_data *dma_data;
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to set DAI config for %s\n",
- sof_dai->name);
- return ret;
- }
+ 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;
}
-
- return 0;
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(dma_data);
}
static const struct snd_soc_dai_ops ssp_dai_ops = {
+ .startup = ssp_dai_startup,
.hw_params = ssp_dai_hw_params,
+ .prepare = ssp_dai_prepare,
+ .trigger = ssp_dai_trigger,
+ .hw_free = ssp_dai_hw_free,
+ .shutdown = ssp_dai_shutdown,
};
/*
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 623cf291e207..058baca2cd0e 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -34,7 +34,7 @@ MODULE_PARM_DESC(enable_trace_D0I3_S0,
* DSP Core control.
*/
-int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
{
u32 adspcs;
u32 reset;
@@ -73,7 +73,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
{
unsigned int crst;
u32 adspcs;
@@ -113,7 +113,7 @@ int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask)
{
/* stall core */
snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
@@ -125,6 +125,31 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask)
return hda_dsp_core_reset_enter(sdev, core_mask);
}
+static bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask)
+{
+ int val;
+ bool is_enable;
+
+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
+
+#define MASK_IS_EQUAL(v, m, field) ({ \
+ u32 _m = field(m); \
+ ((v) & _m) == _m; \
+})
+
+ is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) &&
+ MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) &&
+ !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
+ !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
+
+#undef MASK_IS_EQUAL
+
+ dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
+ is_enable, core_mask);
+
+ return is_enable;
+}
+
int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
{
int ret;
@@ -156,7 +181,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
* Power Management.
*/
-int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
{
unsigned int cpa;
u32 adspcs;
@@ -195,7 +220,7 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
+static int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
{
u32 adspcs;
int ret;
@@ -218,32 +243,6 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
return ret;
}
-bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
- unsigned int core_mask)
-{
- int val;
- bool is_enable;
-
- val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
-
-#define MASK_IS_EQUAL(v, m, field) ({ \
- u32 _m = field(m); \
- ((v) & _m) == _m; \
-})
-
- is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) &&
- MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) &&
- !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
- !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
-
-#undef MASK_IS_EQUAL
-
- dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
- is_enable, core_mask);
-
- return is_enable;
-}
-
int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index acfeca42604c..11f20a5a62df 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -253,9 +253,9 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
return SRAM_WINDOW_OFFSET(id);
}
-void hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
+int hda_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
{
if (!substream || !sdev->stream_box.size) {
sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
@@ -268,10 +268,13 @@ void hda_ipc_msg_data(struct snd_sof_dev *sdev,
hda_stream.hstream);
/* The stream might already be closed */
- if (hstream)
- sof_mailbox_read(sdev, hda_stream->stream.posn_offset,
- p, sz);
+ if (!hstream)
+ return -ESTRPIPE;
+
+ sof_mailbox_read(sdev, hda_stream->stream.posn_offset, p, sz);
}
+
+ return 0;
}
int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 6f4771bf9de3..abad6d0ceb83 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -177,13 +177,13 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
__func__);
err:
- flags = SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX;
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
- /* force error log level after max boot attempts */
+ /* after max boot attempts make sure that the dump is printed */
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
- flags |= SOF_DBG_DUMP_FORCE_ERR_LEVEL;
+ flags &= ~SOF_DBG_DUMP_OPTIONAL;
- hda_dsp_dump(sdev, flags);
+ snd_sof_dsp_dbg_dump(sdev, flags);
snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
return ret;
@@ -414,8 +414,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
if (!ret) {
dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
} else {
- hda_dsp_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX |
- SOF_DBG_DUMP_FORCE_ERR_LEVEL);
+ snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX);
dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret);
}
diff --git a/sound/soc/sof/intel/hda-compress.c b/sound/soc/sof/intel/hda-probes.c
index fe2f3f7d236b..fe2f3f7d236b 100644
--- a/sound/soc/sof/intel/hda-compress.c
+++ b/sound/soc/sof/intel/hda-probes.c
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 63c367478f1c..1d845c2cbc33 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -25,6 +25,33 @@
#define HDA_LTRP_GB_VALUE_US 95
+static inline const char *hda_hstream_direction_str(struct hdac_stream *hstream)
+{
+ if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ return "Playback";
+ else
+ return "Capture";
+}
+
+static char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (hstream->substream)
+ rtd = asoc_substream_to_rtd(hstream->substream);
+ else if (hstream->cstream)
+ rtd = hstream->cstream->private_data;
+ else
+ /* Non audio DMA user, like dma-trace */
+ return kasprintf(GFP_KERNEL, "-- (%s, stream_tag: %u)",
+ hda_hstream_direction_str(hstream),
+ hstream->stream_tag);
+
+ return kasprintf(GFP_KERNEL, "dai_link \"%s\" (%s, stream_tag: %u)",
+ rtd->dai_link->name, hda_hstream_direction_str(hstream),
+ hstream->stream_tag);
+}
+
/*
* set up one of BDL entries for a stream
*/
@@ -89,13 +116,13 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
int remain, ioc;
period_bytes = stream->period_bytes;
- dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes);
+ dev_dbg(sdev->dev, "%s: period_bytes:0x%x\n", __func__, period_bytes);
if (!period_bytes)
period_bytes = stream->bufsize;
periods = stream->bufsize / period_bytes;
- dev_dbg(sdev->dev, "periods:%d\n", periods);
+ dev_dbg(sdev->dev, "%s: periods:%d\n", __func__, periods);
remain = stream->bufsize % period_bytes;
if (remain)
@@ -244,7 +271,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
if (!found) {
- dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag);
+ dev_dbg(sdev->dev, "%s: stream_tag %d not opened!\n",
+ __func__, stream_tag);
return -ENODEV;
}
@@ -257,7 +285,7 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
struct hdac_stream *hstream = &stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
- int ret;
+ int ret = 0;
u32 run;
/* cmd must be for audio stream */
@@ -283,14 +311,9 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
- __func__, cmd);
- return ret;
- }
+ if (ret >= 0)
+ hstream->running = true;
- hstream->running = true;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -306,27 +329,32 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_STREAM_RUN_TIMEOUT);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n",
- __func__, cmd);
- return ret;
- }
+ if (ret >= 0) {
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ SOF_HDA_CL_DMA_SD_INT_MASK);
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_STS,
- SOF_HDA_CL_DMA_SD_INT_MASK);
-
- hstream->running = false;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
- 1 << hstream->index, 0x0);
+ hstream->running = false;
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ SOF_HDA_INTCTL,
+ 1 << hstream->index, 0x0);
+ }
break;
default:
dev_err(sdev->dev, "error: unknown command: %d\n", cmd);
return -EINVAL;
}
- return 0;
+ if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
+ dev_err(sdev->dev,
+ "%s: cmd %d on %s: timeout on STREAM_SD_OFFSET read\n",
+ __func__, cmd, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
+ }
+
+ return ret;
}
/* minimal recommended programming for ICCMAX stream */
@@ -440,9 +468,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_STREAM_RUN_TIMEOUT);
if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
dev_err(sdev->dev,
- "error: %s: timeout on STREAM_SD_OFFSET read1\n",
- __func__);
+ "%s: on %s: timeout on STREAM_SD_OFFSET read1\n",
+ __func__, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
return ret;
}
@@ -506,9 +537,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
HDA_DSP_STREAM_RUN_TIMEOUT);
if (ret < 0) {
+ char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream);
+
dev_err(sdev->dev,
- "error: %s: timeout on STREAM_SD_OFFSET read2\n",
- __func__);
+ "%s: on %s: timeout on STREAM_SD_OFFSET read1\n",
+ __func__, stream_name ? stream_name : "unknown stream");
+ kfree(stream_name);
return ret;
}
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index f60e2c57d3d0..883d78dd01b5 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -41,6 +41,92 @@
#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)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "No config for DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ /*
+ * For static pipelines, the DAI widget would already be set up and calling
+ * sof_widget_setup() simply returns without doing anything.
+ * For dynamic pipelines, the DAI widget will be set up now.
+ */
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name);
+ return ret;
+ }
+
+ /* set HW_PARAMS flag */
+ config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS);
+
+ /* send DAI_CONFIG IPC */
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name);
+ return ret;
+ }
+
+ sof_dai->configured = true;
+
+ return 0;
+}
+
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ /* nothing to do if hw_free() is called without restarting the stream after resume. */
+ if (!sof_dai->configured)
+ return 0;
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ /* set HW_FREE flag */
+ config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE);
+
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name);
+
+ /*
+ * Reset the configured_flag and free the widget even if the IPC fails to keep
+ * the widget use_count balanced
+ */
+ sof_dai->configured = false;
+
+ return sof_widget_free(sdev, swidget);
+}
+
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
{
@@ -64,67 +150,70 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
module_param(sdw_clock_stop_quirks, int, 0444);
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
+static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget *w,
+ int link_id, int alh_stream_id, int dai_id, bool setup)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+
+ if (!swidget) {
+ dev_err(sdev->dev, "error: No private data for widget %s\n", w->name);
+ return -EINVAL;
+ }
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "error: No config for DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ /* update config with link and stream ID */
+ config->dai_index = (link_id << 8) | dai_id;
+ config->alh.stream_id = alh_stream_id;
+
+ if (setup)
+ return hda_ctrl_dai_widget_setup(w);
+
+ return hda_ctrl_dai_widget_free(w);
+}
+
static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
+ struct snd_pcm_substream *substream = params_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = params_data->link_id;
- int alh_stream_id = params_data->alh_stream_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | (d->id);
- config.alh.stream_id = alh_stream_id;
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n",
- link_id, d->id, alh_stream_id);
- }
+ struct snd_soc_dapm_widget *w;
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = d->playback_widget;
+ else
+ w = d->capture_widget;
+
+ return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
+ d->id, true);
}
static int sdw_free_stream(struct device *dev,
struct sdw_intel_stream_free_data *free_data)
{
+ struct snd_pcm_substream *substream = free_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = free_data->link_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | d->id;
- config.alh.stream_id = 0xFFFF; /* invalid value on purpose */
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to free stream for link %d dai->id %d\n",
- link_id, d->id);
- }
+ struct snd_soc_dapm_widget *w;
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = d->playback_widget;
+ else
+ w = d->capture_widget;
+
+ /* send invalid stream_id */
+ return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
}
static const struct sdw_intel_ops sdw_callback = {
@@ -294,7 +383,38 @@ void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
sdw_intel_process_wakeen_event(hdev->sdw);
}
-#endif
+#else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
+static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline int hda_sdw_probe(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline int hda_sdw_exit(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+{
+ return false;
+}
+
+static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
+{
+ return IRQ_HANDLED;
+}
+
+static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+{
+ return false;
+}
+
+#endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
/*
* Debug
@@ -412,8 +532,7 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags)
len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
}
- sof_dev_dbg_or_err(sdev->dev, flags & SOF_DBG_DUMP_FORCE_ERR_LEVEL,
- "extended rom status: %s", msg);
+ dev_err(sdev->dev, "extended rom status: %s", msg);
}
@@ -426,8 +545,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
/* print ROM/FW status */
hda_dsp_get_status(sdev);
- /* print panic info if FW boot is complete. Otherwise, print the extended ROM status */
- if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ if (flags & SOF_DBG_DUMP_REGS) {
u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS);
u32 panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
@@ -456,12 +574,9 @@ void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);
rirbsts = snd_hdac_chip_readb(bus, RIRBSTS);
- dev_err(sdev->dev,
- "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
+ dev_err(sdev->dev, "hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
intsts, intctl, rirbsts);
- dev_err(sdev->dev,
- "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n",
- ppsts, adspis);
+ dev_err(sdev->dev, "dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", ppsts, adspis);
}
void hda_ipc_dump(struct snd_sof_dev *sdev)
@@ -479,8 +594,7 @@ void hda_ipc_dump(struct snd_sof_dev *sdev)
/* dump the IPC regs */
/* TODO: parse the raw msg */
- dev_err(sdev->dev,
- "error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
+ dev_err(sdev->dev, "host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
hipcie, hipct, hipcctl);
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 4fdfb108645c..1195018a1f4f 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -492,17 +492,8 @@ struct sof_intel_hda_stream {
*/
int hda_dsp_probe(struct snd_sof_dev *sdev);
int hda_dsp_remove(struct snd_sof_dev *sdev);
-int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev,
- unsigned int core_mask);
-int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev,
- unsigned int core_mask);
-int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask);
int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask);
int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask);
-bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
- unsigned int core_mask);
int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask);
void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
@@ -572,9 +563,9 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream,
int enable, u32 size);
-void hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz);
+int hda_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz);
int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
const struct sof_ipc_pcm_params_reply *reply);
@@ -619,8 +610,6 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir);
*/
int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev);
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev);
-int hda_dsp_cl_boot_firmware_iccmax_icl(struct snd_sof_dev *sdev);
-int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev);
/* pre and post fw run ops */
int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev);
@@ -695,45 +684,15 @@ bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev);
#else
-static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
-{
- return 0;
-}
-
-static inline int hda_sdw_probe(struct snd_sof_dev *sdev)
-{
- return 0;
-}
-
static inline int hda_sdw_startup(struct snd_sof_dev *sdev)
{
return 0;
}
-static inline int hda_sdw_exit(struct snd_sof_dev *sdev)
-{
- return 0;
-}
-
static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
{
}
-static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
-{
- return false;
-}
-
-static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
-{
- return IRQ_HANDLED;
-}
-
-static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
-{
- return false;
-}
-
static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
{
}
@@ -774,4 +733,9 @@ void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
/* PCI driver selection and probe */
int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id);
+struct snd_sof_dai;
+struct sof_ipc_dai_config;
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w);
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w);
+
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index ee095b8f2d01..0b2cc331d55b 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -41,6 +41,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_thread = cnl_ipc_irq_thread,
@@ -64,6 +68,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.debug_map_count = ARRAY_SIZE(icl_dsp_debugfs),
.dbg_dump = hda_dsp_dump,
.ipc_dump = cnl_ipc_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
.pcm_open = hda_dsp_pcm_open,
@@ -125,7 +130,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index f89e746c2570..a023b3cc0af4 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -26,7 +26,6 @@ static const struct sof_dev_desc bxt_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &apl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -42,7 +41,6 @@ static const struct sof_dev_desc glk_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &apl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c
index f23257adf2ab..40cf1cd00042 100644
--- a/sound/soc/sof/intel/pci-cnl.c
+++ b/sound/soc/sof/intel/pci-cnl.c
@@ -27,7 +27,6 @@ static const struct sof_dev_desc cnl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -44,7 +43,6 @@ static const struct sof_dev_desc cfl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -61,7 +59,6 @@ static const struct sof_dev_desc cml_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c
index 2f60c28ae81f..39c84121b313 100644
--- a/sound/soc/sof/intel/pci-icl.c
+++ b/sound/soc/sof/intel/pci-icl.c
@@ -27,7 +27,6 @@ static const struct sof_dev_desc icl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &icl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -43,7 +42,6 @@ static const struct sof_dev_desc jsl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &jsl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index d04ce84fe7cc..f2ea34df9741 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -27,7 +27,6 @@ static const struct sof_dev_desc tgl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &tgl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -44,7 +43,6 @@ static const struct sof_dev_desc tglh_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &tglh_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -60,7 +58,6 @@ static const struct sof_dev_desc ehl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &ehl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -77,7 +74,6 @@ static const struct sof_dev_desc adls_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &adls_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
@@ -94,7 +90,6 @@ static const struct sof_dev_desc adl_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &tgl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
index 4bded668b672..18eb41b8a8f4 100644
--- a/sound/soc/sof/intel/pci-tng.c
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -142,6 +142,10 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_handler = atom_irq_handler,
.irq_thread = atom_irq_thread,
@@ -152,8 +156,8 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
+ .ipc_msg_data = sof_ipc_msg_data,
+ .ipc_pcm_params = sof_ipc_pcm_params,
/* machine driver */
.machine_select = atom_machine_select,
@@ -165,10 +169,11 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.debug_map = tng_debugfs,
.debug_map_count = ARRAY_SIZE(tng_debugfs),
.dbg_dump = atom_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -187,7 +192,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
const struct sof_intel_dsp_desc tng_chip_info = {
@@ -201,7 +206,6 @@ static const struct sof_dev_desc tng_desc = {
.resindex_pcicfg_base = -1,
.resindex_imr_base = 0,
.irqindex_host_ipc = -1,
- .resindex_dma_base = -1,
.chip_info = &tng_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 199d41a7dc9b..48da8e7a67bc 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -37,6 +37,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* doorbell */
.irq_thread = cnl_ipc_irq_thread,
@@ -60,6 +64,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs),
.dbg_dump = hda_dsp_dump,
.ipc_dump = cnl_ipc_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* stream callbacks */
.pcm_open = hda_dsp_pcm_open,
@@ -120,7 +125,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .arch_ops = &sof_xtensa_arch_ops,
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
};
EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index c2d07b783f60..e6c53c6c470e 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -18,7 +18,7 @@
#include "sof-audio.h"
#include "ops.h"
-static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id);
+static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type);
static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd);
/*
@@ -192,6 +192,29 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
str2 = "unknown type"; break;
}
break;
+ case SOF_IPC_GLB_PROBE:
+ str = "GLB_PROBE";
+ switch (type) {
+ case SOF_IPC_PROBE_INIT:
+ str2 = "INIT"; break;
+ case SOF_IPC_PROBE_DEINIT:
+ str2 = "DEINIT"; break;
+ case SOF_IPC_PROBE_DMA_ADD:
+ str2 = "DMA_ADD"; break;
+ case SOF_IPC_PROBE_DMA_INFO:
+ str2 = "DMA_INFO"; break;
+ case SOF_IPC_PROBE_DMA_REMOVE:
+ str2 = "DMA_REMOVE"; break;
+ case SOF_IPC_PROBE_POINT_ADD:
+ str2 = "POINT_ADD"; break;
+ case SOF_IPC_PROBE_POINT_INFO:
+ str2 = "POINT_INFO"; break;
+ case SOF_IPC_PROBE_POINT_REMOVE:
+ str2 = "POINT_REMOVE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
default:
str = "unknown GLB command"; break;
}
@@ -226,15 +249,17 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
msecs_to_jiffies(sdev->ipc_timeout));
if (ret == 0) {
- dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n",
- hdr->cmd, hdr->size);
+ dev_err(sdev->dev,
+ "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n",
+ hdr->cmd, hdr->size, msg->reply_size);
snd_sof_handle_fw_exception(ipc->sdev);
ret = -ETIMEDOUT;
} else {
ret = msg->reply_error;
if (ret < 0) {
- dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n",
- hdr->cmd, msg->reply_size);
+ dev_err(sdev->dev,
+ "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n",
+ hdr->cmd, hdr->size, msg->reply_size, ret);
} else {
ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
if (msg->reply_size)
@@ -242,6 +267,12 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
memcpy(reply_data, msg->reply_data,
msg->reply_size);
}
+
+ /* re-enable dumps after successful IPC tx */
+ if (sdev->ipc_dump_printed) {
+ sdev->dbg_dump_printed = false;
+ sdev->ipc_dump_printed = false;
+ }
}
return ret;
@@ -286,7 +317,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
spin_unlock_irq(&sdev->ipc_lock);
- if (ret < 0) {
+ if (ret) {
dev_err_ratelimited(sdev->dev,
"error: ipc tx failed with error %d\n",
ret);
@@ -296,10 +327,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
ipc_log_header(sdev->dev, "ipc tx", msg->header);
/* now wait for completion */
- if (!ret)
- ret = tx_wait_done(ipc, msg, reply_data);
-
- return ret;
+ return tx_wait_done(ipc, msg, reply_data);
}
/* send IPC message from host to DSP */
@@ -369,15 +397,52 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
}
EXPORT_SYMBOL(snd_sof_ipc_reply);
+static void ipc_comp_notification(struct snd_sof_dev *sdev,
+ struct sof_ipc_cmd_hdr *hdr)
+{
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+ struct sof_ipc_ctrl_data *cdata;
+ int ret;
+
+ switch (msg_type) {
+ case SOF_IPC_COMP_GET_VALUE:
+ case SOF_IPC_COMP_GET_DATA:
+ cdata = kmalloc(hdr->size, GFP_KERNEL);
+ if (!cdata)
+ return;
+
+ /* read back full message */
+ ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: failed to read component event: %d\n", ret);
+ goto err;
+ }
+ break;
+ default:
+ dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type);
+ return;
+ }
+
+ snd_sof_control_notify(sdev, cdata);
+
+err:
+ kfree(cdata);
+}
+
/* DSP firmware has sent host a message */
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
{
struct sof_ipc_cmd_hdr hdr;
u32 cmd, type;
- int err = 0;
+ int err;
/* read back header */
- snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
+ err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
+ if (err < 0) {
+ dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
+ return;
+ }
ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
@@ -393,9 +458,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
err = sof_ops(sdev)->fw_ready(sdev, cmd);
if (err < 0)
- sdev->fw_state = SOF_FW_BOOT_READY_FAILED;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
else
- sdev->fw_state = SOF_FW_BOOT_COMPLETE;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
/* wake up firmware loader */
wake_up(&sdev->boot_wait);
@@ -404,7 +469,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
case SOF_IPC_GLB_COMPOUND:
case SOF_IPC_GLB_TPLG_MSG:
case SOF_IPC_GLB_PM_MSG:
+ break;
case SOF_IPC_GLB_COMP_MSG:
+ ipc_comp_notification(sdev, &hdr);
break;
case SOF_IPC_GLB_STREAM_MSG:
/* need to pass msg id into the function */
@@ -426,19 +493,22 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
* IPC trace mechanism.
*/
-static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id)
+static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type)
{
struct sof_ipc_dma_trace_posn posn;
+ int ret;
- switch (msg_id) {
+ switch (msg_type) {
case SOF_IPC_TRACE_DMA_POSITION:
/* read back full message */
- snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
- snd_sof_trace_update_pos(sdev, &posn);
+ ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
+ if (ret < 0)
+ dev_warn(sdev->dev, "failed to read trace position: %d\n", ret);
+ else
+ snd_sof_trace_update_pos(sdev, &posn);
break;
default:
- dev_err(sdev->dev, "error: unhandled trace message %x\n",
- msg_id);
+ dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type);
break;
}
}
@@ -453,7 +523,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
struct snd_sof_pcm_stream *stream;
struct sof_ipc_stream_posn posn;
struct snd_sof_pcm *spcm;
- int direction;
+ int direction, ret;
spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
if (!spcm) {
@@ -464,15 +534,21 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ if (ret < 0) {
+ dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
+ return;
+ }
dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
posn.host_posn, posn.dai_posn, posn.wallclock);
memcpy(&stream->posn, &posn, sizeof(posn));
- /* only inform ALSA for period_wakeup mode */
- if (!stream->substream->runtime->no_period_wakeup)
+ if (spcm->pcm.compress)
+ snd_sof_compr_fragment_elapsed(stream->cstream);
+ else if (!stream->substream->runtime->no_period_wakeup)
+ /* only inform ALSA for period_wakeup mode */
snd_sof_pcm_period_elapsed(stream->substream);
}
@@ -483,7 +559,7 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
struct snd_sof_pcm_stream *stream;
struct sof_ipc_stream_posn posn;
struct snd_sof_pcm *spcm;
- int direction;
+ int direction, ret;
spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
if (!spcm) {
@@ -493,7 +569,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ if (ret < 0) {
+ dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
+ return;
+ }
dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n",
posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
@@ -520,7 +600,7 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd)
ipc_xrun(sdev, msg_id);
break;
default:
- dev_err(sdev->dev, "error: unhandled stream message %x\n",
+ dev_err(sdev->dev, "error: unhandled stream message %#x\n",
msg_id);
break;
}
@@ -672,24 +752,50 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_ctrl_data_params sparams;
+ struct snd_sof_widget *swidget;
+ bool widget_found = false;
size_t send_bytes;
int err;
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == scontrol->comp_id) {
+ widget_found = true;
+ break;
+ }
+ }
+
+ if (!widget_found) {
+ dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
+ return -EINVAL;
+ }
+
+ /*
+ * Volatile controls should always be part of static pipelines and the widget use_count
+ * would always be > 0 in this case. For the others, just return the cached value if the
+ * widget is not set up.
+ */
+ if (!swidget->use_count)
+ return 0;
+
/* read or write firmware volume */
if (scontrol->readback_offset != 0) {
/* write/read value header via mmaped region */
send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
cdata->num_elems;
if (send)
- snd_sof_dsp_block_write(sdev, sdev->mmio_bar,
- scontrol->readback_offset,
- cdata->chanv, send_bytes);
+ err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM,
+ scontrol->readback_offset,
+ cdata->chanv, send_bytes);
else
- snd_sof_dsp_block_read(sdev, sdev->mmio_bar,
- scontrol->readback_offset,
- cdata->chanv, send_bytes);
- return 0;
+ err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM,
+ scontrol->readback_offset,
+ cdata->chanv, send_bytes);
+
+ if (err)
+ dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n",
+ send ? "write to" : "read from");
+ return err;
}
cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
@@ -762,22 +868,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
}
EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data);
-/*
- * IPC layer enumeration.
- */
-
-int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox,
- size_t dspbox_size, u32 hostbox,
- size_t hostbox_size)
-{
- sdev->dsp_box.offset = dspbox;
- sdev->dsp_box.size = dspbox_size;
- sdev->host_box.offset = hostbox;
- sdev->host_box.size = hostbox_size;
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_dsp_mailbox_init);
-
int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
{
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
@@ -829,6 +919,22 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL(snd_sof_ipc_valid);
+int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg;
+
+ msg = &sdev->ipc->msg;
+ msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!msg->msg_data)
+ return -ENOMEM;
+
+ msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!msg->reply_data)
+ return -ENOMEM;
+
+ return 0;
+}
+
struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc *ipc;
@@ -845,17 +951,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
/* indicate that we aren't sending a message ATM */
msg->ipc_complete = true;
- /* pre-allocate message data */
- msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
- GFP_KERNEL);
- if (!msg->msg_data)
- return NULL;
-
- msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
- GFP_KERNEL);
- if (!msg->reply_data)
- return NULL;
-
init_waitqueue_head(&msg->waitq);
return ipc;
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index bb79c77775b3..c04646647637 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -13,6 +13,7 @@
#include <linux/firmware.h>
#include <sound/sof.h>
#include <sound/sof/ext_manifest.h>
+#include "sof-priv.h"
#include "ops.h"
static int get_ext_windows(struct snd_sof_dev *sdev,
@@ -86,7 +87,7 @@ static int get_cc_info(struct snd_sof_dev *sdev,
}
/* parse the extended FW boot data structures from FW boot message */
-int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
+static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
{
struct sof_ipc_ext_data_hdr *ext_hdr;
void *ext_data;
@@ -97,15 +98,16 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
return -ENOMEM;
/* get first header */
- snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
sizeof(*ext_hdr));
ext_hdr = ext_data;
while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
/* read in ext structure */
- snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
- (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
- ext_hdr->hdr.size - sizeof(*ext_hdr));
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
+ offset + sizeof(*ext_hdr),
+ (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
+ ext_hdr->hdr.size - sizeof(*ext_hdr));
dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
ext_hdr->type, ext_hdr->hdr.size);
@@ -138,7 +140,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
/* move to next header */
offset += ext_hdr->hdr.size;
- snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
sizeof(*ext_hdr));
ext_hdr = ext_data;
}
@@ -146,7 +148,6 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
kfree(ext_data);
return ret;
}
-EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
const struct sof_ext_man_elem_header *hdr)
@@ -372,7 +373,6 @@ static void sof_get_windows(struct snd_sof_dev *sdev)
u32 debug_size = 0;
u32 debug_offset = 0;
int window_offset;
- int bar;
int i;
if (!sdev->info_window) {
@@ -380,12 +380,6 @@ static void sof_get_windows(struct snd_sof_dev *sdev)
return;
}
- bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
- if (bar < 0) {
- dev_err(sdev->dev, "error: have no bar mapping\n");
- return;
- }
-
for (i = 0; i < sdev->info_window->num_windows; i++) {
elem = &sdev->info_window->window[i];
@@ -400,64 +394,53 @@ static void sof_get_windows(struct snd_sof_dev *sdev)
case SOF_IPC_REGION_UPBOX:
inbox_offset = window_offset + elem->offset;
inbox_size = elem->size;
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- inbox_offset,
- elem->size, "inbox",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ inbox_offset,
+ elem->size, "inbox",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_DOWNBOX:
outbox_offset = window_offset + elem->offset;
outbox_size = elem->size;
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- outbox_offset,
- elem->size, "outbox",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ outbox_offset,
+ elem->size, "outbox",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_TRACE:
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- window_offset +
- elem->offset,
- elem->size, "etrace",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "etrace",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_DEBUG:
debug_offset = window_offset + elem->offset;
debug_size = elem->size;
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- window_offset +
- elem->offset,
- elem->size, "debug",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "debug",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_STREAM:
stream_offset = window_offset + elem->offset;
stream_size = elem->size;
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- stream_offset,
- elem->size, "stream",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ stream_offset,
+ elem->size, "stream",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_REGS:
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- window_offset +
- elem->offset,
- elem->size, "regs",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "regs",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_EXCEPTION:
sdev->dsp_oops_offset = window_offset + elem->offset;
- snd_sof_debugfs_io_item(sdev,
- sdev->bar[bar] +
- window_offset +
- elem->offset,
- elem->size, "exception",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "exception",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
default:
dev_err(sdev->dev, "error: get illegal window info\n");
@@ -470,8 +453,12 @@ static void sof_get_windows(struct snd_sof_dev *sdev)
return;
}
- snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size,
- outbox_offset, outbox_size);
+ sdev->dsp_box.offset = inbox_offset;
+ sdev->dsp_box.size = inbox_size;
+
+ sdev->host_box.offset = outbox_offset;
+ sdev->host_box.size = outbox_size;
+
sdev->stream_box.offset = stream_offset;
sdev->stream_box.size = stream_size;
@@ -493,7 +480,6 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
{
struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
int offset;
- int bar;
int ret;
/* mailbox must be on 4k boundary */
@@ -503,12 +489,6 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
return offset;
}
- bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
- if (bar < 0) {
- dev_err(sdev->dev, "error: have no bar mapping\n");
- return -EINVAL;
- }
-
dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
msg_id, offset);
@@ -516,8 +496,17 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
if (!sdev->first_boot)
return 0;
- /* copy data from the DSP FW ready offset */
- snd_sof_dsp_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
+ /*
+ * copy data from the DSP FW ready offset
+ * Subsequent error handling is not needed for BLK_TYPE_SRAM
+ */
+ ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
+ sizeof(*fw_ready));
+ if (ret) {
+ dev_err(sdev->dev,
+ "error: unable to read fw_ready, read from TYPE_SRAM failed\n");
+ return ret;
+ }
/* make sure ABI version is compatible */
ret = snd_sof_ipc_valid(sdev);
@@ -525,12 +514,11 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
return ret;
/* now check for extended data */
- snd_sof_fw_parse_ext_data(sdev, bar, offset +
- sizeof(struct sof_ipc_fw_ready));
+ snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
sof_get_windows(sdev);
- return 0;
+ return sof_ipc_init_msg_memory(sdev);
}
EXPORT_SYMBOL(sof_fw_ready);
@@ -539,7 +527,7 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
struct snd_sof_mod_hdr *module)
{
struct snd_sof_blk_hdr *block;
- int count, bar;
+ int count, ret;
u32 offset;
size_t remaining;
@@ -576,13 +564,6 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
case SOF_FW_BLK_TYPE_DRAM:
case SOF_FW_BLK_TYPE_SRAM:
offset = block->offset;
- bar = snd_sof_dsp_get_bar_index(sdev, block->type);
- if (bar < 0) {
- dev_err(sdev->dev,
- "error: no BAR mapping for block type 0x%x\n",
- block->type);
- return bar;
- }
break;
default:
dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -600,8 +581,13 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
block->size);
return -EINVAL;
}
- snd_sof_dsp_block_write(sdev, bar, offset,
- block + 1, block->size);
+ ret = snd_sof_dsp_block_write(sdev, block->type, offset,
+ block + 1, block->size);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: write to block type 0x%x failed\n",
+ block->type);
+ return ret;
+ }
if (remaining < block->size) {
dev_err(sdev->dev, "error: not enough data remaining\n");
@@ -800,22 +786,16 @@ error:
}
EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
-int snd_sof_load_firmware(struct snd_sof_dev *sdev)
-{
- dev_dbg(sdev->dev, "loading firmware\n");
-
- if (sof_ops(sdev)->load_firmware)
- return sof_ops(sdev)->load_firmware(sdev);
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_load_firmware);
-
int snd_sof_run_firmware(struct snd_sof_dev *sdev)
{
int ret;
init_waitqueue_head(&sdev->boot_wait);
+ /* (re-)enable dsp dump */
+ sdev->dbg_dump_printed = false;
+ sdev->ipc_dump_printed = false;
+
/* create read-only fw_version debugfs to store boot version info */
if (sdev->first_boot) {
ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
@@ -840,7 +820,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
/* boot the firmware on the DSP */
ret = snd_sof_dsp_run(sdev);
if (ret < 0) {
- dev_err(sdev->dev, "error: failed to reset DSP\n");
+ dev_err(sdev->dev, "error: failed to start DSP\n");
+ snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
return ret;
}
@@ -856,8 +837,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
if (ret == 0) {
dev_err(sdev->dev, "error: firmware boot failure\n");
snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
- SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_FORCE_ERR_LEVEL);
- sdev->fw_state = SOF_FW_BOOT_FAILED;
+ SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return -EIO;
}
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index 11ecebd07907..160b88a2d59f 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -157,6 +157,9 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset)
dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n",
sdev->dsp_oops_offset, offset);
+ /* We want to see the DSP panic! */
+ sdev->dbg_dump_printed = false;
+
snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
snd_sof_trace_notify_for_error(sdev);
}
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 4a5d6e497f05..09bf38fdfb8a 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -241,16 +241,17 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev,
}
/* debug */
-static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags)
-{
- if (sof_ops(sdev)->dbg_dump)
- sof_ops(sdev)->dbg_dump(sdev, flags);
-}
+void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags);
-static inline void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
+static inline int snd_sof_debugfs_add_region_item(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size,
+ const char *name, enum sof_debugfs_access_type access_type)
{
- if (sof_ops(sdev)->ipc_dump)
- sof_ops(sdev)->ipc_dump(sdev);
+ if (sof_ops(sdev) && sof_ops(sdev)->debugfs_add_region_item)
+ return sof_ops(sdev)->debugfs_add_region_item(sdev, blk_type, offset,
+ size, name, access_type);
+
+ return 0;
}
/* register IO */
@@ -297,16 +298,33 @@ static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar,
}
/* block IO */
-static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, u32 bar,
- u32 offset, void *dest, size_t bytes)
+static inline int snd_sof_dsp_block_read(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *dest, size_t bytes)
+{
+ return sof_ops(sdev)->block_read(sdev, blk_type, offset, dest, bytes);
+}
+
+static inline int snd_sof_dsp_block_write(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *src, size_t bytes)
+{
+ return sof_ops(sdev)->block_write(sdev, blk_type, offset, src, bytes);
+}
+
+/* mailbox IO */
+static inline void snd_sof_dsp_mailbox_read(struct snd_sof_dev *sdev,
+ u32 offset, void *dest, size_t bytes)
{
- sof_ops(sdev)->block_read(sdev, bar, offset, dest, bytes);
+ if (sof_ops(sdev)->mailbox_read)
+ sof_ops(sdev)->mailbox_read(sdev, offset, dest, bytes);
}
-static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, u32 bar,
- u32 offset, void *src, size_t bytes)
+static inline void snd_sof_dsp_mailbox_write(struct snd_sof_dev *sdev,
+ u32 offset, void *src, size_t bytes)
{
- sof_ops(sdev)->block_write(sdev, bar, offset, src, bytes);
+ if (sof_ops(sdev)->mailbox_write)
+ sof_ops(sdev)->mailbox_write(sdev, offset, src, bytes);
}
/* ipc */
@@ -400,12 +418,20 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev,
return 0;
}
+/* Firmware loading */
+static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev)
+{
+ dev_dbg(sdev->dev, "loading firmware\n");
+
+ return sof_ops(sdev)->load_firmware(sdev);
+}
+
/* host DSP message data */
-static inline void snd_sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
+static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
{
- sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz);
+ return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz);
}
/* host configure DSP HW parameters */
@@ -503,21 +529,6 @@ snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach,
sof_ops(sdev)->set_mach_params(mach, sdev);
}
-static inline const struct snd_sof_dsp_ops
-*sof_get_ops(const struct sof_dev_desc *d,
- const struct sof_ops_table mach_ops[], int asize)
-{
- int i;
-
- for (i = 0; i < asize; i++) {
- if (d == mach_ops[i].desc)
- return mach_ops[i].ops;
- }
-
- /* not found */
- return NULL;
-}
-
/**
* snd_sof_dsp_register_poll_timeout - Periodically poll an address
* until a condition is met or a timeout occurs
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 9893b182da43..fa0bfcd2474e 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -17,7 +17,7 @@
#include "sof-audio.h"
#include "ops.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "compress.h"
+#include "sof-probes.h"
#endif
/* Create DMA buffer page table for DSP */
@@ -57,7 +57,7 @@ static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream
/*
* sof pcm period elapse work
*/
-void snd_sof_pcm_period_elapsed_work(struct work_struct *work)
+static void snd_sof_pcm_period_elapsed_work(struct work_struct *work)
{
struct snd_sof_pcm_stream *sps =
container_of(work, struct snd_sof_pcm_stream,
@@ -66,6 +66,11 @@ void snd_sof_pcm_period_elapsed_work(struct work_struct *work)
snd_pcm_period_elapsed(sps->substream);
}
+void snd_sof_pcm_init_elapsed_work(struct work_struct *work)
+{
+ INIT_WORK(work, snd_sof_pcm_period_elapsed_work);
+}
+
/*
* sof pcm period elapse, this could be called at irq thread context.
*/
@@ -116,6 +121,40 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
return ret;
}
+static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev,
+ struct snd_soc_pcm_runtime *rtd,
+ struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dai *dai;
+ int ret, j;
+
+ /* query DAPM for list of connected widgets and set them up */
+ for_each_rtd_cpu_dais(rtd, j, dai) {
+ struct snd_soc_dapm_widget_list *list;
+
+ ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
+ dpcm_end_walk_at_be);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
+ dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
+ return ret;
+ }
+
+ spcm->stream[dir].list = list;
+
+ ret = sof_widget_list_setup(sdev, spcm, dir);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
+ spcm->pcm.pcm_id, dir);
+ spcm->stream[dir].list = NULL;
+ snd_soc_dapm_dai_free_widgets(&list);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -213,7 +252,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
- /* send IPC to the DSP */
+ /* if this is a repeated hw_params without hw_free, skip setting up widgets */
+ if (!spcm->stream[substream->stream].list) {
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* send hw_params IPC to the DSP */
ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0) {
@@ -259,6 +305,10 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
err = ret;
}
+ ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ if (ret < 0)
+ err = ret;
+
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
@@ -316,6 +366,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
bool reset_hw_params = false;
+ bool free_widget_list = false;
bool ipc_first = false;
int ret;
@@ -386,6 +437,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
+ free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
@@ -414,8 +466,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
- if (!ret && reset_hw_params)
+ if (!ret && reset_hw_params) {
ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
+ if (ret < 0)
+ return ret;
+
+ /* free widget list only for SUSPEND trigger */
+ if (free_widget_list)
+ ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ }
return ret;
}
@@ -829,11 +888,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->trigger = sof_pcm_trigger;
pd->pointer = sof_pcm_pointer;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
- pd->compress_ops = &sof_compressed_ops;
-#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- /* override cops when probe support is enabled */
pd->compress_ops = &sof_probe_compressed_ops;
#endif
pd->pcm_construct = sof_pcm_new;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index c83fb6255961..ac8ae6e422a7 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -122,7 +122,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
old_state == SOF_DSP_PM_D0)
return 0;
- sdev->fw_state = SOF_FW_BOOT_PREPARE;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* load the firmware */
ret = snd_sof_load_firmware(sdev);
@@ -133,7 +133,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
- sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
/*
* Boot the firmware. The FW boot status will be modified
@@ -157,7 +157,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
- ret = sof_restore_pipelines(sdev->dev);
+ ret = sof_set_up_pipelines(sdev, false);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to restore pipeline after resume %d\n",
@@ -191,7 +191,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
goto suspend;
- /* set restore_stream for all streams during system suspend */
+ /* prepare for streams to be resumed properly upon resume */
if (!runtime_suspend) {
ret = sof_set_hw_params_upon_resume(sdev->dev);
if (ret < 0) {
@@ -208,6 +208,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (target_state == SOF_DSP_PM_D0)
goto suspend;
+ sof_tear_down_pipelines(sdev, false);
+
/* release trace */
snd_sof_release_trace(sdev);
@@ -255,7 +257,7 @@ suspend:
return ret;
/* reset FW state */
- sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
sdev->enabled_cores_mask = 0;
return ret;
diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h
deleted file mode 100644
index 5e159ab239fa..000000000000
--- a/sound/soc/sof/probe.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* 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) 2019-2020 Intel Corporation. All rights reserved.
- *
- * Author: Cezary Rojewski <cezary.rojewski@intel.com>
- */
-
-#ifndef __SOF_PROBE_H
-#define __SOF_PROBE_H
-
-#include <sound/sof/header.h>
-
-struct snd_sof_dev;
-
-#define SOF_PROBE_INVALID_NODE_ID UINT_MAX
-
-struct sof_probe_dma {
- unsigned int stream_tag;
- unsigned int dma_buffer_size;
-} __packed;
-
-enum sof_connection_purpose {
- SOF_CONNECTION_PURPOSE_EXTRACT = 1,
- SOF_CONNECTION_PURPOSE_INJECT,
-};
-
-struct sof_probe_point_desc {
- unsigned int buffer_id;
- unsigned int purpose;
- unsigned int stream_tag;
-} __packed;
-
-struct sof_ipc_probe_dma_add_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- struct sof_probe_dma dma[];
-} __packed;
-
-struct sof_ipc_probe_info_params {
- struct sof_ipc_reply rhdr;
- unsigned int num_elems;
- union {
- struct sof_probe_dma dma[0];
- struct sof_probe_point_desc desc[0];
- };
-} __packed;
-
-struct sof_ipc_probe_dma_remove_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- unsigned int stream_tag[];
-} __packed;
-
-struct sof_ipc_probe_point_add_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- struct sof_probe_point_desc desc[];
-} __packed;
-
-struct sof_ipc_probe_point_remove_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- unsigned int buffer_id[];
-} __packed;
-
-int sof_ipc_probe_init(struct snd_sof_dev *sdev,
- u32 stream_tag, size_t buffer_size);
-int sof_ipc_probe_deinit(struct snd_sof_dev *sdev);
-int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev,
- struct sof_probe_dma **dma, size_t *num_dma);
-int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev,
- struct sof_probe_dma *dma, size_t num_dma);
-int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev,
- unsigned int *stream_tag, size_t num_stream_tag);
-int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc **desc, size_t *num_desc);
-int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc *desc, size_t num_desc);
-int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
- unsigned int *buffer_id, size_t num_buffer_id);
-
-#endif
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 989912f2b739..7cbe757c1fe2 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -8,9 +8,493 @@
// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
//
+#include <linux/bitfield.h>
#include "sof-audio.h"
#include "ops.h"
+static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ int ipc_cmd, ctrl_type;
+ int ret;
+
+ /* reset readback offset for scontrol */
+ scontrol->readback_offset = 0;
+
+ /* notify DSP of kcontrol values */
+ switch (scontrol->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_ENUM:
+ case SOF_CTRL_CMD_SWITCH:
+ ipc_cmd = SOF_IPC_COMP_SET_VALUE;
+ ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ ipc_cmd = SOF_IPC_COMP_SET_DATA;
+ ctrl_type = SOF_CTRL_TYPE_DATA_SET;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true);
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n",
+ scontrol->comp_id);
+
+ return ret;
+}
+
+static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai)
+{
+ struct sof_ipc_dai_config *config;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ config = &dai->dai_config[dai->current_config];
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ /* set NONE flag to clear all previous settings */
+ config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE);
+
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name);
+
+ return ret;
+}
+
+static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+ int ret;
+
+ /* set up all controls for the widget */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id) {
+ ret = sof_kcontrol_setup(sdev, scontrol);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n",
+ swidget->widget->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
+{
+ struct snd_sof_route *sroute;
+
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ if (sroute->src_widget == widget || sroute->sink_widget == widget)
+ sroute->setup = false;
+}
+
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_free ipc_free = {
+ .hdr = {
+ .size = sizeof(ipc_free),
+ .cmd = SOF_IPC_GLB_TPLG_MSG,
+ },
+ .id = swidget->comp_id,
+ };
+ struct sof_ipc_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ /* only free when use_count is 0 */
+ if (--swidget->use_count)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
+ break;
+ case snd_soc_dapm_buffer:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
+ break;
+ default:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
+ break;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
+ swidget->use_count++;
+ return ret;
+ }
+
+ /* reset route setup status for all routes that contain this widget */
+ sof_reset_route_setup_status(sdev, swidget);
+ swidget->complete = 0;
+ dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_widget_free);
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_pipe_new *pipeline;
+ struct sof_ipc_comp_reply r;
+ struct sof_ipc_cmd_hdr *hdr;
+ struct sof_ipc_comp *comp;
+ struct snd_sof_dai *dai;
+ size_t ipc_size;
+ int ret;
+
+ /* skip if there is no private data */
+ if (!swidget->private)
+ return 0;
+
+ /* widget already set up */
+ if (++swidget->use_count > 1)
+ return 0;
+
+ ret = sof_pipeline_core_enable(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
+ ret, swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ switch (swidget->id) {
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_dai_out:
+ ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext);
+ comp = kzalloc(ipc_size, GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai = swidget->private;
+ dai->configured = false;
+ memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai));
+
+ /* append extended data to the end of the component */
+ memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext,
+ sizeof(swidget->comp_ext));
+
+ ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r));
+ kfree(comp);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load widget %s\n",
+ swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ ret = sof_dai_config_setup(sdev, dai);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n",
+ swidget->widget->name);
+ sof_widget_free(sdev, swidget);
+ return ret;
+ }
+ break;
+ case snd_soc_dapm_scheduler:
+ pipeline = swidget->private;
+ ret = sof_load_pipeline_ipc(sdev, pipeline, &r);
+ break;
+ default:
+ hdr = swidget->private;
+ ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
+ &r, sizeof(r));
+ break;
+ }
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ /* restore kcontrols for widget */
+ ret = sof_widget_kcontrol_setup(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
+ swidget->widget->name);
+ sof_widget_free(sdev, swidget);
+ return ret;
+ }
+
+ dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
+
+ return 0;
+
+use_count_dec:
+ swidget->use_count--;
+ return ret;
+}
+EXPORT_SYMBOL(sof_widget_setup);
+
+static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
+{
+ struct sof_ipc_pipe_comp_connect *connect;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ /* skip if there's no private data */
+ if (!sroute->private)
+ return 0;
+
+ /* nothing to do if route is already set up */
+ if (sroute->setup)
+ return 0;
+
+ connect = sroute->private;
+
+ dev_dbg(sdev->dev, "setting up route %s -> %s\n",
+ sroute->src_widget->widget->name,
+ sroute->sink_widget->widget->name);
+
+ /* send ipc */
+ ret = sof_ipc_tx_message(sdev->ipc,
+ connect->hdr.cmd,
+ connect, sizeof(*connect),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ sroute->setup = true;
+
+ return 0;
+}
+
+static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
+ struct snd_soc_dapm_widget *wsink)
+{
+ struct snd_sof_widget *src_widget = wsource->dobj.private;
+ struct snd_sof_widget *sink_widget = wsink->dobj.private;
+ struct snd_sof_route *sroute;
+ bool route_found = false;
+
+ /* ignore routes involving virtual widgets in topology */
+ switch (src_widget->id) {
+ case snd_soc_dapm_out_drv:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ return 0;
+ default:
+ break;
+ }
+
+ switch (sink_widget->id) {
+ case snd_soc_dapm_out_drv:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ return 0;
+ default:
+ break;
+ }
+
+ /* find route matching source and sink widgets */
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
+ route_found = true;
+ break;
+ }
+
+ if (!route_found) {
+ dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ return sof_route_setup_ipc(sdev, sroute);
+}
+
+static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget_list *list, int dir)
+{
+ struct snd_soc_dapm_widget *widget;
+ struct snd_soc_dapm_path *p;
+ int ret;
+ int i;
+
+ /*
+ * Set up connections between widgets in the sink/source paths based on direction.
+ * Some non-SOF widgets exist in topology either for compatibility or for the
+ * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
+ * events. But they are not handled by the firmware. So ignore them.
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ for_each_dapm_widgets(list, i, widget) {
+ if (!widget->dobj.private)
+ continue;
+
+ snd_soc_dapm_widget_for_each_sink_path(widget, p)
+ if (p->sink->dobj.private) {
+ ret = sof_route_setup(sdev, widget, p->sink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ } else {
+ for_each_dapm_widgets(list, i, widget) {
+ if (!widget->dobj.private)
+ continue;
+
+ snd_soc_dapm_widget_for_each_source_path(widget, p)
+ if (p->source->dobj.private) {
+ ret = sof_route_setup(sdev, p->source, widget);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_soc_dapm_widget *widget;
+ int i, ret, num_widgets;
+
+ /* nothing to set up */
+ if (!list)
+ return 0;
+
+ /* set up widgets in the list */
+ for_each_dapm_widgets(list, num_widgets, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_widget *pipe_widget;
+
+ if (!swidget)
+ continue;
+
+ /*
+ * The scheduler widget for a pipeline is not part of the connected DAPM
+ * widget list and it needs to be set up before the widgets in the pipeline
+ * are set up. The use_count for the scheduler widget is incremented for every
+ * widget in a given pipeline to ensure that it is freed only after the last
+ * widget in the pipeline is freed.
+ */
+ pipe_widget = swidget->pipe_widget;
+ if (!pipe_widget) {
+ dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ ret = sof_widget_setup(sdev, pipe_widget);
+ if (ret < 0)
+ goto widget_free;
+
+ /* set up the widget */
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0) {
+ sof_widget_free(sdev, pipe_widget);
+ goto widget_free;
+ }
+ }
+
+ /*
+ * error in setting pipeline connections will result in route status being reset for
+ * routes that were successfully set up when the widgets are freed.
+ */
+ ret = sof_setup_pipeline_connections(sdev, list, dir);
+ if (ret < 0)
+ goto widget_free;
+
+ /* complete pipelines */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_widget *pipe_widget;
+
+ if (!swidget)
+ continue;
+
+ pipe_widget = swidget->pipe_widget;
+ if (!pipe_widget) {
+ dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ if (pipe_widget->complete)
+ continue;
+
+ pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget);
+ if (pipe_widget->complete < 0) {
+ ret = pipe_widget->complete;
+ goto widget_free;
+ }
+ }
+
+ return 0;
+
+widget_free:
+ /* free all widgets that have been set up successfully */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ if (!num_widgets--)
+ break;
+
+ sof_widget_free(sdev, swidget);
+ sof_widget_free(sdev, swidget->pipe_widget);
+ }
+
+ return ret;
+}
+
+int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_soc_dapm_widget *widget;
+ int i, ret;
+ int ret1 = 0;
+
+ /* nothing to free */
+ if (!list)
+ return 0;
+
+ /*
+ * Free widgets in the list. This can fail but continue freeing other widgets to keep
+ * use_counts balanced.
+ */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ /*
+ * free widget and its pipe_widget. Either of these can fail, but free as many as
+ * possible before freeing the list and returning the error.
+ */
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ ret1 = ret;
+
+ ret = sof_widget_free(sdev, swidget->pipe_widget);
+ if (ret < 0)
+ ret1 = ret;
+ }
+
+ snd_soc_dapm_dai_free_widgets(&list);
+ spcm->stream[dir].list = NULL;
+
+ return ret1;
+}
+
/*
* helper to determine if there are only D0i3 compatible
* streams active
@@ -93,55 +577,6 @@ int sof_set_hw_params_upon_resume(struct device *dev)
return snd_sof_dsp_hw_params_upon_resume(sdev);
}
-static int sof_restore_kcontrols(struct device *dev)
-{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct snd_sof_control *scontrol;
- int ipc_cmd, ctrl_type;
- int ret = 0;
-
- /* restore kcontrol values */
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- /* reset readback offset for scontrol after resuming */
- scontrol->readback_offset = 0;
-
- /* notify DSP of kcontrol values */
- switch (scontrol->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_ENUM:
- case SOF_CTRL_CMD_SWITCH:
- ipc_cmd = SOF_IPC_COMP_SET_VALUE;
- ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- true);
- break;
- case SOF_CTRL_CMD_BINARY:
- ipc_cmd = SOF_IPC_COMP_SET_DATA;
- ctrl_type = SOF_CTRL_TYPE_DATA_SET;
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- true);
- break;
-
- default:
- break;
- }
-
- if (ret < 0) {
- dev_err(dev,
- "error: failed kcontrol value set for widget: %d\n",
- scontrol->comp_id);
-
- return ret;
- }
- }
-
- return 0;
-}
-
const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
int pipeline_id)
{
@@ -158,142 +593,53 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
return NULL;
}
-int sof_restore_pipelines(struct device *dev)
+int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
- struct sof_ipc_pipe_new *pipeline;
- struct snd_sof_dai *dai;
- struct sof_ipc_cmd_hdr *hdr;
- struct sof_ipc_comp *comp;
- size_t ipc_size;
int ret;
/* restore pipeline components */
list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
- struct sof_ipc_comp_reply r;
-
- /* skip if there is no private data */
- if (!swidget->private)
+ /* only set up the widgets belonging to static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
continue;
- ret = sof_pipeline_core_enable(sdev, swidget);
- if (ret < 0) {
- dev_err(dev,
- "error: failed to enable target core: %d\n",
- ret);
-
- return ret;
- }
+ /* update DAI config. The IPC will be sent in sof_widget_setup() */
+ if (WIDGET_IS_DAI(swidget->id)) {
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_ipc_dai_config *config;
- switch (swidget->id) {
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- ipc_size = sizeof(struct sof_ipc_comp_dai) +
- sizeof(struct sof_ipc_comp_ext);
- comp = kzalloc(ipc_size, GFP_KERNEL);
- if (!comp)
- return -ENOMEM;
-
- dai = swidget->private;
- memcpy(comp, &dai->comp_dai,
- sizeof(struct sof_ipc_comp_dai));
-
- /* append extended data to the end of the component */
- memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai),
- &swidget->comp_ext, sizeof(swidget->comp_ext));
-
- ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
- comp, ipc_size,
- &r, sizeof(r));
- kfree(comp);
- break;
- case snd_soc_dapm_scheduler:
+ if (!dai || !dai->dai_config)
+ continue;
+ config = dai->dai_config;
/*
- * During suspend, all DSP cores are powered off.
- * Therefore upon resume, create the pipeline comp
- * and power up the core that the pipeline is
- * scheduled on.
+ * The link DMA channel would be invalidated for running
+ * streams but not for streams that were in the PAUSED
+ * state during suspend. So invalidate it here before setting
+ * the dai config in the DSP.
*/
- pipeline = swidget->private;
- ret = sof_load_pipeline_ipc(dev, pipeline, &r);
- break;
- default:
- hdr = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
- swidget->private, hdr->size,
- &r, sizeof(r));
- break;
+ if (config->type == SOF_DAI_INTEL_HDA)
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
}
- if (ret < 0) {
- dev_err(dev,
- "error: failed to load widget type %d with ID: %d\n",
- swidget->widget->id, swidget->comp_id);
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0)
return ret;
- }
}
/* restore pipeline connections */
- list_for_each_entry_reverse(sroute, &sdev->route_list, list) {
- struct sof_ipc_pipe_comp_connect *connect;
- struct sof_ipc_reply reply;
+ list_for_each_entry(sroute, &sdev->route_list, list) {
- /* skip if there's no private data */
- if (!sroute->private)
+ /* only set up routes belonging to static pipelines */
+ if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||
+ sroute->sink_widget->dynamic_pipeline_widget))
continue;
- connect = sroute->private;
-
- /* send ipc */
- ret = sof_ipc_tx_message(sdev->ipc,
- connect->hdr.cmd,
- connect, sizeof(*connect),
- &reply, sizeof(reply));
+ ret = sof_route_setup_ipc(sdev, sroute);
if (ret < 0) {
- dev_err(dev,
- "error: failed to load route sink %s control %s source %s\n",
- sroute->route->sink,
- sroute->route->control ? sroute->route->control
- : "none",
- sroute->route->source);
-
- return ret;
- }
- }
-
- /* restore dai links */
- list_for_each_entry_reverse(dai, &sdev->dai_list, list) {
- struct sof_ipc_reply reply;
- struct sof_ipc_dai_config *config = &dai->dai_config[dai->current_config];
-
- if (!config) {
- dev_err(dev, "error: no config for DAI %s\n",
- dai->name);
- continue;
- }
-
- /*
- * The link DMA channel would be invalidated for running
- * streams but not for streams that were in the PAUSED
- * state during suspend. So invalidate it here before setting
- * the dai config in the DSP.
- */
- if (config->type == SOF_DAI_INTEL_HDA)
- config->hda.link_dma_ch = DMA_CHAN_INVALID;
-
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config,
- config->hdr.size,
- &reply, sizeof(reply));
-
- if (ret < 0) {
- dev_err(dev,
- "error: failed to set dai config for %s\n",
- dai->name);
-
+ dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__);
return ret;
}
}
@@ -302,21 +648,52 @@ int sof_restore_pipelines(struct device *dev)
list_for_each_entry(swidget, &sdev->widget_list, list) {
switch (swidget->id) {
case snd_soc_dapm_scheduler:
+ /* only complete static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
+ continue;
+
swidget->complete =
- snd_sof_complete_pipeline(dev, swidget);
+ snd_sof_complete_pipeline(sdev, swidget);
break;
default:
break;
}
}
- /* restore pipeline kcontrols */
- ret = sof_restore_kcontrols(dev);
- if (ret < 0)
- dev_err(dev,
- "error: restoring kcontrols after resume\n");
+ return 0;
+}
- return ret;
+/*
+ * This function doesn't free widgets during suspend. It only resets the set up status for all
+ * routes and use_count for all widgets.
+ */
+int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
+{
+ struct snd_sof_widget *swidget;
+ struct snd_sof_route *sroute;
+ int ret;
+
+ /*
+ * This function is called during suspend and for one-time topology verification during
+ * first boot. In both cases, there is no need to protect swidget->use_count and
+ * sroute->setup because during suspend all streams are suspended and during topology
+ * loading the sound card unavailable to open PCMs.
+ */
+ list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
+ if (!verify) {
+ swidget->use_count = 0;
+ continue;
+ }
+
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ sroute->setup = false;
+
+ return 0;
}
/*
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index dc274e63ed9a..05e98e231b85 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -28,13 +28,17 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
+#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
struct snd_dma_buffer page_table;
struct sof_ipc_stream_posn posn;
struct snd_pcm_substream *substream;
+ struct snd_compr_stream *cstream;
struct work_struct period_elapsed_work;
+ struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */
bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */
/*
* flag to indicate that the DSP pipelines should be kept
@@ -66,6 +70,7 @@ struct snd_sof_control {
int min_volume_step; /* min volume step for volume_table */
int max_volume_step; /* max volume step for volume_table */
int num_channels;
+ unsigned int access;
u32 readback_offset; /* offset to mmapped data if used */
struct sof_ipc_ctrl_data *control_data;
u32 size; /* cdata size */
@@ -75,19 +80,36 @@ struct snd_sof_control {
struct list_head list; /* list in sdev control list */
struct snd_sof_led_control led_ctl;
+
+ /* if true, the control's data needs to be updated from Firmware */
+ bool comp_data_dirty;
};
+struct snd_sof_widget;
+
/* ASoC SOF DAPM widget */
struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_id;
int pipeline_id;
int complete;
+ int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id;
+ /*
+ * Flag indicating if the widget should be set up dynamically when a PCM is opened.
+ * This flag is only set for the scheduler type widget in topology. During topology
+ * loading, this flag is propagated to all the widgets belonging to the same pipeline.
+ * When this flag is not set, a widget is set up at the time of topology loading
+ * and retained until the DSP enters D3. It will need to be set up again when resuming
+ * from D3.
+ */
+ bool dynamic_pipeline_widget;
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
+ struct snd_sof_widget *pipe_widget;
/* extended data for UUID components */
struct sof_ipc_comp_ext comp_ext;
@@ -101,6 +123,9 @@ struct snd_sof_route {
struct snd_soc_dapm_route *route;
struct list_head list; /* list in sdev route list */
+ struct snd_sof_widget *src_widget;
+ struct snd_sof_widget *sink_widget;
+ bool setup;
void *private;
};
@@ -109,11 +134,11 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_soc_component *scomp;
const char *name;
- const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
int number_configs;
int current_config;
+ bool configured; /* DAI configured during BE hw_params */
struct sof_ipc_dai_config *dai_config;
struct list_head list; /* list in sdev dai list */
};
@@ -148,6 +173,8 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
unsigned int size);
int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data,
unsigned int size);
+void snd_sof_control_notify(struct snd_sof_dev *sdev,
+ struct sof_ipc_ctrl_data *cdata);
/*
* Topology.
@@ -155,10 +182,10 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
* be freed by snd_soc_unregister_component,
*/
int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file);
-int snd_sof_complete_pipeline(struct device *dev,
+int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget);
-int sof_load_pipeline_ipc(struct device *dev,
+int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
struct sof_ipc_pipe_new *pipeline,
struct sof_ipc_comp_reply *r);
int sof_pipeline_core_enable(struct snd_sof_dev *sdev,
@@ -205,7 +232,15 @@ struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
int pipeline_id);
void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream);
-void snd_sof_pcm_period_elapsed_work(struct work_struct *work);
+void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream);
+void snd_sof_compr_init_elapsed_work(struct work_struct *work);
+#else
+static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { }
+static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { }
+#endif
/*
* Mixer IPC
@@ -220,7 +255,8 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */
-int sof_restore_pipelines(struct device *dev);
+int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify);
+int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify);
int sof_set_hw_params_upon_resume(struct device *dev);
bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
@@ -229,4 +265,10 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+
+/* PCM */
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
+int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
#endif
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index d1a21edfa05d..885430a42226 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -7,14 +7,20 @@
#include <linux/firmware.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/pm_runtime.h>
#include <sound/sof.h>
#include "ops.h"
+#include "imx/imx-ops.h"
-extern struct snd_sof_dsp_ops sof_imx8_ops;
-extern struct snd_sof_dsp_ops sof_imx8x_ops;
-extern struct snd_sof_dsp_ops sof_imx8m_ops;
+static char *fw_path;
+module_param(fw_path, charp, 0444);
+MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware.");
+
+static char *tplg_path;
+module_param(tplg_path, charp, 0444);
+MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
/* platform specific devices */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8)
@@ -90,9 +96,15 @@ static int sof_of_probe(struct platform_device *pdev)
sof_pdata->dev = &pdev->dev;
sof_pdata->fw_filename = desc->default_fw_filename;
- /* TODO: read alternate fw and tplg filenames from DT */
- sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path;
- sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path;
+ if (fw_path)
+ sof_pdata->fw_filename_prefix = fw_path;
+ else
+ sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path;
+
+ if (tplg_path)
+ sof_pdata->tplg_filename_prefix = tplg_path;
+ else
+ sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path;
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_of_probe_complete;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index fd8423172d8f..ba341b1bda0c 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -23,13 +23,23 @@
/* debug flags */
#define SOF_DBG_ENABLE_TRACE BIT(0)
#define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */
+#define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */
+#define SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE BIT(3) /* 0: use topology token
+ * 1: override topology
+ */
+#define SOF_DBG_DYNAMIC_PIPELINES_ENABLE BIT(4) /* 0: use static pipelines
+ * 1: use dynamic pipelines
+ */
+#define SOF_DBG_DISABLE_MULTICORE BIT(5) /* schedule all pipelines/widgets
+ * on primary core
+ */
+#define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */
#define SOF_DBG_DUMP_REGS BIT(0)
#define SOF_DBG_DUMP_MBOX BIT(1)
#define SOF_DBG_DUMP_TEXT BIT(2)
#define SOF_DBG_DUMP_PCI BIT(3)
-#define SOF_DBG_DUMP_FORCE_ERR_LEVEL BIT(4) /* used to dump dsp status with error log level */
-
+#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* only dump if SOF_DBG_PRINT_ALL_DUMPS is set */
/* global debug state set by SOF_DBG_ flags */
extern int sof_core_debug;
@@ -83,6 +93,16 @@ enum sof_system_suspend_state {
SOF_SUSPEND_S3,
};
+enum sof_dfsentry_type {
+ SOF_DFSENTRY_TYPE_IOMEM = 0,
+ SOF_DFSENTRY_TYPE_BUF,
+};
+
+enum sof_debugfs_access_type {
+ SOF_DEBUGFS_ACCESS_ALWAYS = 0,
+ SOF_DEBUGFS_ACCESS_D0_ONLY,
+};
+
struct snd_sof_dev;
struct snd_sof_ipc_msg;
struct snd_sof_ipc;
@@ -127,12 +147,20 @@ struct snd_sof_dsp_ops {
void __iomem *addr); /* optional */
/* memcpy IO */
- void (*block_read)(struct snd_sof_dev *sof_dev, u32 bar,
- u32 offset, void *dest,
- size_t size); /* mandatory */
- void (*block_write)(struct snd_sof_dev *sof_dev, u32 bar,
- u32 offset, void *src,
- size_t size); /* mandatory */
+ int (*block_read)(struct snd_sof_dev *sof_dev,
+ enum snd_sof_fw_blk_type type, u32 offset,
+ void *dest, size_t size); /* mandatory */
+ int (*block_write)(struct snd_sof_dev *sof_dev,
+ enum snd_sof_fw_blk_type type, u32 offset,
+ void *src, size_t size); /* mandatory */
+
+ /* Mailbox IO */
+ void (*mailbox_read)(struct snd_sof_dev *sof_dev,
+ u32 offset, void *dest,
+ size_t size); /* optional */
+ void (*mailbox_write)(struct snd_sof_dev *sof_dev,
+ u32 offset, void *src,
+ size_t size); /* optional */
/* doorbell */
irqreturn_t (*irq_handler)(int irq, void *context); /* optional */
@@ -200,9 +228,9 @@ struct snd_sof_dsp_ops {
#endif
/* host read DSP stream data */
- void (*ipc_msg_data)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz); /* mandatory */
+ int (*ipc_msg_data)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz); /* mandatory */
/* host configure DSP HW parameters */
int (*ipc_pcm_params)(struct snd_sof_dev *sdev,
@@ -237,6 +265,10 @@ struct snd_sof_dsp_ops {
void (*dbg_dump)(struct snd_sof_dev *sof_dev,
u32 flags); /* optional */
void (*ipc_dump)(struct snd_sof_dev *sof_dev); /* optional */
+ int (*debugfs_add_region_item)(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type, u32 offset,
+ size_t size, const char *name,
+ enum sof_debugfs_access_type access_type); /* optional */
/* host DMA trace initialization */
int (*trace_init)(struct snd_sof_dev *sdev,
@@ -268,33 +300,17 @@ struct snd_sof_dsp_ops {
/* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
u32 hw_info;
- const struct sof_arch_ops *arch_ops;
+ const struct dsp_arch_ops *dsp_arch_ops;
};
/* DSP architecture specific callbacks for oops and stack dumps */
-struct sof_arch_ops {
+struct dsp_arch_ops {
void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops);
void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops,
u32 *stack, u32 stack_words);
};
-#define sof_arch_ops(sdev) ((sdev)->pdata->desc->ops->arch_ops)
-
-/* DSP device HW descriptor mapping between bus ID and ops */
-struct sof_ops_table {
- const struct sof_dev_desc *desc;
- const struct snd_sof_dsp_ops *ops;
-};
-
-enum sof_dfsentry_type {
- SOF_DFSENTRY_TYPE_IOMEM = 0,
- SOF_DFSENTRY_TYPE_BUF,
-};
-
-enum sof_debugfs_access_type {
- SOF_DEBUGFS_ACCESS_ALWAYS = 0,
- SOF_DEBUGFS_ACCESS_D0_ONLY,
-};
+#define sof_dsp_arch_ops(sdev) ((sdev)->pdata->desc->ops->dsp_arch_ops)
/* FS entry for debug files that can expose DSP memories, registers */
struct snd_sof_dfsentry {
@@ -413,6 +429,8 @@ struct snd_sof_dev {
/* debug */
struct dentry *debugfs_root;
struct list_head dfsentry_list;
+ bool dbg_dump_printed;
+ bool ipc_dump_printed;
/* firmware loader */
struct snd_dma_buffer dmab;
@@ -485,14 +503,12 @@ int snd_sof_create_page_table(struct device *dev,
/*
* Firmware loading.
*/
-int snd_sof_load_firmware(struct snd_sof_dev *sdev);
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev);
int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev);
int snd_sof_run_firmware(struct snd_sof_dev *sdev);
int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
struct snd_sof_mod_hdr *module);
void snd_sof_fw_unload(struct snd_sof_dev *sdev);
-int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset);
/*
* IPC low level APIs.
@@ -503,9 +519,6 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev);
int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev,
struct sof_ipc_pcm_params *params);
-int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox,
- size_t dspbox_size, u32 hostbox,
- size_t hostbox_size);
int snd_sof_ipc_valid(struct snd_sof_dev *sdev);
int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
void *msg_data, size_t msg_bytes, void *reply_data,
@@ -513,6 +526,7 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes);
+int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev);
/*
* Trace/debug
@@ -522,10 +536,6 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev);
void snd_sof_free_trace(struct snd_sof_dev *sdev);
int snd_sof_dbg_init(struct snd_sof_dev *sdev);
void snd_sof_free_debug(struct snd_sof_dev *sdev);
-int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
- void __iomem *base, size_t size,
- const char *name,
- enum sof_debugfs_access_type access_type);
int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
void *base, size_t size,
const char *name, mode_t mode);
@@ -539,11 +549,9 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev);
void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev);
int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev);
-
-/*
- * Platform specific ops.
- */
-extern struct snd_compress_ops sof_compressed_ops;
+int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size,
+ const char *name, enum sof_debugfs_access_type access_type);
/*
* DSP Architectures.
@@ -551,16 +559,29 @@ extern struct snd_compress_ops sof_compressed_ops;
static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
u32 stack_words)
{
- sof_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words);
+ sof_dsp_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words);
}
static inline void sof_oops(struct snd_sof_dev *sdev, void *oops)
{
- if (sof_arch_ops(sdev)->dsp_oops)
- sof_arch_ops(sdev)->dsp_oops(sdev, oops);
+ if (sof_dsp_arch_ops(sdev)->dsp_oops)
+ sof_dsp_arch_ops(sdev)->dsp_oops(sdev, oops);
}
-extern const struct sof_arch_ops sof_xtensa_arch_ops;
+extern const struct dsp_arch_ops sof_xtensa_arch_ops;
+
+/*
+ * Firmware state tracking
+ */
+static inline void sof_set_fw_state(struct snd_sof_dev *sdev,
+ enum snd_sof_fw_state new_state)
+{
+ if (sdev->fw_state == new_state)
+ return;
+
+ dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
+ sdev->fw_state = new_state;
+}
/*
* Utilities
@@ -573,33 +594,24 @@ void sof_mailbox_write(struct snd_sof_dev *sdev, u32 offset,
void *message, size_t bytes);
void sof_mailbox_read(struct snd_sof_dev *sdev, u32 offset,
void *message, size_t bytes);
-void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src,
- size_t size);
-void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest,
- size_t size);
+int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *src, size_t size);
+int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *dest, size_t size);
int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id);
-void intel_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz);
-int intel_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply);
+int sof_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz);
+int sof_ipc_pcm_params(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ const struct sof_ipc_pcm_params_reply *reply);
-int intel_pcm_open(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream);
-int intel_pcm_close(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream);
+int sof_stream_pcm_open(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
+int sof_stream_pcm_close(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
int sof_machine_check(struct snd_sof_dev *sdev);
-
-#define sof_dev_dbg_or_err(dev, is_err, fmt, ...) \
- do { \
- if (is_err) \
- dev_err(dev, "error: " fmt, __VA_ARGS__); \
- else \
- dev_dbg(dev, fmt, __VA_ARGS__); \
- } while (0)
-
#endif
diff --git a/sound/soc/sof/probe.c b/sound/soc/sof/sof-probes.c
index 14509f4d3f86..5586af9f1a25 100644
--- a/sound/soc/sof/probe.c
+++ b/sound/soc/sof/sof-probes.c
@@ -3,13 +3,46 @@
// 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) 2019-2020 Intel Corporation. All rights reserved.
-//
+// Copyright(c) 2019-2021 Intel Corporation. All rights reserved.
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
+#include <sound/soc.h>
+#include "ops.h"
#include "sof-priv.h"
-#include "probe.h"
+#include "sof-probes.h"
+
+struct sof_probe_dma {
+ unsigned int stream_tag;
+ unsigned int dma_buffer_size;
+} __packed;
+
+struct sof_ipc_probe_dma_add_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ struct sof_probe_dma dma[];
+} __packed;
+
+struct sof_ipc_probe_info_params {
+ struct sof_ipc_reply rhdr;
+ unsigned int num_elems;
+ union {
+ struct sof_probe_dma dma[0];
+ struct sof_probe_point_desc desc[0];
+ };
+} __packed;
+
+struct sof_ipc_probe_point_add_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ struct sof_probe_point_desc desc[];
+} __packed;
+
+struct sof_ipc_probe_point_remove_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ unsigned int buffer_id[];
+} __packed;
/**
* sof_ipc_probe_init - initialize data probing
@@ -25,8 +58,8 @@
* Probing is initialized only once and each INIT request must be
* matched by DEINIT call.
*/
-int sof_ipc_probe_init(struct snd_sof_dev *sdev,
- u32 stream_tag, size_t buffer_size)
+static int sof_ipc_probe_init(struct snd_sof_dev *sdev, u32 stream_tag,
+ size_t buffer_size)
{
struct sof_ipc_probe_dma_add_params *msg;
struct sof_ipc_reply reply;
@@ -47,7 +80,6 @@ int sof_ipc_probe_init(struct snd_sof_dev *sdev,
kfree(msg);
return ret;
}
-EXPORT_SYMBOL(sof_ipc_probe_init);
/**
* sof_ipc_probe_deinit - cleanup after data probing
@@ -57,7 +89,7 @@ EXPORT_SYMBOL(sof_ipc_probe_init);
* on DSP side once it is no longer needed. DEINIT only when there
* are no probes connected and with all injectors detached.
*/
-int sof_ipc_probe_deinit(struct snd_sof_dev *sdev)
+static int sof_ipc_probe_deinit(struct snd_sof_dev *sdev)
{
struct sof_ipc_cmd_hdr msg;
struct sof_ipc_reply reply;
@@ -68,10 +100,9 @@ int sof_ipc_probe_deinit(struct snd_sof_dev *sdev)
return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size,
&reply, sizeof(reply));
}
-EXPORT_SYMBOL(sof_ipc_probe_deinit);
static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd,
- void **params, size_t *num_params)
+ void **params, size_t *num_params)
{
struct sof_ipc_probe_info_params msg = {{{0}}};
struct sof_ipc_probe_info_params *reply;
@@ -113,97 +144,6 @@ exit:
}
/**
- * sof_ipc_probe_dma_info - retrieve list of active injection dmas
- * @sdev: SOF sound device
- * @dma: Returned list of active dmas
- * @num_dma: Returned count of active dmas
- *
- * Host sends DMA_INFO request to obtain list of injection dmas it
- * can use to transfer data over with.
- *
- * Note that list contains only injection dmas as there is only one
- * extractor (dma) and it is always assigned on probing init.
- * DSP knows exactly where data from extraction probes is going to,
- * which is not the case for injection where multiple streams
- * could be engaged.
- */
-int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev,
- struct sof_probe_dma **dma, size_t *num_dma)
-{
- return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_DMA_INFO,
- (void **)dma, num_dma);
-}
-EXPORT_SYMBOL(sof_ipc_probe_dma_info);
-
-/**
- * sof_ipc_probe_dma_add - attach to specified dmas
- * @sdev: SOF sound device
- * @dma: List of streams (dmas) to attach to
- * @num_dma: Number of elements in @dma
- *
- * Contrary to extraction, injection streams are never assigned
- * on init. Before attempting any data injection, host is responsible
- * for specifying streams which will be later used to transfer data
- * to connected probe points.
- */
-int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev,
- struct sof_probe_dma *dma, size_t num_dma)
-{
- struct sof_ipc_probe_dma_add_params *msg;
- struct sof_ipc_reply reply;
- size_t size = struct_size(msg, dma, num_dma);
- int ret;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- msg->hdr.size = size;
- msg->num_elems = num_dma;
- msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_ADD;
- memcpy(&msg->dma[0], dma, size - sizeof(*msg));
-
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- kfree(msg);
- return ret;
-}
-EXPORT_SYMBOL(sof_ipc_probe_dma_add);
-
-/**
- * sof_ipc_probe_dma_remove - detach from specified dmas
- * @sdev: SOF sound device
- * @stream_tag: List of stream tags to detach from
- * @num_stream_tag: Number of elements in @stream_tag
- *
- * Host sends DMA_REMOVE request to free previously attached stream
- * from being occupied for injection. Each detach operation should
- * match equivalent DMA_ADD. Detach only when all probes tied to
- * given stream have been disconnected.
- */
-int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev,
- unsigned int *stream_tag, size_t num_stream_tag)
-{
- struct sof_ipc_probe_dma_remove_params *msg;
- struct sof_ipc_reply reply;
- size_t size = struct_size(msg, stream_tag, num_stream_tag);
- int ret;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- msg->hdr.size = size;
- msg->num_elems = num_stream_tag;
- msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_REMOVE;
- memcpy(&msg->stream_tag[0], stream_tag, size - sizeof(*msg));
-
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- kfree(msg);
- return ret;
-}
-EXPORT_SYMBOL(sof_ipc_probe_dma_remove);
-
-/**
* sof_ipc_probe_points_info - retrieve list of active probe points
* @sdev: SOF sound device
* @desc: Returned list of active probes
@@ -214,7 +154,8 @@ EXPORT_SYMBOL(sof_ipc_probe_dma_remove);
* required.
*/
int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc **desc, size_t *num_desc)
+ struct sof_probe_point_desc **desc,
+ size_t *num_desc)
{
return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO,
(void **)desc, num_desc);
@@ -235,7 +176,7 @@ EXPORT_SYMBOL(sof_ipc_probe_points_info);
* request when no longer needed.
*/
int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc *desc, size_t num_desc)
+ struct sof_probe_point_desc *desc, size_t num_desc)
{
struct sof_ipc_probe_point_add_params *msg;
struct sof_ipc_reply reply;
@@ -267,7 +208,7 @@ EXPORT_SYMBOL(sof_ipc_probe_points_add);
* points and frees all resources on DSP side.
*/
int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
- unsigned int *buffer_id, size_t num_buffer_id)
+ unsigned int *buffer_id, size_t num_buffer_id)
{
struct sof_ipc_probe_point_remove_params *msg;
struct sof_ipc_reply reply;
@@ -288,3 +229,136 @@ int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
return ret;
}
EXPORT_SYMBOL(sof_ipc_probe_points_remove);
+
+static int sof_probe_compr_startup(struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ ret = snd_sof_probe_compr_assign(sdev, cstream, dai);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
+ return ret;
+ }
+
+ sdev->extractor_stream_tag = ret;
+ return 0;
+}
+
+static int sof_probe_compr_shutdown(struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ struct sof_probe_point_desc *desc;
+ size_t num_desc;
+ int i, ret;
+
+ /* disconnect all probe points */
+ ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++)
+ sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1);
+ kfree(desc);
+
+exit:
+ ret = sof_ipc_probe_deinit(sdev);
+ if (ret < 0)
+ dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
+
+ sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
+ snd_compr_free_pages(cstream);
+
+ return snd_sof_probe_compr_free(sdev, cstream, dai);
+}
+
+static int sof_probe_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ int ret;
+
+ cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+ cstream->dma_buffer.dev.dev = sdev->dev;
+ ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai);
+ if (ret < 0)
+ return ret;
+
+ ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag,
+ rtd->dma_bytes);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to init probe: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+
+ return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai);
+}
+
+static int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+
+ return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai);
+}
+
+struct snd_soc_cdai_ops sof_probe_compr_ops = {
+ .startup = sof_probe_compr_startup,
+ .shutdown = sof_probe_compr_shutdown,
+ .set_params = sof_probe_compr_set_params,
+ .trigger = sof_probe_compr_trigger,
+ .pointer = sof_probe_compr_pointer,
+};
+EXPORT_SYMBOL(sof_probe_compr_ops);
+
+static int sof_probe_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ unsigned int offset, n;
+ void *ptr;
+ int ret;
+
+ if (count > rtd->buffer_size)
+ count = rtd->buffer_size;
+
+ div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
+ ptr = rtd->dma_area + offset;
+ n = rtd->buffer_size - offset;
+
+ if (count < n) {
+ ret = copy_to_user(buf, ptr, count);
+ } else {
+ ret = copy_to_user(buf, ptr, n);
+ ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+ }
+
+ if (ret)
+ return count - ret;
+ return count;
+}
+
+const struct snd_compress_ops sof_probe_compressed_ops = {
+ .copy = sof_probe_compr_copy,
+};
+EXPORT_SYMBOL(sof_probe_compressed_ops);
diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h
new file mode 100644
index 000000000000..35e1dd8d9e03
--- /dev/null
+++ b/sound/soc/sof/sof-probes.h
@@ -0,0 +1,38 @@
+/* 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) 2019-2021 Intel Corporation. All rights reserved.
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SOF_PROBES_H
+#define __SOF_PROBES_H
+
+#include <sound/compress_driver.h>
+#include <sound/sof/header.h>
+
+struct snd_sof_dev;
+
+#define SOF_PROBE_INVALID_NODE_ID UINT_MAX
+
+struct sof_probe_point_desc {
+ unsigned int buffer_id;
+ unsigned int purpose;
+ unsigned int stream_tag;
+} __packed;
+
+int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
+ struct sof_probe_point_desc **desc,
+ size_t *num_desc);
+int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
+ struct sof_probe_point_desc *desc,
+ size_t num_desc);
+int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
+ unsigned int *buffer_id, size_t num_buffer_id);
+
+extern struct snd_soc_cdai_ops sof_probe_compr_ops;
+extern const struct snd_compress_ops sof_probe_compressed_ops;
+
+#endif
diff --git a/sound/soc/sof/intel/intel-ipc.c b/sound/soc/sof/stream-ipc.c
index de66f8a82a07..15a55851faeb 100644
--- a/sound/soc/sof/intel/intel-ipc.c
+++ b/sound/soc/sof/stream-ipc.c
@@ -7,7 +7,7 @@
//
// Authors: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
-/* Intel-specific SOF IPC code */
+/* Generic SOF IPC code */
#include <linux/device.h>
#include <linux/export.h>
@@ -17,35 +17,39 @@
#include <sound/pcm.h>
#include <sound/sof/stream.h>
-#include "../ops.h"
-#include "../sof-priv.h"
+#include "ops.h"
+#include "sof-priv.h"
-struct intel_stream {
+struct sof_stream {
size_t posn_offset;
};
-/* Mailbox-based Intel IPC implementation */
-void intel_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
+/* Mailbox-based Generic IPC implementation */
+int sof_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
{
if (!substream || !sdev->stream_box.size) {
- sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+ snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
- struct intel_stream *stream = substream->runtime->private_data;
+ struct sof_stream *stream = substream->runtime->private_data;
/* The stream might already be closed */
- if (stream)
- sof_mailbox_read(sdev, stream->posn_offset, p, sz);
+ if (!stream)
+ return -ESTRPIPE;
+
+ snd_sof_dsp_mailbox_read(sdev, stream->posn_offset, p, sz);
}
+
+ return 0;
}
-EXPORT_SYMBOL_NS(intel_ipc_msg_data, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
+EXPORT_SYMBOL(sof_ipc_msg_data);
-int intel_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
+int sof_ipc_pcm_params(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ const struct sof_ipc_pcm_params_reply *reply)
{
- struct intel_stream *stream = substream->runtime->private_data;
+ struct sof_stream *stream = substream->runtime->private_data;
size_t posn_offset = reply->posn_offset;
/* check if offset is overflow or it is not aligned */
@@ -60,12 +64,12 @@ int intel_ipc_pcm_params(struct snd_sof_dev *sdev,
return 0;
}
-EXPORT_SYMBOL_NS(intel_ipc_pcm_params, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
+EXPORT_SYMBOL(sof_ipc_pcm_params);
-int intel_pcm_open(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream)
+int sof_stream_pcm_open(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
{
- struct intel_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+ struct sof_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
@@ -82,18 +86,18 @@ int intel_pcm_open(struct snd_sof_dev *sdev,
return 0;
}
-EXPORT_SYMBOL_NS(intel_pcm_open, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
+EXPORT_SYMBOL(sof_stream_pcm_open);
-int intel_pcm_close(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream)
+int sof_stream_pcm_close(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
{
- struct intel_stream *stream = substream->runtime->private_data;
+ struct sof_stream *stream = substream->runtime->private_data;
substream->runtime->private_data = NULL;
kfree(stream);
return 0;
}
-EXPORT_SYMBOL_NS(intel_pcm_close, SND_SOC_SOF_INTEL_HIFI_EP_IPC);
+EXPORT_SYMBOL(sof_stream_pcm_close);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index cc9585bfa4e9..bb9e62bbe5db 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -572,6 +572,12 @@ static const struct sof_topology_token sched_tokens[] = {
offsetof(struct sof_ipc_pipe_new, time_domain), 0},
};
+static const struct sof_topology_token pipeline_tokens[] = {
+ {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0},
+
+};
+
/* volume */
static const struct sof_topology_token volume_tokens[] = {
{SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
@@ -1250,6 +1256,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index,
return -ENOMEM;
scontrol->scomp = scomp;
+ scontrol->access = kc->access;
switch (le32_to_cpu(hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
@@ -1512,10 +1519,8 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget,
static int sof_widget_load_dai(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r,
struct snd_sof_dai *dai)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_dai *comp_dai;
size_t ipc_size = sizeof(*comp_dai);
@@ -1552,10 +1557,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index,
swidget->widget->name, comp_dai->type, comp_dai->dai_index);
sof_dbg_comp_config(scomp, &comp_dai->config);
- ret = sof_ipc_tx_message(sdev->ipc, comp_dai->comp.hdr.cmd,
- comp_dai, ipc_size, r, sizeof(*r));
-
- if (ret == 0 && dai) {
+ if (dai) {
dai->scomp = scomp;
/*
@@ -1577,10 +1579,8 @@ finish:
static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_buffer *buffer;
int ret;
@@ -1612,15 +1612,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index,
swidget->private = buffer;
- ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer,
- sizeof(*buffer), r, sizeof(*r));
- if (ret < 0) {
- dev_err(scomp->dev, "error: buffer %s load failed\n",
- swidget->widget->name);
- kfree(buffer);
- }
-
- return ret;
+ return 0;
}
/* bind PCM ID to host component ID */
@@ -1649,10 +1641,8 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
enum sof_ipc_stream_direction dir,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_host *host;
size_t ipc_size = sizeof(*host);
@@ -1691,10 +1681,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
swidget->private = host;
- ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(host);
return ret;
@@ -1703,11 +1690,10 @@ err:
/*
* Pipeline Topology
*/
-int sof_load_pipeline_ipc(struct device *dev,
+int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
struct sof_ipc_pipe_new *pipeline,
struct sof_ipc_comp_reply *r)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
int ret = sof_core_enable(sdev, pipeline->core);
if (ret < 0)
@@ -1716,15 +1702,14 @@ int sof_load_pipeline_ipc(struct device *dev,
ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
sizeof(*pipeline), r, sizeof(*r));
if (ret < 0)
- dev_err(dev, "error: load pipeline ipc failure\n");
+ dev_err(sdev->dev, "error: load pipeline ipc failure\n");
return ret;
}
static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_pipe_new *pipeline;
@@ -1764,16 +1749,30 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
goto err;
}
- dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n",
+ ret = sof_parse_tokens(scomp, swidget, pipeline_tokens,
+ ARRAY_SIZE(pipeline_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n",
+ private->size);
+ goto err;
+ }
+
+ if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE)
+ pipeline->core = SOF_DSP_PRIMARY_CORE;
+
+ if (sof_core_debug & SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)
+ swidget->dynamic_pipeline_widget = sof_core_debug &
+ SOF_DBG_DYNAMIC_PIPELINES_ENABLE;
+
+ dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n",
swidget->widget->name, pipeline->period, pipeline->priority,
- pipeline->period_mips, pipeline->core, pipeline->frames_per_sched);
+ pipeline->period_mips, pipeline->core, pipeline->frames_per_sched,
+ swidget->dynamic_pipeline_widget);
swidget->private = pipeline;
- /* send ipc's to create pipeline comp and power up schedule core */
- ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r);
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(pipeline);
return ret;
@@ -1785,10 +1784,8 @@ err:
static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_mixer *mixer;
size_t ipc_size = sizeof(*mixer);
@@ -1817,12 +1814,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
swidget->private = mixer;
- ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer,
- ipc_size, r, sizeof(*r));
- if (ret < 0)
- kfree(mixer);
-
- return ret;
+ return 0;
}
/*
@@ -1830,10 +1822,8 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
*/
static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_mux *mux;
size_t ipc_size = sizeof(*mux);
@@ -1862,12 +1852,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
swidget->private = mux;
- ret = sof_ipc_tx_message(sdev->ipc, mux->comp.hdr.cmd, mux,
- ipc_size, r, sizeof(*r));
- if (ret < 0)
- kfree(mux);
-
- return ret;
+ return 0;
}
/*
@@ -1876,8 +1861,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
@@ -1937,10 +1921,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
}
}
- ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(volume);
return ret;
@@ -1952,10 +1933,8 @@ err:
static int sof_widget_load_src(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_src *src;
size_t ipc_size = sizeof(*src);
@@ -1994,10 +1973,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index,
swidget->private = src;
- ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(src);
return ret;
@@ -2009,10 +1985,8 @@ err:
static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_asrc *asrc;
size_t ipc_size = sizeof(*asrc);
@@ -2053,10 +2027,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index,
swidget->private = asrc;
- ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(asrc);
return ret;
@@ -2068,10 +2039,8 @@ err:
static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_tone *tone;
size_t ipc_size = sizeof(*tone);
@@ -2110,10 +2079,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index,
swidget->private = tone;
- ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(tone);
return ret;
@@ -2195,10 +2161,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
static int sof_process_load(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r,
int type)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_dapm_widget *widget = swidget->widget;
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_process *process;
@@ -2272,33 +2236,6 @@ static int sof_process_load(struct snd_soc_component *scomp, int index,
process->size = ipc_data_size;
swidget->private = process;
-
- ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process,
- ipc_size, r, sizeof(*r));
-
- if (ret < 0) {
- dev_err(scomp->dev, "error: create process failed\n");
- goto err;
- }
-
- /* we sent the data in single message so return */
- if (ipc_data_size)
- goto out;
-
- /* send control data with large message supported method */
- for (i = 0; i < widget->num_kcontrols; i++) {
- wdata[i].control->readback_offset = 0;
- ret = snd_sof_ipc_set_get_comp_data(wdata[i].control,
- wdata[i].ipc_cmd,
- wdata[i].ctrl_type,
- wdata[i].control->cmd,
- true);
- if (ret != 0) {
- dev_err(scomp->dev, "error: send control failed\n");
- break;
- }
- }
-
err:
if (ret < 0)
kfree(process);
@@ -2314,8 +2251,7 @@ out:
static int sof_widget_load_process(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_process config;
@@ -2341,8 +2277,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index,
}
/* now load process specific data and send IPC */
- ret = sof_process_load(scomp, index, swidget, tw, r,
- find_process_comp_type(config.type));
+ ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type));
if (ret < 0) {
dev_err(scomp->dev, "error: process loading failed\n");
return ret;
@@ -2391,8 +2326,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_widget *swidget;
struct snd_sof_dai *dai;
- struct sof_ipc_comp_reply reply;
- struct snd_sof_control *scontrol;
struct sof_ipc_comp comp = {
.core = SOF_DSP_PRIMARY_CORE,
};
@@ -2409,7 +2342,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->id = w->id;
swidget->pipeline_id = index;
swidget->private = NULL;
- memset(&reply, 0, sizeof(reply));
dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n",
swidget->comp_id, index, swidget->id, tw->name,
@@ -2426,15 +2358,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
return ret;
}
- swidget->core = comp.core;
+ if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE)
+ comp.core = SOF_DSP_PRIMARY_CORE;
- /* default is primary core, safe to call for already enabled cores */
- ret = sof_core_enable(sdev, comp.core);
- if (ret < 0) {
- dev_err(scomp->dev, "error: enable core: %d\n", ret);
- kfree(swidget);
- return ret;
- }
+ swidget->core = comp.core;
ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens,
ARRAY_SIZE(comp_ext_tokens), tw->priv.array,
@@ -2456,57 +2383,51 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
return -ENOMEM;
}
- ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, dai);
- if (ret == 0) {
- sof_connect_dai_widget(scomp, w, tw, dai);
- list_add(&dai->list, &sdev->dai_list);
- swidget->private = dai;
- } else {
+ ret = sof_widget_load_dai(scomp, index, swidget, tw, dai);
+ if (!ret)
+ ret = sof_connect_dai_widget(scomp, w, tw, dai);
+ if (ret < 0) {
kfree(dai);
+ break;
}
+ list_add(&dai->list, &sdev->dai_list);
+ swidget->private = dai;
break;
case snd_soc_dapm_mixer:
- ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_mixer(scomp, index, swidget, tw);
break;
case snd_soc_dapm_pga:
- ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply);
- /* Find scontrol for this pga and set readback offset*/
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- if (scontrol->comp_id == swidget->comp_id) {
- scontrol->readback_offset = reply.offset;
- break;
- }
- }
+ ret = sof_widget_load_pga(scomp, index, swidget, tw);
break;
case snd_soc_dapm_buffer:
- ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_buffer(scomp, index, swidget, tw);
break;
case snd_soc_dapm_scheduler:
- ret = sof_widget_load_pipeline(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_pipeline(scomp, index, swidget, tw);
break;
case snd_soc_dapm_aif_out:
ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_CAPTURE, tw, &reply);
+ SOF_IPC_STREAM_CAPTURE, tw);
break;
case snd_soc_dapm_aif_in:
ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_PLAYBACK, tw, &reply);
+ SOF_IPC_STREAM_PLAYBACK, tw);
break;
case snd_soc_dapm_src:
- ret = sof_widget_load_src(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_src(scomp, index, swidget, tw);
break;
case snd_soc_dapm_asrc:
- ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_asrc(scomp, index, swidget, tw);
break;
case snd_soc_dapm_siggen:
- ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_siggen(scomp, index, swidget, tw);
break;
case snd_soc_dapm_effect:
- ret = sof_widget_load_process(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_process(scomp, index, swidget, tw);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
- ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_mux(scomp, index, swidget, tw);
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_dai_link:
@@ -2517,12 +2438,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
}
/* check IPC reply */
- if (ret < 0 || reply.rhdr.error < 0) {
+ if (ret < 0) {
dev_err(scomp->dev,
- "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n",
+ "error: failed to add widget id %d type %d name : %s stream %s\n",
tw->shift, swidget->id, tw->name,
strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
- ? tw->sname : "none", reply.rhdr.error);
+ ? tw->sname : "none");
kfree(swidget);
return ret;
}
@@ -2598,6 +2519,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
/* power down the pipeline schedule core */
pipeline = swidget->private;
+
+ /*
+ * Runtime PM should still function normally if topology loading fails and
+ * it's components are unloaded. Do not power down the primary core so that the
+ * CTX_SAVE IPC can succeed during runtime suspend.
+ */
+ if (pipeline->core == SOF_DSP_PRIMARY_CORE)
+ break;
+
ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core);
if (ret < 0)
dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n",
@@ -2671,8 +2601,10 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
for_each_pcm_streams(stream) {
spcm->stream[stream].comp_id = COMP_ID_UNASSIGNED;
- INIT_WORK(&spcm->stream[stream].period_elapsed_work,
- snd_sof_pcm_period_elapsed_work);
+ if (pcm->compress)
+ snd_sof_compr_init_elapsed_work(&spcm->stream[stream].period_elapsed_work);
+ else
+ snd_sof_pcm_init_elapsed_work(&spcm->stream[stream].period_elapsed_work);
}
spcm->pcm = *pcm;
@@ -2828,9 +2760,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
continue;
if (strcmp(link->name, dai->name) == 0) {
- struct sof_ipc_reply reply;
- int ret;
-
/*
* the same dai config will be applied to all DAIs in
* the same dai link. We have to ensure that the ipc
@@ -2842,18 +2771,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
dev_dbg(sdev->dev, "set DAI config for %s index %d\n",
dai->name, config[curr_conf].dai_index);
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config[curr_conf].hdr.cmd,
- &config[curr_conf], size,
- &reply, sizeof(reply));
-
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to set DAI config for %s index %d\n",
- dai->name, config[curr_conf].dai_index);
- return ret;
- }
dai->number_configs = num_conf;
dai->current_config = curr_conf;
@@ -2861,9 +2778,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
if (!dai->dai_config)
return -ENOMEM;
- /* set cpu_dai_name */
- dai->cpu_dai_name = link->cpus->dai_name;
-
found = 1;
}
}
@@ -2933,12 +2847,12 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots);
config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots);
- dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n",
+ dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n",
config[i].dai_index, config[i].format,
config[i].ssp.mclk_rate, config[i].ssp.bclk_rate,
config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits,
config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots,
- config[i].ssp.mclk_id, config[i].ssp.quirks);
+ config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control);
/* validate SSP fsync rate and channel count */
if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) {
@@ -3383,7 +3297,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *source_swidget, *sink_swidget;
struct snd_soc_dobj *dobj = &route->dobj;
struct snd_sof_route *sroute;
- struct sof_ipc_reply reply;
int ret = 0;
/* allocate memory for sroute and connect */
@@ -3458,33 +3371,11 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
route->source, route->sink);
goto err;
} else {
- ret = sof_ipc_tx_message(sdev->ipc,
- connect->hdr.cmd,
- connect, sizeof(*connect),
- &reply, sizeof(reply));
-
- /* check IPC return value */
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n",
- route->sink,
- route->control ? route->control : "none",
- route->source);
- goto err;
- }
-
- /* check IPC reply */
- if (reply.error < 0) {
- dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n",
- route->sink,
- route->control ? route->control : "none",
- route->source, reply.error);
- ret = reply.error;
- goto err;
- }
-
sroute->route = route;
dobj->private = sroute;
sroute->private = connect;
+ sroute->src_widget = source_swidget;
+ sroute->sink_widget = sink_swidget;
/* add route to route list */
list_add(&sroute->list, &sdev->route_list);
@@ -3498,59 +3389,14 @@ err:
return ret;
}
-/* Function to set the initial value of SOF kcontrols.
- * The value will be stored in scontrol->control_data
- */
-static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_control *scontrol = NULL;
- int ipc_cmd, ctrl_type;
- int ret = 0;
-
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
-
- /* notify DSP of kcontrol values */
- switch (scontrol->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_ENUM:
- case SOF_CTRL_CMD_SWITCH:
- ipc_cmd = SOF_IPC_COMP_GET_VALUE;
- ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET;
- break;
- case SOF_CTRL_CMD_BINARY:
- ipc_cmd = SOF_IPC_COMP_GET_DATA;
- ctrl_type = SOF_CTRL_TYPE_DATA_GET;
- break;
- default:
- dev_err(scomp->dev,
- "error: Invalid scontrol->cmd: %d\n",
- scontrol->cmd);
- return -EINVAL;
- }
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- false);
- if (ret < 0) {
- dev_warn(scomp->dev,
- "error: kcontrol value get for widget: %d\n",
- scontrol->comp_id);
- }
- }
-
- return ret;
-}
-
-int snd_sof_complete_pipeline(struct device *dev,
+int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct sof_ipc_pipe_ready ready;
struct sof_ipc_reply reply;
int ret;
- dev_dbg(dev, "tplg: complete pipeline %s id %d\n",
+ dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
swidget->widget->name, swidget->comp_id);
memset(&ready, 0, sizeof(ready));
@@ -3566,31 +3412,84 @@ int snd_sof_complete_pipeline(struct device *dev,
return 1;
}
+/**
+ * sof_set_pipe_widget - Set pipe_widget for a component
+ * @sdev: pointer to struct snd_sof_dev
+ * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
+ *
+ * Return: 0 if successful, -EINVAL on error.
+ * The function checks if @swidget is associated with any volatile controls. If so, setting
+ * the dynamic_pipeline_widget is disallowed.
+ */
+static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
+ struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+
+ if (pipe_widget->dynamic_pipeline_widget) {
+ /* dynamic widgets cannot have volatile kcontrols */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id &&
+ (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) {
+ dev_err(sdev->dev,
+ "error: volatile control found for dynamic widget %s\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+ }
+
+ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
+ swidget->pipe_widget = pipe_widget;
+ swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
+
+ return 0;
+}
+
/* completion - called at completion of firmware loading */
-static void sof_complete(struct snd_soc_component *scomp)
+static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_widget *swidget;
+ struct snd_sof_widget *swidget, *comp_swidget;
+ int ret;
- /* some widget types require completion notificattion */
+ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (swidget->complete)
- continue;
-
switch (swidget->id) {
case snd_soc_dapm_scheduler:
- swidget->complete =
- snd_sof_complete_pipeline(scomp->dev, swidget);
+ /*
+ * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+ * for all widgets that have the same pipeline ID as the scheduler widget
+ */
+ list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list)
+ if (comp_swidget->pipeline_id == swidget->pipeline_id) {
+ ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
+ if (ret < 0)
+ return ret;
+ }
break;
default:
break;
}
}
- /*
- * cache initial values of SOF kcontrols by reading DSP value over
- * IPC. It may be overwritten by alsa-mixer after booting up
- */
- snd_sof_cache_kcontrol_val(scomp);
+
+ /* verify topology components loading including dynamic pipelines */
+ if (sof_core_debug & SOF_DBG_VERIFY_TPLG) {
+ ret = sof_set_up_pipelines(sdev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: topology verification failed %d\n", ret);
+ return ret;
+ }
+
+ ret = sof_tear_down_pipelines(sdev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* set up static pipelines */
+ return sof_set_up_pipelines(sdev, false);
}
/* manifest - optional to inform component of manifest */
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index 58f6ca5cf491..e3afc3dac7d1 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -417,7 +417,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
"error: fail in snd_sof_dma_trace_init %d\n", ret);
return ret;
}
- dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag);
+ dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
/* send IPC to the DSP */
ret = sof_ipc_tx_message(sdev->ipc,
@@ -480,7 +480,8 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev)
goto table_err;
sdev->dma_trace_pages = ret;
- dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages);
+ dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n",
+ __func__, sdev->dma_trace_pages);
if (sdev->first_boot) {
ret = trace_debugfs_create(sdev);
diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c
index 5539d3afbe8f..66fa6602fb67 100644
--- a/sound/soc/sof/utils.c
+++ b/sound/soc/sof/utils.c
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/sof.h>
#include "sof-priv.h"
+#include "ops.h"
/*
* Register IO
@@ -72,15 +73,21 @@ EXPORT_SYMBOL(sof_mailbox_read);
* Memory copy.
*/
-void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src,
- size_t size)
+int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *src, size_t size)
{
- void __iomem *dest = sdev->bar[bar] + offset;
+ int bar = snd_sof_dsp_get_bar_index(sdev, blk_type);
const u8 *src_byte = src;
+ void __iomem *dest;
u32 affected_mask;
u32 tmp;
int m, n;
+ if (bar < 0)
+ return bar;
+
+ dest = sdev->bar[bar] + offset;
+
m = size / 4;
n = size % 4;
@@ -100,15 +107,22 @@ void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src,
tmp |= *(u32 *)(src_byte + m * 4) & affected_mask;
iowrite32(tmp, dest + m * 4);
}
+
+ return 0;
}
EXPORT_SYMBOL(sof_block_write);
-void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest,
- size_t size)
+int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *dest, size_t size)
{
- void __iomem *src = sdev->bar[bar] + offset;
+ int bar = snd_sof_dsp_get_bar_index(sdev, blk_type);
+
+ if (bar < 0)
+ return bar;
+
+ memcpy_fromio(dest, sdev->bar[bar] + offset, size);
- memcpy_fromio(dest, src, size);
+ return 0;
}
EXPORT_SYMBOL(sof_block_read);
diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c
index f6e3411b33cf..bd09c3825caf 100644
--- a/sound/soc/sof/xtensa/core.c
+++ b/sound/soc/sof/xtensa/core.c
@@ -128,7 +128,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
}
}
-const struct sof_arch_ops sof_xtensa_arch_ops = {
+const struct dsp_arch_ops sof_xtensa_arch_ops = {
.dsp_oops = xtensa_dsp_oops,
.dsp_stack = xtensa_stack,
};
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 83c87f35a7d3..cd454871d654 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -108,6 +108,54 @@ config SND_SOC_TEGRA210_ADMAIF
channel. Buffer size is configurable for each ADMAIIF channel.
Say Y or M if you want to add support for Tegra210 ADMAIF module.
+config SND_SOC_TEGRA210_MVC
+ tristate "Tegra210 MVC module"
+ help
+ Config to enable the digital Master Volume Controller (MVC) which
+ provides gain or attenuation to a digital signal path. It can be
+ used in input or output signal path. It can be used either for
+ per-stream volume control or for master volume control.
+ Say Y or M if you want to add support for Tegra210 MVC module.
+
+config SND_SOC_TEGRA210_SFC
+ tristate "Tegra210 SFC module"
+ help
+ Config to enable the Sampling Frequency Converter (SFC) which
+ converts the sampling frequency of input signal to another
+ frequency. It supports sampling frequency conversion of streams
+ upto 2 channels (stereo).
+ Say Y or M if you want to add support for Tegra210 SFC module.
+
+config SND_SOC_TEGRA210_AMX
+ tristate "Tegra210 AMX module"
+ help
+ Config to enable the Audio Multiplexer (AMX) which can multiplex
+ four input streams (each of up to 16 channels) and generate
+ output stream (of up to 16 channels). A byte RAM helps to form an
+ output frame by any combination of bytes from the input frames.
+ Say Y or M if you want to add support for Tegra210 AMX module.
+
+config SND_SOC_TEGRA210_ADX
+ tristate "Tegra210 ADX module"
+ help
+ Config to enable the Audio Demultiplexer (ADX) which takes an
+ input stream (up to 16 channels) and demultiplexes it into four
+ output streams (each of up to 16 channels). A byte RAM helps to
+ form output frames by any combination of bytes from the input
+ frame. Its design is identical to that of byte RAM in the AMX
+ except that the data flow direction is reversed.
+ Say Y or M if you want to add support for Tegra210 ADX module.
+
+config SND_SOC_TEGRA210_MIXER
+ tristate "Tegra210 Mixer module"
+ help
+ Config to enable the Mixer module which can help to mix multiple
+ audio streams. It supports mixing of upto 10 input streams,
+ where each stream can contain maximum of 8 channels. It supports
+ 5 output each of which can be a mix of any combination of 10
+ input streams.
+ Say Y or M if you want to add support for Tegra210 Mixer module.
+
config SND_SOC_TEGRA_AUDIO_GRAPH_CARD
tristate "Audio Graph Card based Tegra driver"
depends on SND_AUDIO_GRAPH_CARD
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index e2cec9ae31c9..f19d56690a0d 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -13,6 +13,11 @@ snd-soc-tegra210-dmic-objs := tegra210_dmic.o
snd-soc-tegra210-i2s-objs := tegra210_i2s.o
snd-soc-tegra186-dspk-objs := tegra186_dspk.o
snd-soc-tegra210-admaif-objs := tegra210_admaif.o
+snd-soc-tegra210-mvc-objs := tegra210_mvc.o
+snd-soc-tegra210-sfc-objs := tegra210_sfc.o
+snd-soc-tegra210-amx-objs := tegra210_amx.o
+snd-soc-tegra210-adx-objs := tegra210_adx.o
+snd-soc-tegra210-mixer-objs := tegra210_mixer.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
@@ -26,6 +31,11 @@ obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o
obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o
+obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o
+obj-$(CONFIG_SND_SOC_TEGRA210_SFC) += snd-soc-tegra210-sfc.o
+obj-$(CONFIG_SND_SOC_TEGRA210_AMX) += snd-soc-tegra210-amx.o
+obj-$(CONFIG_SND_SOC_TEGRA210_ADX) += snd-soc-tegra210-adx.o
+obj-$(CONFIG_SND_SOC_TEGRA210_MIXER) += snd-soc-tegra210-mixer.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
new file mode 100644
index 000000000000..d7c7849c2f92
--- /dev/null
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_adx.c - Tegra210 ADX driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_adx.h"
+#include "tegra_cif.h"
+
+static const struct reg_default tegra210_adx_reg_defaults[] = {
+ { TEGRA210_ADX_RX_INT_MASK, 0x00000001},
+ { TEGRA210_ADX_RX_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX_INT_MASK, 0x0000000f },
+ { TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_CG, 0x1},
+ { TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000},
+};
+
+static void tegra210_adx_write_map_ram(struct tegra210_adx *adx)
+{
+ int i;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
+ TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
+ TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN |
+ TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);
+
+ for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA,
+ adx->map[i]);
+
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]);
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]);
+}
+
+static int tegra210_adx_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+ int err;
+
+ /* Ensure if ADX status is disabled */
+ err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS,
+ val, !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to stop ADX, err = %d\n", err);
+ return err;
+ }
+
+ /*
+ * Soft Reset: Below performs module soft reset which clears
+ * all FSM logic, flushes flow control of FIFO and resets the
+ * state register. It also brings module back to disabled
+ * state (without flushing the data in the pipe).
+ */
+ regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
+ TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
+ TEGRA210_ADX_SOFT_RESET_SOFT_EN);
+
+ err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET,
+ val, !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to reset ADX, err = %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, true);
+ regcache_mark_dirty(adx->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, false);
+ regcache_sync(adx->regmap);
+
+ tegra210_adx_write_map_ram(adx);
+
+ return 0;
+}
+
+static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
+ unsigned int channels,
+ unsigned int format,
+ unsigned int reg)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ struct tegra_cif_conf cif_conf;
+ int audio_bits;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ if (channels < 1 || channels > 16)
+ return -EINVAL;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S8:
+ audio_bits = TEGRA_ACIF_BITS_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ tegra_set_cif(adx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return tegra210_adx_set_audio_cif(dai, params_channels(params),
+ params_format(params),
+ TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));
+}
+
+static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return tegra210_adx_set_audio_cif(dai, params_channels(params),
+ params_format(params),
+ TEGRA210_ADX_RX_CIF_CTRL);
+}
+
+static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
+ struct soc_mixer_control *mc;
+ unsigned char *bytes_map = (unsigned char *)&adx->map;
+ int enabled;
+
+ mc = (struct soc_mixer_control *)kcontrol->private_value;
+ enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32));
+
+ if (enabled)
+ ucontrol->value.integer.value[0] = bytes_map[mc->reg];
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
+ unsigned char *bytes_map = (unsigned char *)&adx->map;
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;;
+
+ if (value >= 0 && value <= 255) {
+ /* update byte map and enable slot */
+ bytes_map[mc->reg] = value;
+ adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32));
+ } else {
+ /* reset byte map and disable slot */
+ bytes_map[mc->reg] = 0;
+ adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32));
+ }
+
+ return 1;
+}
+
+static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops = {
+ .hw_params = tegra210_adx_in_hw_params,
+ .startup = tegra210_adx_startup,
+};
+
+static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {
+ .hw_params = tegra210_adx_out_hw_params,
+};
+
+#define IN_DAI \
+ { \
+ .name = "ADX-RX-CIF", \
+ .playback = { \
+ .stream_name = "RX-CIF-Playback", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "RX-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_adx_in_dai_ops, \
+ }
+
+#define OUT_DAI(id) \
+ { \
+ .name = "ADX-TX" #id "-CIF", \
+ .playback = { \
+ .stream_name = "TX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "TX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_adx_out_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_adx_dais[] = {
+ IN_DAI,
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ OUT_DAI(4),
+};
+
+static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE,
+ TEGRA210_ADX_ENABLE_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),
+};
+
+#define STREAM_ROUTES(id, sname) \
+ { "XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX" #id, NULL, "RX" }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
+
+#define ADX_ROUTES(id) \
+ STREAM_ROUTES(id, "Playback"), \
+ STREAM_ROUTES(id, "Capture")
+
+#define STREAM_ROUTES(id, sname) \
+ { "XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX" #id, NULL, "RX" }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
+
+#define ADX_ROUTES(id) \
+ STREAM_ROUTES(id, "Playback"), \
+ STREAM_ROUTES(id, "Capture")
+
+static const struct snd_soc_dapm_route tegra210_adx_routes[] = {
+ ADX_ROUTES(1),
+ ADX_ROUTES(2),
+ ADX_ROUTES(3),
+ ADX_ROUTES(4),
+};
+
+#define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \
+ SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
+ tegra210_adx_get_byte_map, \
+ tegra210_adx_put_byte_map)
+
+static struct snd_kcontrol_new tegra210_adx_controls[] = {
+ TEGRA210_ADX_BYTE_MAP_CTRL(0),
+ TEGRA210_ADX_BYTE_MAP_CTRL(1),
+ TEGRA210_ADX_BYTE_MAP_CTRL(2),
+ TEGRA210_ADX_BYTE_MAP_CTRL(3),
+ TEGRA210_ADX_BYTE_MAP_CTRL(4),
+ TEGRA210_ADX_BYTE_MAP_CTRL(5),
+ TEGRA210_ADX_BYTE_MAP_CTRL(6),
+ TEGRA210_ADX_BYTE_MAP_CTRL(7),
+ TEGRA210_ADX_BYTE_MAP_CTRL(8),
+ TEGRA210_ADX_BYTE_MAP_CTRL(9),
+ TEGRA210_ADX_BYTE_MAP_CTRL(10),
+ TEGRA210_ADX_BYTE_MAP_CTRL(11),
+ TEGRA210_ADX_BYTE_MAP_CTRL(12),
+ TEGRA210_ADX_BYTE_MAP_CTRL(13),
+ TEGRA210_ADX_BYTE_MAP_CTRL(14),
+ TEGRA210_ADX_BYTE_MAP_CTRL(15),
+ TEGRA210_ADX_BYTE_MAP_CTRL(16),
+ TEGRA210_ADX_BYTE_MAP_CTRL(17),
+ TEGRA210_ADX_BYTE_MAP_CTRL(18),
+ TEGRA210_ADX_BYTE_MAP_CTRL(19),
+ TEGRA210_ADX_BYTE_MAP_CTRL(20),
+ TEGRA210_ADX_BYTE_MAP_CTRL(21),
+ TEGRA210_ADX_BYTE_MAP_CTRL(22),
+ TEGRA210_ADX_BYTE_MAP_CTRL(23),
+ TEGRA210_ADX_BYTE_MAP_CTRL(24),
+ TEGRA210_ADX_BYTE_MAP_CTRL(25),
+ TEGRA210_ADX_BYTE_MAP_CTRL(26),
+ TEGRA210_ADX_BYTE_MAP_CTRL(27),
+ TEGRA210_ADX_BYTE_MAP_CTRL(28),
+ TEGRA210_ADX_BYTE_MAP_CTRL(29),
+ TEGRA210_ADX_BYTE_MAP_CTRL(30),
+ TEGRA210_ADX_BYTE_MAP_CTRL(31),
+ TEGRA210_ADX_BYTE_MAP_CTRL(32),
+ TEGRA210_ADX_BYTE_MAP_CTRL(33),
+ TEGRA210_ADX_BYTE_MAP_CTRL(34),
+ TEGRA210_ADX_BYTE_MAP_CTRL(35),
+ TEGRA210_ADX_BYTE_MAP_CTRL(36),
+ TEGRA210_ADX_BYTE_MAP_CTRL(37),
+ TEGRA210_ADX_BYTE_MAP_CTRL(38),
+ TEGRA210_ADX_BYTE_MAP_CTRL(39),
+ TEGRA210_ADX_BYTE_MAP_CTRL(40),
+ TEGRA210_ADX_BYTE_MAP_CTRL(41),
+ TEGRA210_ADX_BYTE_MAP_CTRL(42),
+ TEGRA210_ADX_BYTE_MAP_CTRL(43),
+ TEGRA210_ADX_BYTE_MAP_CTRL(44),
+ TEGRA210_ADX_BYTE_MAP_CTRL(45),
+ TEGRA210_ADX_BYTE_MAP_CTRL(46),
+ TEGRA210_ADX_BYTE_MAP_CTRL(47),
+ TEGRA210_ADX_BYTE_MAP_CTRL(48),
+ TEGRA210_ADX_BYTE_MAP_CTRL(49),
+ TEGRA210_ADX_BYTE_MAP_CTRL(50),
+ TEGRA210_ADX_BYTE_MAP_CTRL(51),
+ TEGRA210_ADX_BYTE_MAP_CTRL(52),
+ TEGRA210_ADX_BYTE_MAP_CTRL(53),
+ TEGRA210_ADX_BYTE_MAP_CTRL(54),
+ TEGRA210_ADX_BYTE_MAP_CTRL(55),
+ TEGRA210_ADX_BYTE_MAP_CTRL(56),
+ TEGRA210_ADX_BYTE_MAP_CTRL(57),
+ TEGRA210_ADX_BYTE_MAP_CTRL(58),
+ TEGRA210_ADX_BYTE_MAP_CTRL(59),
+ TEGRA210_ADX_BYTE_MAP_CTRL(60),
+ TEGRA210_ADX_BYTE_MAP_CTRL(61),
+ TEGRA210_ADX_BYTE_MAP_CTRL(62),
+ TEGRA210_ADX_BYTE_MAP_CTRL(63),
+};
+
+static const struct snd_soc_component_driver tegra210_adx_cmpnt = {
+ .dapm_widgets = tegra210_adx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets),
+ .dapm_routes = tegra210_adx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes),
+ .controls = tegra210_adx_controls,
+ .num_controls = ARRAY_SIZE(tegra210_adx_controls),
+};
+
+static bool tegra210_adx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL:
+ case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG:
+ case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1:
+ case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_adx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_adx_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS:
+ case TEGRA210_ADX_RX_INT_STATUS:
+ case TEGRA210_ADX_RX_INT_SET:
+ case TEGRA210_ADX_TX_STATUS:
+ case TEGRA210_ADX_TX_INT_STATUS:
+ case TEGRA210_ADX_TX_INT_SET:
+ case TEGRA210_ADX_SOFT_RESET:
+ case TEGRA210_ADX_STATUS:
+ case TEGRA210_ADX_INT_STATUS:
+ case TEGRA210_ADX_CFG_RAM_CTRL:
+ case TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static const struct regmap_config tegra210_adx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_ADX_CFG_RAM_DATA,
+ .writeable_reg = tegra210_adx_wr_reg,
+ .readable_reg = tegra210_adx_rd_reg,
+ .volatile_reg = tegra210_adx_volatile_reg,
+ .reg_defaults = tegra210_adx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_adx_of_match[] = {
+ { .compatible = "nvidia,tegra210-adx" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
+
+static int tegra210_adx_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_adx *adx;
+ void __iomem *regs;
+ int err;
+
+ adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL);
+ if (!adx)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, adx);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ adx->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra210_adx_regmap_config);
+ if (IS_ERR(adx->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(adx->regmap);
+ }
+
+ regcache_cache_only(adx->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt,
+ tegra210_adx_dais,
+ ARRAY_SIZE(tegra210_adx_dais));
+ if (err) {
+ dev_err(dev, "can't register ADX component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int 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 = {
+ SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend,
+ tegra210_adx_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_adx_driver = {
+ .driver = {
+ .name = "tegra210-adx",
+ .of_match_table = tegra210_adx_of_match,
+ .pm = &tegra210_adx_pm_ops,
+ },
+ .probe = tegra210_adx_platform_probe,
+ .remove = tegra210_adx_platform_remove,
+};
+module_platform_driver(tegra210_adx_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra210_adx.h b/sound/soc/tegra/tegra210_adx.h
new file mode 100644
index 000000000000..d7dcb6497978
--- /dev/null
+++ b/sound/soc/tegra/tegra210_adx.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_adx.h - Definitions for Tegra210 ADX driver
+ *
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_ADX_H__
+#define __TEGRA210_ADX_H__
+
+/* Register offsets from TEGRA210_ADX*_BASE */
+#define TEGRA210_ADX_RX_STATUS 0x0c
+#define TEGRA210_ADX_RX_INT_STATUS 0x10
+#define TEGRA210_ADX_RX_INT_MASK 0x14
+#define TEGRA210_ADX_RX_INT_SET 0x18
+#define TEGRA210_ADX_RX_INT_CLEAR 0x1c
+#define TEGRA210_ADX_RX_CIF_CTRL 0x20
+#define TEGRA210_ADX_TX_STATUS 0x4c
+#define TEGRA210_ADX_TX_INT_STATUS 0x50
+#define TEGRA210_ADX_TX_INT_MASK 0x54
+#define TEGRA210_ADX_TX_INT_SET 0x58
+#define TEGRA210_ADX_TX_INT_CLEAR 0x5c
+#define TEGRA210_ADX_TX1_CIF_CTRL 0x60
+#define TEGRA210_ADX_TX2_CIF_CTRL 0x64
+#define TEGRA210_ADX_TX3_CIF_CTRL 0x68
+#define TEGRA210_ADX_TX4_CIF_CTRL 0x6c
+#define TEGRA210_ADX_ENABLE 0x80
+#define TEGRA210_ADX_SOFT_RESET 0x84
+#define TEGRA210_ADX_CG 0x88
+#define TEGRA210_ADX_STATUS 0x8c
+#define TEGRA210_ADX_INT_STATUS 0x90
+#define TEGRA210_ADX_CTRL 0xa4
+#define TEGRA210_ADX_IN_BYTE_EN0 0xa8
+#define TEGRA210_ADX_IN_BYTE_EN1 0xac
+#define TEGRA210_ADX_CFG_RAM_CTRL 0xb8
+#define TEGRA210_ADX_CFG_RAM_DATA 0xbc
+
+/* Fields in TEGRA210_ADX_ENABLE */
+#define TEGRA210_ADX_ENABLE_SHIFT 0
+
+/* Fields in TEGRA210_ADX_CFG_RAM_CTRL */
+#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT 14
+#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT)
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
+#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
+#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
+
+/* Fields in TEGRA210_ADX_SOFT_RESET */
+#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT 0
+#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+#define TEGRA210_ADX_SOFT_RESET_SOFT_EN (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+#define TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+
+#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4
+#define TEGRA210_ADX_RAM_DEPTH 16
+#define TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT 6
+#define TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT 2
+#define TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT 0
+
+struct tegra210_adx {
+ struct regmap *regmap;
+ unsigned int map[TEGRA210_ADX_RAM_DEPTH];
+ unsigned int byte_mask[2];
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index 66287a7c9865..a1989eae2b52 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -105,14 +105,68 @@ static struct snd_soc_dai_driver tegra210_ahub_dais[] = {
DAI(ADMAIF8),
DAI(ADMAIF9),
DAI(ADMAIF10),
+ /* XBAR <-> I2S <-> Codec */
DAI(I2S1),
DAI(I2S2),
DAI(I2S3),
DAI(I2S4),
DAI(I2S5),
+ /* XBAR <- DMIC <- Codec */
DAI(DMIC1),
DAI(DMIC2),
DAI(DMIC3),
+ /* XBAR -> SFC -> XBAR */
+ DAI(SFC1 RX),
+ DAI(SFC1 TX),
+ DAI(SFC2 RX),
+ DAI(SFC2 TX),
+ DAI(SFC3 RX),
+ DAI(SFC3 TX),
+ DAI(SFC4 RX),
+ DAI(SFC4 TX),
+ /* XBAR -> MVC -> XBAR */
+ DAI(MVC1 RX),
+ DAI(MVC1 TX),
+ DAI(MVC2 RX),
+ DAI(MVC2 TX),
+ /* XBAR -> AMX(4:1) -> XBAR */
+ DAI(AMX1 RX1),
+ DAI(AMX1 RX2),
+ DAI(AMX1 RX3),
+ DAI(AMX1 RX4),
+ DAI(AMX1),
+ DAI(AMX2 RX1),
+ DAI(AMX2 RX2),
+ DAI(AMX2 RX3),
+ DAI(AMX2 RX4),
+ DAI(AMX2),
+ /* XBAR -> ADX(1:4) -> XBAR */
+ DAI(ADX1),
+ DAI(ADX1 TX1),
+ DAI(ADX1 TX2),
+ DAI(ADX1 TX3),
+ DAI(ADX1 TX4),
+ DAI(ADX2),
+ DAI(ADX2 TX1),
+ DAI(ADX2 TX2),
+ DAI(ADX2 TX3),
+ DAI(ADX2 TX4),
+ /* XBAR -> MIXER(10:5) -> XBAR */
+ DAI(MIXER1 RX1),
+ DAI(MIXER1 RX2),
+ DAI(MIXER1 RX3),
+ DAI(MIXER1 RX4),
+ DAI(MIXER1 RX5),
+ DAI(MIXER1 RX6),
+ DAI(MIXER1 RX7),
+ DAI(MIXER1 RX8),
+ DAI(MIXER1 RX9),
+ DAI(MIXER1 RX10),
+ DAI(MIXER1 TX1),
+ DAI(MIXER1 TX2),
+ DAI(MIXER1 TX3),
+ DAI(MIXER1 TX4),
+ DAI(MIXER1 TX5),
};
static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
@@ -136,18 +190,93 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
DAI(ADMAIF18),
DAI(ADMAIF19),
DAI(ADMAIF20),
+ /* XBAR <-> I2S <-> Codec */
DAI(I2S1),
DAI(I2S2),
DAI(I2S3),
DAI(I2S4),
DAI(I2S5),
DAI(I2S6),
+ /* XBAR <- DMIC <- Codec */
DAI(DMIC1),
DAI(DMIC2),
DAI(DMIC3),
DAI(DMIC4),
+ /* XBAR -> DSPK -> Codec */
DAI(DSPK1),
DAI(DSPK2),
+ /* XBAR -> SFC -> XBAR */
+ DAI(SFC1 RX),
+ DAI(SFC1 TX),
+ DAI(SFC2 RX),
+ DAI(SFC2 TX),
+ DAI(SFC3 RX),
+ DAI(SFC3 TX),
+ DAI(SFC4 RX),
+ DAI(SFC4 TX),
+ /* XBAR -> MVC -> XBAR */
+ DAI(MVC1 RX),
+ DAI(MVC1 TX),
+ DAI(MVC2 RX),
+ DAI(MVC2 TX),
+ /* XBAR -> AMX(4:1) -> XBAR */
+ DAI(AMX1 RX1),
+ DAI(AMX1 RX2),
+ DAI(AMX1 RX3),
+ DAI(AMX1 RX4),
+ DAI(AMX1),
+ DAI(AMX2 RX1),
+ DAI(AMX2 RX2),
+ DAI(AMX2 RX3),
+ DAI(AMX2 RX4),
+ DAI(AMX2),
+ DAI(AMX3 RX1),
+ DAI(AMX3 RX2),
+ DAI(AMX3 RX3),
+ DAI(AMX3 RX4),
+ DAI(AMX3),
+ DAI(AMX4 RX1),
+ DAI(AMX4 RX2),
+ DAI(AMX4 RX3),
+ DAI(AMX4 RX4),
+ DAI(AMX4),
+ /* XBAR -> ADX(1:4) -> XBAR */
+ DAI(ADX1),
+ DAI(ADX1 TX1),
+ DAI(ADX1 TX2),
+ DAI(ADX1 TX3),
+ DAI(ADX1 TX4),
+ DAI(ADX2),
+ DAI(ADX2 TX1),
+ DAI(ADX2 TX2),
+ DAI(ADX2 TX3),
+ DAI(ADX2 TX4),
+ DAI(ADX3),
+ DAI(ADX3 TX1),
+ DAI(ADX3 TX2),
+ DAI(ADX3 TX3),
+ DAI(ADX3 TX4),
+ DAI(ADX4),
+ DAI(ADX4 TX1),
+ DAI(ADX4 TX2),
+ DAI(ADX4 TX3),
+ DAI(ADX4 TX4),
+ /* XBAR -> MIXER1(10:5) -> XBAR */
+ DAI(MIXER1 RX1),
+ DAI(MIXER1 RX2),
+ DAI(MIXER1 RX3),
+ DAI(MIXER1 RX4),
+ DAI(MIXER1 RX5),
+ DAI(MIXER1 RX6),
+ DAI(MIXER1 RX7),
+ DAI(MIXER1 RX8),
+ DAI(MIXER1 RX9),
+ DAI(MIXER1 RX10),
+ DAI(MIXER1 TX1),
+ DAI(MIXER1 TX2),
+ DAI(MIXER1 TX3),
+ DAI(MIXER1 TX4),
+ DAI(MIXER1 TX5),
};
static const char * const tegra210_ahub_mux_texts[] = {
@@ -170,6 +299,27 @@ static const char * const tegra210_ahub_mux_texts[] = {
"DMIC1",
"DMIC2",
"DMIC3",
+ "SFC1",
+ "SFC2",
+ "SFC3",
+ "SFC4",
+ "MVC1",
+ "MVC2",
+ "AMX1",
+ "AMX2",
+ "ADX1 TX1",
+ "ADX1 TX2",
+ "ADX1 TX3",
+ "ADX1 TX4",
+ "ADX2 TX1",
+ "ADX2 TX2",
+ "ADX2 TX3",
+ "ADX2 TX4",
+ "MIXER1 TX1",
+ "MIXER1 TX2",
+ "MIXER1 TX3",
+ "MIXER1 TX4",
+ "MIXER1 TX5",
};
static const char * const tegra186_ahub_mux_texts[] = {
@@ -204,10 +354,42 @@ static const char * const tegra186_ahub_mux_texts[] = {
"DMIC2",
"DMIC3",
"DMIC4",
+ "SFC1",
+ "SFC2",
+ "SFC3",
+ "SFC4",
+ "MVC1",
+ "MVC2",
+ "AMX1",
+ "AMX2",
+ "AMX3",
+ "AMX4",
+ "ADX1 TX1",
+ "ADX1 TX2",
+ "ADX1 TX3",
+ "ADX1 TX4",
+ "ADX2 TX1",
+ "ADX2 TX2",
+ "ADX2 TX3",
+ "ADX2 TX4",
+ "ADX3 TX1",
+ "ADX3 TX2",
+ "ADX3 TX3",
+ "ADX3 TX4",
+ "ADX4 TX1",
+ "ADX4 TX2",
+ "ADX4 TX3",
+ "ADX4 TX4",
+ "MIXER1 TX1",
+ "MIXER1 TX2",
+ "MIXER1 TX3",
+ "MIXER1 TX4",
+ "MIXER1 TX5",
};
static const unsigned int tegra210_ahub_mux_values[] = {
0,
+ /* ADMAIF */
MUX_VALUE(0, 0),
MUX_VALUE(0, 1),
MUX_VALUE(0, 2),
@@ -218,18 +400,47 @@ static const unsigned int tegra210_ahub_mux_values[] = {
MUX_VALUE(0, 7),
MUX_VALUE(0, 8),
MUX_VALUE(0, 9),
+ /* I2S */
MUX_VALUE(0, 16),
MUX_VALUE(0, 17),
MUX_VALUE(0, 18),
MUX_VALUE(0, 19),
MUX_VALUE(0, 20),
+ /* DMIC */
MUX_VALUE(2, 18),
MUX_VALUE(2, 19),
MUX_VALUE(2, 20),
+ /* SFC */
+ MUX_VALUE(0, 24),
+ MUX_VALUE(0, 25),
+ MUX_VALUE(0, 26),
+ MUX_VALUE(0, 27),
+ /* MVC */
+ MUX_VALUE(2, 8),
+ MUX_VALUE(2, 9),
+ /* AMX */
+ MUX_VALUE(1, 8),
+ MUX_VALUE(1, 9),
+ /* ADX */
+ MUX_VALUE(2, 24),
+ MUX_VALUE(2, 25),
+ MUX_VALUE(2, 26),
+ MUX_VALUE(2, 27),
+ MUX_VALUE(2, 28),
+ MUX_VALUE(2, 29),
+ MUX_VALUE(2, 30),
+ MUX_VALUE(2, 31),
+ /* MIXER */
+ MUX_VALUE(1, 0),
+ MUX_VALUE(1, 1),
+ MUX_VALUE(1, 2),
+ MUX_VALUE(1, 3),
+ MUX_VALUE(1, 4),
};
static const unsigned int tegra186_ahub_mux_values[] = {
0,
+ /* ADMAIF */
MUX_VALUE(0, 0),
MUX_VALUE(0, 1),
MUX_VALUE(0, 2),
@@ -246,20 +457,59 @@ static const unsigned int tegra186_ahub_mux_values[] = {
MUX_VALUE(0, 13),
MUX_VALUE(0, 14),
MUX_VALUE(0, 15),
+ /* I2S */
MUX_VALUE(0, 16),
MUX_VALUE(0, 17),
MUX_VALUE(0, 18),
MUX_VALUE(0, 19),
MUX_VALUE(0, 20),
MUX_VALUE(0, 21),
+ /* ADMAIF */
MUX_VALUE(3, 16),
MUX_VALUE(3, 17),
MUX_VALUE(3, 18),
MUX_VALUE(3, 19),
+ /* DMIC */
MUX_VALUE(2, 18),
MUX_VALUE(2, 19),
MUX_VALUE(2, 20),
MUX_VALUE(2, 21),
+ /* SFC */
+ MUX_VALUE(0, 24),
+ MUX_VALUE(0, 25),
+ MUX_VALUE(0, 26),
+ MUX_VALUE(0, 27),
+ /* MVC */
+ MUX_VALUE(2, 8),
+ MUX_VALUE(2, 9),
+ /* AMX */
+ MUX_VALUE(1, 8),
+ MUX_VALUE(1, 9),
+ MUX_VALUE(1, 10),
+ MUX_VALUE(1, 11),
+ /* ADX */
+ MUX_VALUE(2, 24),
+ MUX_VALUE(2, 25),
+ MUX_VALUE(2, 26),
+ MUX_VALUE(2, 27),
+ MUX_VALUE(2, 28),
+ MUX_VALUE(2, 29),
+ MUX_VALUE(2, 30),
+ MUX_VALUE(2, 31),
+ MUX_VALUE(3, 0),
+ MUX_VALUE(3, 1),
+ MUX_VALUE(3, 2),
+ MUX_VALUE(3, 3),
+ MUX_VALUE(3, 4),
+ MUX_VALUE(3, 5),
+ MUX_VALUE(3, 6),
+ MUX_VALUE(3, 7),
+ /* MIXER */
+ MUX_VALUE(1, 0),
+ MUX_VALUE(1, 1),
+ MUX_VALUE(1, 2),
+ MUX_VALUE(1, 3),
+ MUX_VALUE(1, 4),
};
/* Controls for t210 */
@@ -278,6 +528,32 @@ MUX_ENUM_CTRL_DECL(t210_i2s2_tx, 0x11);
MUX_ENUM_CTRL_DECL(t210_i2s3_tx, 0x12);
MUX_ENUM_CTRL_DECL(t210_i2s4_tx, 0x13);
MUX_ENUM_CTRL_DECL(t210_i2s5_tx, 0x14);
+MUX_ENUM_CTRL_DECL(t210_sfc1_tx, 0x18);
+MUX_ENUM_CTRL_DECL(t210_sfc2_tx, 0x19);
+MUX_ENUM_CTRL_DECL(t210_sfc3_tx, 0x1a);
+MUX_ENUM_CTRL_DECL(t210_sfc4_tx, 0x1b);
+MUX_ENUM_CTRL_DECL(t210_mvc1_tx, 0x48);
+MUX_ENUM_CTRL_DECL(t210_mvc2_tx, 0x49);
+MUX_ENUM_CTRL_DECL(t210_amx11_tx, 0x50);
+MUX_ENUM_CTRL_DECL(t210_amx12_tx, 0x51);
+MUX_ENUM_CTRL_DECL(t210_amx13_tx, 0x52);
+MUX_ENUM_CTRL_DECL(t210_amx14_tx, 0x53);
+MUX_ENUM_CTRL_DECL(t210_amx21_tx, 0x54);
+MUX_ENUM_CTRL_DECL(t210_amx22_tx, 0x55);
+MUX_ENUM_CTRL_DECL(t210_amx23_tx, 0x56);
+MUX_ENUM_CTRL_DECL(t210_amx24_tx, 0x57);
+MUX_ENUM_CTRL_DECL(t210_adx1_tx, 0x58);
+MUX_ENUM_CTRL_DECL(t210_adx2_tx, 0x59);
+MUX_ENUM_CTRL_DECL(t210_mixer11_tx, 0x20);
+MUX_ENUM_CTRL_DECL(t210_mixer12_tx, 0x21);
+MUX_ENUM_CTRL_DECL(t210_mixer13_tx, 0x22);
+MUX_ENUM_CTRL_DECL(t210_mixer14_tx, 0x23);
+MUX_ENUM_CTRL_DECL(t210_mixer15_tx, 0x24);
+MUX_ENUM_CTRL_DECL(t210_mixer16_tx, 0x25);
+MUX_ENUM_CTRL_DECL(t210_mixer17_tx, 0x26);
+MUX_ENUM_CTRL_DECL(t210_mixer18_tx, 0x27);
+MUX_ENUM_CTRL_DECL(t210_mixer19_tx, 0x28);
+MUX_ENUM_CTRL_DECL(t210_mixer110_tx, 0x29);
/* Controls for t186 */
MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00);
@@ -308,6 +584,42 @@ MUX_ENUM_CTRL_DECL_186(t186_admaif17_tx, 0x68);
MUX_ENUM_CTRL_DECL_186(t186_admaif18_tx, 0x69);
MUX_ENUM_CTRL_DECL_186(t186_admaif19_tx, 0x6a);
MUX_ENUM_CTRL_DECL_186(t186_admaif20_tx, 0x6b);
+MUX_ENUM_CTRL_DECL_186(t186_sfc1_tx, 0x18);
+MUX_ENUM_CTRL_DECL_186(t186_sfc2_tx, 0x19);
+MUX_ENUM_CTRL_DECL_186(t186_sfc3_tx, 0x1a);
+MUX_ENUM_CTRL_DECL_186(t186_sfc4_tx, 0x1b);
+MUX_ENUM_CTRL_DECL_186(t186_mvc1_tx, 0x48);
+MUX_ENUM_CTRL_DECL_186(t186_mvc2_tx, 0x49);
+MUX_ENUM_CTRL_DECL_186(t186_amx11_tx, 0x50);
+MUX_ENUM_CTRL_DECL_186(t186_amx12_tx, 0x51);
+MUX_ENUM_CTRL_DECL_186(t186_amx13_tx, 0x52);
+MUX_ENUM_CTRL_DECL_186(t186_amx14_tx, 0x53);
+MUX_ENUM_CTRL_DECL_186(t186_amx21_tx, 0x54);
+MUX_ENUM_CTRL_DECL_186(t186_amx22_tx, 0x55);
+MUX_ENUM_CTRL_DECL_186(t186_amx23_tx, 0x56);
+MUX_ENUM_CTRL_DECL_186(t186_amx24_tx, 0x57);
+MUX_ENUM_CTRL_DECL_186(t186_amx31_tx, 0x58);
+MUX_ENUM_CTRL_DECL_186(t186_amx32_tx, 0x59);
+MUX_ENUM_CTRL_DECL_186(t186_amx33_tx, 0x5a);
+MUX_ENUM_CTRL_DECL_186(t186_amx34_tx, 0x5b);
+MUX_ENUM_CTRL_DECL_186(t186_amx41_tx, 0x64);
+MUX_ENUM_CTRL_DECL_186(t186_amx42_tx, 0x65);
+MUX_ENUM_CTRL_DECL_186(t186_amx43_tx, 0x66);
+MUX_ENUM_CTRL_DECL_186(t186_amx44_tx, 0x67);
+MUX_ENUM_CTRL_DECL_186(t186_adx1_tx, 0x60);
+MUX_ENUM_CTRL_DECL_186(t186_adx2_tx, 0x61);
+MUX_ENUM_CTRL_DECL_186(t186_adx3_tx, 0x62);
+MUX_ENUM_CTRL_DECL_186(t186_adx4_tx, 0x63);
+MUX_ENUM_CTRL_DECL_186(t186_mixer11_tx, 0x20);
+MUX_ENUM_CTRL_DECL_186(t186_mixer12_tx, 0x21);
+MUX_ENUM_CTRL_DECL_186(t186_mixer13_tx, 0x22);
+MUX_ENUM_CTRL_DECL_186(t186_mixer14_tx, 0x23);
+MUX_ENUM_CTRL_DECL_186(t186_mixer15_tx, 0x24);
+MUX_ENUM_CTRL_DECL_186(t186_mixer16_tx, 0x25);
+MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26);
+MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27);
+MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28);
+MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29);
/*
* The number of entries in, and order of, this array is closely tied to the
@@ -333,6 +645,47 @@ static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = {
TX_WIDGETS("DMIC1"),
TX_WIDGETS("DMIC2"),
TX_WIDGETS("DMIC3"),
+ WIDGETS("SFC1", t210_sfc1_tx),
+ WIDGETS("SFC2", t210_sfc2_tx),
+ WIDGETS("SFC3", t210_sfc3_tx),
+ WIDGETS("SFC4", t210_sfc4_tx),
+ WIDGETS("MVC1", t210_mvc1_tx),
+ WIDGETS("MVC2", t210_mvc2_tx),
+ WIDGETS("AMX1 RX1", t210_amx11_tx),
+ WIDGETS("AMX1 RX2", t210_amx12_tx),
+ WIDGETS("AMX1 RX3", t210_amx13_tx),
+ WIDGETS("AMX1 RX4", t210_amx14_tx),
+ WIDGETS("AMX2 RX1", t210_amx21_tx),
+ WIDGETS("AMX2 RX2", t210_amx22_tx),
+ WIDGETS("AMX2 RX3", t210_amx23_tx),
+ WIDGETS("AMX2 RX4", t210_amx24_tx),
+ TX_WIDGETS("AMX1"),
+ TX_WIDGETS("AMX2"),
+ WIDGETS("ADX1", t210_adx1_tx),
+ WIDGETS("ADX2", t210_adx2_tx),
+ TX_WIDGETS("ADX1 TX1"),
+ TX_WIDGETS("ADX1 TX2"),
+ TX_WIDGETS("ADX1 TX3"),
+ TX_WIDGETS("ADX1 TX4"),
+ TX_WIDGETS("ADX2 TX1"),
+ TX_WIDGETS("ADX2 TX2"),
+ TX_WIDGETS("ADX2 TX3"),
+ TX_WIDGETS("ADX2 TX4"),
+ WIDGETS("MIXER1 RX1", t210_mixer11_tx),
+ WIDGETS("MIXER1 RX2", t210_mixer12_tx),
+ WIDGETS("MIXER1 RX3", t210_mixer13_tx),
+ WIDGETS("MIXER1 RX4", t210_mixer14_tx),
+ WIDGETS("MIXER1 RX5", t210_mixer15_tx),
+ WIDGETS("MIXER1 RX6", t210_mixer16_tx),
+ WIDGETS("MIXER1 RX7", t210_mixer17_tx),
+ WIDGETS("MIXER1 RX8", t210_mixer18_tx),
+ WIDGETS("MIXER1 RX9", t210_mixer19_tx),
+ WIDGETS("MIXER1 RX10", t210_mixer110_tx),
+ TX_WIDGETS("MIXER1 TX1"),
+ TX_WIDGETS("MIXER1 TX2"),
+ TX_WIDGETS("MIXER1 TX3"),
+ TX_WIDGETS("MIXER1 TX4"),
+ TX_WIDGETS("MIXER1 TX5"),
};
static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
@@ -368,6 +721,67 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
TX_WIDGETS("DMIC4"),
WIDGETS("DSPK1", t186_dspk1_tx),
WIDGETS("DSPK2", t186_dspk2_tx),
+ WIDGETS("SFC1", t186_sfc1_tx),
+ WIDGETS("SFC2", t186_sfc2_tx),
+ WIDGETS("SFC3", t186_sfc3_tx),
+ WIDGETS("SFC4", t186_sfc4_tx),
+ WIDGETS("MVC1", t186_mvc1_tx),
+ WIDGETS("MVC2", t186_mvc2_tx),
+ WIDGETS("AMX1 RX1", t186_amx11_tx),
+ WIDGETS("AMX1 RX2", t186_amx12_tx),
+ WIDGETS("AMX1 RX3", t186_amx13_tx),
+ WIDGETS("AMX1 RX4", t186_amx14_tx),
+ WIDGETS("AMX2 RX1", t186_amx21_tx),
+ WIDGETS("AMX2 RX2", t186_amx22_tx),
+ WIDGETS("AMX2 RX3", t186_amx23_tx),
+ WIDGETS("AMX2 RX4", t186_amx24_tx),
+ WIDGETS("AMX3 RX1", t186_amx31_tx),
+ WIDGETS("AMX3 RX2", t186_amx32_tx),
+ WIDGETS("AMX3 RX3", t186_amx33_tx),
+ WIDGETS("AMX3 RX4", t186_amx34_tx),
+ WIDGETS("AMX4 RX1", t186_amx41_tx),
+ WIDGETS("AMX4 RX2", t186_amx42_tx),
+ WIDGETS("AMX4 RX3", t186_amx43_tx),
+ WIDGETS("AMX4 RX4", t186_amx44_tx),
+ TX_WIDGETS("AMX1"),
+ TX_WIDGETS("AMX2"),
+ TX_WIDGETS("AMX3"),
+ TX_WIDGETS("AMX4"),
+ WIDGETS("ADX1", t186_adx1_tx),
+ WIDGETS("ADX2", t186_adx2_tx),
+ WIDGETS("ADX3", t186_adx3_tx),
+ WIDGETS("ADX4", t186_adx4_tx),
+ TX_WIDGETS("ADX1 TX1"),
+ TX_WIDGETS("ADX1 TX2"),
+ TX_WIDGETS("ADX1 TX3"),
+ TX_WIDGETS("ADX1 TX4"),
+ TX_WIDGETS("ADX2 TX1"),
+ TX_WIDGETS("ADX2 TX2"),
+ TX_WIDGETS("ADX2 TX3"),
+ TX_WIDGETS("ADX2 TX4"),
+ TX_WIDGETS("ADX3 TX1"),
+ TX_WIDGETS("ADX3 TX2"),
+ TX_WIDGETS("ADX3 TX3"),
+ TX_WIDGETS("ADX3 TX4"),
+ TX_WIDGETS("ADX4 TX1"),
+ TX_WIDGETS("ADX4 TX2"),
+ TX_WIDGETS("ADX4 TX3"),
+ TX_WIDGETS("ADX4 TX4"),
+ WIDGETS("MIXER1 RX1", t186_mixer11_tx),
+ WIDGETS("MIXER1 RX2", t186_mixer12_tx),
+ WIDGETS("MIXER1 RX3", t186_mixer13_tx),
+ WIDGETS("MIXER1 RX4", t186_mixer14_tx),
+ WIDGETS("MIXER1 RX5", t186_mixer15_tx),
+ WIDGETS("MIXER1 RX6", t186_mixer16_tx),
+ WIDGETS("MIXER1 RX7", t186_mixer17_tx),
+ WIDGETS("MIXER1 RX8", t186_mixer18_tx),
+ WIDGETS("MIXER1 RX9", t186_mixer19_tx),
+ WIDGETS("MIXER1 RX10", t186_mixer110_tx),
+ TX_WIDGETS("MIXER1 TX1"),
+ TX_WIDGETS("MIXER1 TX2"),
+ TX_WIDGETS("MIXER1 TX3"),
+ TX_WIDGETS("MIXER1 TX4"),
+ TX_WIDGETS("MIXER1 TX5"),
};
#define TEGRA_COMMON_MUX_ROUTES(name) \
@@ -389,7 +803,28 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
{ name " Mux", "I2S5", "I2S5 XBAR-RX" }, \
{ name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \
{ name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \
- { name " Mux", "DMIC3", "DMIC3 XBAR-RX" },
+ { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \
+ { name " Mux", "SFC1", "SFC1 XBAR-RX" }, \
+ { name " Mux", "SFC2", "SFC2 XBAR-RX" }, \
+ { name " Mux", "SFC3", "SFC3 XBAR-RX" }, \
+ { name " Mux", "SFC4", "SFC4 XBAR-RX" }, \
+ { name " Mux", "MVC1", "MVC1 XBAR-RX" }, \
+ { name " Mux", "MVC2", "MVC2 XBAR-RX" }, \
+ { name " Mux", "AMX1", "AMX1 XBAR-RX" }, \
+ { name " Mux", "AMX2", "AMX2 XBAR-RX" }, \
+ { name " Mux", "ADX1 TX1", "ADX1 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX1 TX2", "ADX1 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX1 TX3", "ADX1 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX1 TX4", "ADX1 TX4 XBAR-RX" }, \
+ { name " Mux", "ADX2 TX1", "ADX2 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX2 TX2", "ADX2 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX2 TX3", "ADX2 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX2 TX4", "ADX2 TX4 XBAR-RX" }, \
+ { name " Mux", "MIXER1 TX1", "MIXER1 TX1 XBAR-RX" }, \
+ { name " Mux", "MIXER1 TX2", "MIXER1 TX2 XBAR-RX" }, \
+ { name " Mux", "MIXER1 TX3", "MIXER1 TX3 XBAR-RX" }, \
+ { name " Mux", "MIXER1 TX4", "MIXER1 TX4 XBAR-RX" }, \
+ { name " Mux", "MIXER1 TX5", "MIXER1 TX5 XBAR-RX" },
#define TEGRA186_ONLY_MUX_ROUTES(name) \
{ name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \
@@ -403,7 +838,17 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
{ name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \
{ name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \
{ name " Mux", "I2S6", "I2S6 XBAR-RX" }, \
- { name " Mux", "DMIC4", "DMIC4 XBAR-RX" },
+ { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \
+ { name " Mux", "AMX3", "AMX3 XBAR-RX" }, \
+ { name " Mux", "AMX4", "AMX4 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX1", "ADX3 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX2", "ADX3 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX3", "ADX3 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX4", "ADX3 TX4 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" },
#define TEGRA210_MUX_ROUTES(name) \
TEGRA_COMMON_MUX_ROUTES(name)
@@ -450,6 +895,32 @@ static const struct snd_soc_dapm_route tegra210_ahub_routes[] = {
TEGRA210_MUX_ROUTES("I2S3")
TEGRA210_MUX_ROUTES("I2S4")
TEGRA210_MUX_ROUTES("I2S5")
+ TEGRA210_MUX_ROUTES("SFC1")
+ TEGRA210_MUX_ROUTES("SFC2")
+ TEGRA210_MUX_ROUTES("SFC3")
+ TEGRA210_MUX_ROUTES("SFC4")
+ TEGRA210_MUX_ROUTES("MVC1")
+ TEGRA210_MUX_ROUTES("MVC2")
+ TEGRA210_MUX_ROUTES("AMX1 RX1")
+ TEGRA210_MUX_ROUTES("AMX1 RX2")
+ TEGRA210_MUX_ROUTES("AMX1 RX3")
+ TEGRA210_MUX_ROUTES("AMX1 RX4")
+ TEGRA210_MUX_ROUTES("AMX2 RX1")
+ TEGRA210_MUX_ROUTES("AMX2 RX2")
+ TEGRA210_MUX_ROUTES("AMX2 RX3")
+ TEGRA210_MUX_ROUTES("AMX2 RX4")
+ TEGRA210_MUX_ROUTES("ADX1")
+ TEGRA210_MUX_ROUTES("ADX2")
+ TEGRA210_MUX_ROUTES("MIXER1 RX1")
+ TEGRA210_MUX_ROUTES("MIXER1 RX2")
+ TEGRA210_MUX_ROUTES("MIXER1 RX3")
+ TEGRA210_MUX_ROUTES("MIXER1 RX4")
+ TEGRA210_MUX_ROUTES("MIXER1 RX5")
+ TEGRA210_MUX_ROUTES("MIXER1 RX6")
+ TEGRA210_MUX_ROUTES("MIXER1 RX7")
+ TEGRA210_MUX_ROUTES("MIXER1 RX8")
+ TEGRA210_MUX_ROUTES("MIXER1 RX9")
+ TEGRA210_MUX_ROUTES("MIXER1 RX10")
};
static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
@@ -501,6 +972,42 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
TEGRA186_MUX_ROUTES("I2S6")
TEGRA186_MUX_ROUTES("DSPK1")
TEGRA186_MUX_ROUTES("DSPK2")
+ TEGRA186_MUX_ROUTES("SFC1")
+ TEGRA186_MUX_ROUTES("SFC2")
+ TEGRA186_MUX_ROUTES("SFC3")
+ TEGRA186_MUX_ROUTES("SFC4")
+ TEGRA186_MUX_ROUTES("MVC1")
+ TEGRA186_MUX_ROUTES("MVC2")
+ TEGRA186_MUX_ROUTES("AMX1 RX1")
+ TEGRA186_MUX_ROUTES("AMX1 RX2")
+ TEGRA186_MUX_ROUTES("AMX1 RX3")
+ TEGRA186_MUX_ROUTES("AMX1 RX4")
+ TEGRA186_MUX_ROUTES("AMX2 RX1")
+ TEGRA186_MUX_ROUTES("AMX2 RX2")
+ TEGRA186_MUX_ROUTES("AMX2 RX3")
+ TEGRA186_MUX_ROUTES("AMX2 RX4")
+ TEGRA186_MUX_ROUTES("AMX3 RX1")
+ TEGRA186_MUX_ROUTES("AMX3 RX2")
+ TEGRA186_MUX_ROUTES("AMX3 RX3")
+ TEGRA186_MUX_ROUTES("AMX3 RX4")
+ TEGRA186_MUX_ROUTES("AMX4 RX1")
+ TEGRA186_MUX_ROUTES("AMX4 RX2")
+ TEGRA186_MUX_ROUTES("AMX4 RX3")
+ TEGRA186_MUX_ROUTES("AMX4 RX4")
+ TEGRA186_MUX_ROUTES("ADX1")
+ TEGRA186_MUX_ROUTES("ADX2")
+ TEGRA186_MUX_ROUTES("ADX3")
+ TEGRA186_MUX_ROUTES("ADX4")
+ TEGRA186_MUX_ROUTES("MIXER1 RX1")
+ TEGRA186_MUX_ROUTES("MIXER1 RX2")
+ TEGRA186_MUX_ROUTES("MIXER1 RX3")
+ TEGRA186_MUX_ROUTES("MIXER1 RX4")
+ TEGRA186_MUX_ROUTES("MIXER1 RX5")
+ TEGRA186_MUX_ROUTES("MIXER1 RX6")
+ TEGRA186_MUX_ROUTES("MIXER1 RX7")
+ TEGRA186_MUX_ROUTES("MIXER1 RX8")
+ TEGRA186_MUX_ROUTES("MIXER1 RX9")
+ TEGRA186_MUX_ROUTES("MIXER1 RX10")
};
static const struct snd_soc_component_driver tegra210_ahub_component = {
diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c
new file mode 100644
index 000000000000..af9bddfc3120
--- /dev/null
+++ b/sound/soc/tegra/tegra210_amx.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_amx.c - Tegra210 AMX driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_amx.h"
+#include "tegra_cif.h"
+
+/*
+ * The counter is in terms of AHUB clock cycles. If a frame is not
+ * received within these clock cycles, the AMX input channel gets
+ * automatically disabled. For now the counter is calculated as a
+ * function of sample rate (8 kHz) and AHUB clock (49.152 MHz).
+ * If later an accurate number is needed, the counter needs to be
+ * calculated at runtime.
+ *
+ * count = ahub_clk / sample_rate
+ */
+#define TEGRA194_MAX_FRAME_IDLE_COUNT 0x1800
+
+#define AMX_CH_REG(id, reg) ((reg) + ((id) * TEGRA210_AMX_AUDIOCIF_CH_STRIDE))
+
+static const struct reg_default tegra210_amx_reg_defaults[] = {
+ { TEGRA210_AMX_RX_INT_MASK, 0x0000000f},
+ { TEGRA210_AMX_RX1_CIF_CTRL, 0x00007000},
+ { TEGRA210_AMX_RX2_CIF_CTRL, 0x00007000},
+ { TEGRA210_AMX_RX3_CIF_CTRL, 0x00007000},
+ { TEGRA210_AMX_RX4_CIF_CTRL, 0x00007000},
+ { TEGRA210_AMX_TX_INT_MASK, 0x00000001},
+ { TEGRA210_AMX_TX_CIF_CTRL, 0x00007000},
+ { TEGRA210_AMX_CG, 0x1},
+ { TEGRA210_AMX_CFG_RAM_CTRL, 0x00004000},
+};
+
+static void tegra210_amx_write_map_ram(struct tegra210_amx *amx)
+{
+ int i;
+
+ regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL,
+ TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
+ TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN |
+ TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE);
+
+ for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++)
+ regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA,
+ amx->map[i]);
+
+ regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN0, amx->byte_mask[0]);
+ regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN1, amx->byte_mask[1]);
+}
+
+static int tegra210_amx_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+ int err;
+
+ /* Ensure if AMX is disabled */
+ err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_STATUS, val,
+ !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to stop AMX, err = %d\n", err);
+ return err;
+ }
+
+ /*
+ * Soft Reset: Below performs module soft reset which clears
+ * all FSM logic, flushes flow control of FIFO and resets the
+ * state register. It also brings module back to disabled
+ * state (without flushing the data in the pipe).
+ */
+ regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
+ TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
+ TEGRA210_AMX_SOFT_RESET_SOFT_EN);
+
+ err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_SOFT_RESET,
+ val, !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to reset AMX, err = %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_amx_runtime_suspend(struct device *dev)
+{
+ struct tegra210_amx *amx = dev_get_drvdata(dev);
+
+ regcache_cache_only(amx->regmap, true);
+ regcache_mark_dirty(amx->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_amx_runtime_resume(struct device *dev)
+{
+ struct tegra210_amx *amx = dev_get_drvdata(dev);
+
+ regcache_cache_only(amx->regmap, false);
+ regcache_sync(amx->regmap);
+
+ regmap_update_bits(amx->regmap,
+ TEGRA210_AMX_CTRL,
+ TEGRA210_AMX_CTRL_RX_DEP_MASK,
+ TEGRA210_AMX_WAIT_ON_ANY << TEGRA210_AMX_CTRL_RX_DEP_SHIFT);
+
+ tegra210_amx_write_map_ram(amx);
+
+ return 0;
+}
+
+static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
+ int channels, audio_bits;
+ struct tegra_cif_conf cif_conf;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ audio_bits = TEGRA_ACIF_BITS_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ tegra_set_cif(amx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
+
+ if (amx->soc_data->auto_disable) {
+ regmap_write(amx->regmap,
+ AMX_CH_REG(dai->id, TEGRA194_AMX_RX1_FRAME_PERIOD),
+ TEGRA194_MAX_FRAME_IDLE_COUNT);
+ regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1);
+ }
+
+ return tegra210_amx_set_audio_cif(dai, params,
+ AMX_CH_REG(dai->id, TEGRA210_AMX_RX1_CIF_CTRL));
+}
+
+static int tegra210_amx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return tegra210_amx_set_audio_cif(dai, params,
+ TEGRA210_AMX_TX_CIF_CTRL);
+}
+
+static int tegra210_amx_get_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
+ unsigned char *bytes_map = (unsigned char *)&amx->map;
+ int reg = mc->reg;
+ int enabled;
+
+ if (reg > 31)
+ enabled = amx->byte_mask[1] & (1 << (reg - 32));
+ else
+ enabled = amx->byte_mask[0] & (1 << reg);
+
+ if (enabled)
+ ucontrol->value.integer.value[0] = bytes_map[reg];
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tegra210_amx_put_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
+ unsigned char *bytes_map = (unsigned char *)&amx->map;
+ int reg = mc->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ if (value >= 0 && value <= 255) {
+ /* Update byte map and enable slot */
+ bytes_map[reg] = value;
+ if (reg > 31)
+ amx->byte_mask[1] |= (1 << (reg - 32));
+ else
+ amx->byte_mask[0] |= (1 << reg);
+ } else {
+ /* Reset byte map and disable slot */
+ bytes_map[reg] = 0;
+ if (reg > 31)
+ amx->byte_mask[1] &= ~(1 << (reg - 32));
+ else
+ amx->byte_mask[0] &= ~(1 << reg);
+ }
+
+ return 1;
+}
+
+static const struct snd_soc_dai_ops tegra210_amx_out_dai_ops = {
+ .hw_params = tegra210_amx_out_hw_params,
+ .startup = tegra210_amx_startup,
+};
+
+static const struct snd_soc_dai_ops tegra210_amx_in_dai_ops = {
+ .hw_params = tegra210_amx_in_hw_params,
+};
+
+#define IN_DAI(id) \
+ { \
+ .name = "AMX-RX-CIF" #id, \
+ .playback = { \
+ .stream_name = "RX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "RX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_amx_in_dai_ops, \
+ }
+
+#define OUT_DAI \
+ { \
+ .name = "AMX-TX-CIF", \
+ .playback = { \
+ .stream_name = "TX-CIF-Playback", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "TX-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_amx_out_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_amx_dais[] = {
+ IN_DAI(1),
+ IN_DAI(2),
+ IN_DAI(3),
+ IN_DAI(4),
+ OUT_DAI,
+};
+
+static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_AMX_ENABLE,
+ TEGRA210_AMX_ENABLE_SHIFT, 0),
+};
+
+#define STREAM_ROUTES(id, sname) \
+ { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \
+ { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname },\
+ { "RX" #id, NULL, "RX" #id "-CIF-" sname }, \
+ { "TX", NULL, "RX" #id }, \
+ { "TX-CIF-" sname, NULL, "TX" }, \
+ { "XBAR-" sname, NULL, "TX-CIF-" sname }, \
+ { "XBAR-RX", NULL, "XBAR-" sname }
+
+#define AMX_ROUTES(id) \
+ STREAM_ROUTES(id, "Playback"), \
+ STREAM_ROUTES(id, "Capture")
+
+static const struct snd_soc_dapm_route tegra210_amx_routes[] = {
+ AMX_ROUTES(1),
+ AMX_ROUTES(2),
+ AMX_ROUTES(3),
+ AMX_ROUTES(4),
+};
+
+#define TEGRA210_AMX_BYTE_MAP_CTRL(reg) \
+ SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
+ tegra210_amx_get_byte_map, \
+ tegra210_amx_put_byte_map)
+
+static struct snd_kcontrol_new tegra210_amx_controls[] = {
+ TEGRA210_AMX_BYTE_MAP_CTRL(0),
+ TEGRA210_AMX_BYTE_MAP_CTRL(1),
+ TEGRA210_AMX_BYTE_MAP_CTRL(2),
+ TEGRA210_AMX_BYTE_MAP_CTRL(3),
+ TEGRA210_AMX_BYTE_MAP_CTRL(4),
+ TEGRA210_AMX_BYTE_MAP_CTRL(5),
+ TEGRA210_AMX_BYTE_MAP_CTRL(6),
+ TEGRA210_AMX_BYTE_MAP_CTRL(7),
+ TEGRA210_AMX_BYTE_MAP_CTRL(8),
+ TEGRA210_AMX_BYTE_MAP_CTRL(9),
+ TEGRA210_AMX_BYTE_MAP_CTRL(10),
+ TEGRA210_AMX_BYTE_MAP_CTRL(11),
+ TEGRA210_AMX_BYTE_MAP_CTRL(12),
+ TEGRA210_AMX_BYTE_MAP_CTRL(13),
+ TEGRA210_AMX_BYTE_MAP_CTRL(14),
+ TEGRA210_AMX_BYTE_MAP_CTRL(15),
+ TEGRA210_AMX_BYTE_MAP_CTRL(16),
+ TEGRA210_AMX_BYTE_MAP_CTRL(17),
+ TEGRA210_AMX_BYTE_MAP_CTRL(18),
+ TEGRA210_AMX_BYTE_MAP_CTRL(19),
+ TEGRA210_AMX_BYTE_MAP_CTRL(20),
+ TEGRA210_AMX_BYTE_MAP_CTRL(21),
+ TEGRA210_AMX_BYTE_MAP_CTRL(22),
+ TEGRA210_AMX_BYTE_MAP_CTRL(23),
+ TEGRA210_AMX_BYTE_MAP_CTRL(24),
+ TEGRA210_AMX_BYTE_MAP_CTRL(25),
+ TEGRA210_AMX_BYTE_MAP_CTRL(26),
+ TEGRA210_AMX_BYTE_MAP_CTRL(27),
+ TEGRA210_AMX_BYTE_MAP_CTRL(28),
+ TEGRA210_AMX_BYTE_MAP_CTRL(29),
+ TEGRA210_AMX_BYTE_MAP_CTRL(30),
+ TEGRA210_AMX_BYTE_MAP_CTRL(31),
+ TEGRA210_AMX_BYTE_MAP_CTRL(32),
+ TEGRA210_AMX_BYTE_MAP_CTRL(33),
+ TEGRA210_AMX_BYTE_MAP_CTRL(34),
+ TEGRA210_AMX_BYTE_MAP_CTRL(35),
+ TEGRA210_AMX_BYTE_MAP_CTRL(36),
+ TEGRA210_AMX_BYTE_MAP_CTRL(37),
+ TEGRA210_AMX_BYTE_MAP_CTRL(38),
+ TEGRA210_AMX_BYTE_MAP_CTRL(39),
+ TEGRA210_AMX_BYTE_MAP_CTRL(40),
+ TEGRA210_AMX_BYTE_MAP_CTRL(41),
+ TEGRA210_AMX_BYTE_MAP_CTRL(42),
+ TEGRA210_AMX_BYTE_MAP_CTRL(43),
+ TEGRA210_AMX_BYTE_MAP_CTRL(44),
+ TEGRA210_AMX_BYTE_MAP_CTRL(45),
+ TEGRA210_AMX_BYTE_MAP_CTRL(46),
+ TEGRA210_AMX_BYTE_MAP_CTRL(47),
+ TEGRA210_AMX_BYTE_MAP_CTRL(48),
+ TEGRA210_AMX_BYTE_MAP_CTRL(49),
+ TEGRA210_AMX_BYTE_MAP_CTRL(50),
+ TEGRA210_AMX_BYTE_MAP_CTRL(51),
+ TEGRA210_AMX_BYTE_MAP_CTRL(52),
+ TEGRA210_AMX_BYTE_MAP_CTRL(53),
+ TEGRA210_AMX_BYTE_MAP_CTRL(54),
+ TEGRA210_AMX_BYTE_MAP_CTRL(55),
+ TEGRA210_AMX_BYTE_MAP_CTRL(56),
+ TEGRA210_AMX_BYTE_MAP_CTRL(57),
+ TEGRA210_AMX_BYTE_MAP_CTRL(58),
+ TEGRA210_AMX_BYTE_MAP_CTRL(59),
+ TEGRA210_AMX_BYTE_MAP_CTRL(60),
+ TEGRA210_AMX_BYTE_MAP_CTRL(61),
+ TEGRA210_AMX_BYTE_MAP_CTRL(62),
+ TEGRA210_AMX_BYTE_MAP_CTRL(63),
+};
+
+static const struct snd_soc_component_driver tegra210_amx_cmpnt = {
+ .dapm_widgets = tegra210_amx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets),
+ .dapm_routes = tegra210_amx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_amx_routes),
+ .controls = tegra210_amx_controls,
+ .num_controls = ARRAY_SIZE(tegra210_amx_controls),
+};
+
+static bool tegra210_amx_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_INT_MASK ... TEGRA210_AMX_RX4_CIF_CTRL:
+ case TEGRA210_AMX_TX_INT_MASK ... TEGRA210_AMX_CG:
+ case TEGRA210_AMX_CTRL ... TEGRA210_AMX_CYA:
+ case TEGRA210_AMX_CFG_RAM_CTRL ... TEGRA210_AMX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra194_amx_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD:
+ return true;
+ default:
+ return tegra210_amx_wr_reg(dev, reg);
+ }
+}
+
+static bool tegra210_amx_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_STATUS ... TEGRA210_AMX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra194_amx_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD:
+ return true;
+ default:
+ return tegra210_amx_rd_reg(dev, reg);
+ }
+}
+
+static bool tegra210_amx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_STATUS:
+ case TEGRA210_AMX_RX_INT_STATUS:
+ case TEGRA210_AMX_RX_INT_SET:
+ case TEGRA210_AMX_TX_STATUS:
+ case TEGRA210_AMX_TX_INT_STATUS:
+ case TEGRA210_AMX_TX_INT_SET:
+ case TEGRA210_AMX_SOFT_RESET:
+ case TEGRA210_AMX_STATUS:
+ case TEGRA210_AMX_INT_STATUS:
+ case TEGRA210_AMX_CFG_RAM_CTRL:
+ case TEGRA210_AMX_CFG_RAM_DATA:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static const struct regmap_config tegra210_amx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_AMX_CFG_RAM_DATA,
+ .writeable_reg = tegra210_amx_wr_reg,
+ .readable_reg = tegra210_amx_rd_reg,
+ .volatile_reg = tegra210_amx_volatile_reg,
+ .reg_defaults = tegra210_amx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config tegra194_amx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA194_AMX_RX4_LAST_FRAME_PERIOD,
+ .writeable_reg = tegra194_amx_wr_reg,
+ .readable_reg = tegra194_amx_rd_reg,
+ .volatile_reg = tegra210_amx_volatile_reg,
+ .reg_defaults = tegra210_amx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct tegra210_amx_soc_data soc_data_tegra210 = {
+ .regmap_conf = &tegra210_amx_regmap_config,
+};
+
+static const struct tegra210_amx_soc_data soc_data_tegra194 = {
+ .regmap_conf = &tegra194_amx_regmap_config,
+ .auto_disable = true,
+};
+
+static const struct of_device_id tegra210_amx_of_match[] = {
+ { .compatible = "nvidia,tegra210-amx", .data = &soc_data_tegra210 },
+ { .compatible = "nvidia,tegra194-amx", .data = &soc_data_tegra194 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_amx_of_match);
+
+static int tegra210_amx_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_amx *amx;
+ void __iomem *regs;
+ int err;
+ const struct of_device_id *match;
+ struct tegra210_amx_soc_data *soc_data;
+
+ match = of_match_device(tegra210_amx_of_match, dev);
+
+ soc_data = (struct tegra210_amx_soc_data *)match->data;
+
+ amx = devm_kzalloc(dev, sizeof(*amx), GFP_KERNEL);
+ if (!amx)
+ return -ENOMEM;
+
+ amx->soc_data = soc_data;
+
+ dev_set_drvdata(dev, amx);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ amx->regmap = devm_regmap_init_mmio(dev, regs,
+ soc_data->regmap_conf);
+ if (IS_ERR(amx->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(amx->regmap);
+ }
+
+ regcache_cache_only(amx->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_amx_cmpnt,
+ tegra210_amx_dais,
+ ARRAY_SIZE(tegra210_amx_dais));
+ if (err) {
+ dev_err(dev, "can't register AMX component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int 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 = {
+ SET_RUNTIME_PM_OPS(tegra210_amx_runtime_suspend,
+ tegra210_amx_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_amx_driver = {
+ .driver = {
+ .name = "tegra210-amx",
+ .of_match_table = tegra210_amx_of_match,
+ .pm = &tegra210_amx_pm_ops,
+ },
+ .probe = tegra210_amx_platform_probe,
+ .remove = tegra210_amx_platform_remove,
+};
+module_platform_driver(tegra210_amx_driver);
+
+MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 AMX ASoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra210_amx.h b/sound/soc/tegra/tegra210_amx.h
new file mode 100644
index 000000000000..e277741e4258
--- /dev/null
+++ b/sound/soc/tegra/tegra210_amx.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_amx.h - Definitions for Tegra210 AMX driver
+ *
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_AMX_H__
+#define __TEGRA210_AMX_H__
+
+/* Register offsets from TEGRA210_AMX*_BASE */
+#define TEGRA210_AMX_RX_STATUS 0x0c
+#define TEGRA210_AMX_RX_INT_STATUS 0x10
+#define TEGRA210_AMX_RX_INT_MASK 0x14
+#define TEGRA210_AMX_RX_INT_SET 0x18
+#define TEGRA210_AMX_RX_INT_CLEAR 0x1c
+#define TEGRA210_AMX_RX1_CIF_CTRL 0x20
+#define TEGRA210_AMX_RX2_CIF_CTRL 0x24
+#define TEGRA210_AMX_RX3_CIF_CTRL 0x28
+#define TEGRA210_AMX_RX4_CIF_CTRL 0x2c
+#define TEGRA210_AMX_TX_STATUS 0x4c
+#define TEGRA210_AMX_TX_INT_STATUS 0x50
+#define TEGRA210_AMX_TX_INT_MASK 0x54
+#define TEGRA210_AMX_TX_INT_SET 0x58
+#define TEGRA210_AMX_TX_INT_CLEAR 0x5c
+#define TEGRA210_AMX_TX_CIF_CTRL 0x60
+#define TEGRA210_AMX_ENABLE 0x80
+#define TEGRA210_AMX_SOFT_RESET 0x84
+#define TEGRA210_AMX_CG 0x88
+#define TEGRA210_AMX_STATUS 0x8c
+#define TEGRA210_AMX_INT_STATUS 0x90
+#define TEGRA210_AMX_CTRL 0xa4
+#define TEGRA210_AMX_OUT_BYTE_EN0 0xa8
+#define TEGRA210_AMX_OUT_BYTE_EN1 0xac
+#define TEGRA210_AMX_CYA 0xb0
+#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8
+#define TEGRA210_AMX_CFG_RAM_DATA 0xbc
+
+#define TEGRA194_AMX_RX1_FRAME_PERIOD 0xc0
+#define TEGRA194_AMX_RX4_FRAME_PERIOD 0xcc
+#define TEGRA194_AMX_RX4_LAST_FRAME_PERIOD 0xdc
+
+/* Fields in TEGRA210_AMX_ENABLE */
+#define TEGRA210_AMX_ENABLE_SHIFT 0
+
+/* Fields in TEGRA210_AMX_CTRL */
+#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT 14
+#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK (3 << TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT)
+
+#define TEGRA210_AMX_CTRL_RX_DEP_SHIFT 12
+#define TEGRA210_AMX_CTRL_RX_DEP_MASK (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT)
+
+/* Fields in TEGRA210_AMX_CFG_RAM_CTRL */
+#define TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT 14
+#define TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT)
+
+#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
+#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
+
+#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
+#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
+
+#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT 0
+
+/* Fields in TEGRA210_AMX_SOFT_RESET */
+#define TEGRA210_AMX_SOFT_RESET_SOFT_EN 1
+#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK TEGRA210_AMX_SOFT_RESET_SOFT_EN
+
+#define TEGRA210_AMX_AUDIOCIF_CH_STRIDE 4
+#define TEGRA210_AMX_RAM_DEPTH 16
+#define TEGRA210_AMX_MAP_STREAM_NUM_SHIFT 6
+#define TEGRA210_AMX_MAP_WORD_NUM_SHIFT 2
+#define TEGRA210_AMX_MAP_BYTE_NUM_SHIFT 0
+
+enum {
+ TEGRA210_AMX_WAIT_ON_ALL,
+ TEGRA210_AMX_WAIT_ON_ANY,
+};
+
+struct tegra210_amx_soc_data {
+ const struct regmap_config *regmap_conf;
+ bool auto_disable;
+};
+
+struct tegra210_amx {
+ const struct tegra210_amx_soc_data *soc_data;
+ unsigned int map[TEGRA210_AMX_RAM_DEPTH];
+ struct regmap *regmap;
+ unsigned int byte_mask[2];
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c
new file mode 100644
index 000000000000..55e61776c565
--- /dev/null
+++ b/sound/soc/tegra/tegra210_mixer.c
@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_mixer.c - Tegra210 MIXER driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_mixer.h"
+#include "tegra_cif.h"
+
+#define MIXER_REG(reg, id) ((reg) + ((id) * TEGRA210_MIXER_REG_STRIDE))
+#define MIXER_REG_BASE(reg) ((reg) % TEGRA210_MIXER_REG_STRIDE)
+
+#define MIXER_GAIN_CFG_RAM_ADDR(id) \
+ (TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 + \
+ ((id) * TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE))
+
+#define MIXER_RX_REG_DEFAULTS(id) \
+ { MIXER_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \
+ { MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \
+ { MIXER_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0}
+
+#define MIXER_TX_REG_DEFAULTS(id) \
+ { MIXER_REG(TEGRA210_MIXER_TX1_INT_MASK, (id)), 0x00000001}, \
+ { MIXER_REG(TEGRA210_MIXER_TX1_CIF_CTRL, (id)), 0x00007700}
+
+#define REG_DURATION_PARAM(reg, i) ((reg) + NUM_GAIN_POLY_COEFFS + 1 + (i))
+
+static const struct reg_default tegra210_mixer_reg_defaults[] = {
+ /* Inputs */
+ MIXER_RX_REG_DEFAULTS(0),
+ MIXER_RX_REG_DEFAULTS(1),
+ MIXER_RX_REG_DEFAULTS(2),
+ MIXER_RX_REG_DEFAULTS(3),
+ MIXER_RX_REG_DEFAULTS(4),
+ MIXER_RX_REG_DEFAULTS(5),
+ MIXER_RX_REG_DEFAULTS(6),
+ MIXER_RX_REG_DEFAULTS(7),
+ MIXER_RX_REG_DEFAULTS(8),
+ MIXER_RX_REG_DEFAULTS(9),
+ /* Outputs */
+ MIXER_TX_REG_DEFAULTS(0),
+ MIXER_TX_REG_DEFAULTS(1),
+ MIXER_TX_REG_DEFAULTS(2),
+ MIXER_TX_REG_DEFAULTS(3),
+ MIXER_TX_REG_DEFAULTS(4),
+
+ { TEGRA210_MIXER_CG, 0x00000001},
+ { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000},
+ { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000},
+ { TEGRA210_MIXER_ENABLE, 0x1 },
+};
+
+/* Default gain parameters */
+static const struct tegra210_mixer_gain_params gain_params = {
+ /* Polynomial coefficients */
+ { 0, 0, 0, 0, 0, 0, 0, 0x1000000, 0 },
+ /* Gain value */
+ 0x10000,
+ /* Duration Parameters */
+ { 0, 0, 0x400, 0x8000000 },
+};
+
+static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev)
+{
+ struct tegra210_mixer *mixer = dev_get_drvdata(dev);
+
+ regcache_cache_only(mixer->regmap, true);
+ regcache_mark_dirty(mixer->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_mixer_runtime_resume(struct device *dev)
+{
+ struct tegra210_mixer *mixer = dev_get_drvdata(dev);
+
+ regcache_cache_only(mixer->regmap, false);
+ regcache_sync(mixer->regmap);
+
+ return 0;
+}
+
+static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
+ unsigned int addr,
+ unsigned int coef)
+{
+ unsigned int reg, val;
+ int err;
+
+ /* Check if busy */
+ err = regmap_read_poll_timeout(mixer->regmap,
+ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
+ val, !(val & 0x80000000), 10, 10000);
+ if (err < 0)
+ return err;
+
+ reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) &
+ TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK;
+ reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN;
+ reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE;
+ reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN;
+
+ regmap_write(mixer->regmap,
+ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
+ reg);
+ regmap_write(mixer->regmap,
+ TEGRA210_MIXER_GAIN_CFG_RAM_DATA,
+ coef);
+
+ return 0;
+}
+
+static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt,
+ unsigned int id, bool instant_gain)
+{
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg = MIXER_GAIN_CFG_RAM_ADDR(id);
+ int err, i;
+
+ pm_runtime_get_sync(cmpnt->dev);
+
+ /* Write default gain poly coefficients */
+ for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) {
+ err = tegra210_mixer_write_ram(mixer, reg + i,
+ gain_params.poly_coeff[i]);
+
+ if (err < 0)
+ goto rpm_put;
+ }
+
+ /* Write stored gain value */
+ err = tegra210_mixer_write_ram(mixer, reg + NUM_GAIN_POLY_COEFFS,
+ mixer->gain_value[id]);
+ if (err < 0)
+ goto rpm_put;
+
+ /* Write duration parameters */
+ for (i = 0; i < NUM_DURATION_PARMS; i++) {
+ int val;
+
+ if (instant_gain)
+ val = 1;
+ else
+ val = gain_params.duration[i];
+
+ err = tegra210_mixer_write_ram(mixer,
+ REG_DURATION_PARAM(reg, i),
+ val);
+ if (err < 0)
+ goto rpm_put;
+ }
+
+ /* Trigger to apply gain configurations */
+ err = tegra210_mixer_write_ram(mixer, reg + REG_CFG_DONE_TRIGGER,
+ VAL_CFG_DONE_TRIGGER);
+
+rpm_put:
+ pm_runtime_put(cmpnt->dev);
+
+ return err;
+}
+
+static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg = mc->reg;
+ unsigned int i;
+
+ i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
+ TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
+
+ ucontrol->value.integer.value[0] = mixer->gain_value[i];
+
+ return 0;
+}
+
+static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg = mc->reg, id;
+ bool instant_gain = false;
+ int err;
+
+ if (strstr(kcontrol->id.name, "Instant Gain Volume"))
+ instant_gain = true;
+
+ /* Save gain value for specific MIXER input */
+ id = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
+ TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
+
+ mixer->gain_value[id] = ucontrol->value.integer.value[0];
+
+ err = tegra210_mixer_configure_gain(cmpnt, id, instant_gain);
+ if (err) {
+ dev_err(cmpnt->dev, "Failed to apply gain\n");
+ return err;
+ }
+
+ return 1;
+}
+
+static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg,
+ unsigned int id)
+{
+ unsigned int channels, audio_bits;
+ struct tegra_cif_conf cif_conf;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ tegra_set_cif(mixer->regmap,
+ reg + (id * TEGRA210_MIXER_REG_STRIDE),
+ &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
+ int err;
+
+ err = tegra210_mixer_set_audio_cif(mixer, params,
+ TEGRA210_MIXER_RX1_CIF_CTRL,
+ dai->id);
+ if (err < 0)
+ return err;
+
+ return tegra210_mixer_configure_gain(dai->component, dai->id, false);
+}
+
+static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
+
+ return tegra210_mixer_set_audio_cif(mixer, params,
+ TEGRA210_MIXER_TX1_CIF_CTRL,
+ dai->id - TEGRA210_MIXER_RX_MAX);
+}
+
+static const struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = {
+ .hw_params = tegra210_mixer_out_hw_params,
+};
+
+static const struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = {
+ .hw_params = tegra210_mixer_in_hw_params,
+};
+
+#define IN_DAI(id) \
+ { \
+ .name = "MIXER-RX-CIF"#id, \
+ .playback = { \
+ .stream_name = "RX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "RX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_mixer_in_dai_ops, \
+ }
+
+#define OUT_DAI(id) \
+ { \
+ .name = "MIXER-TX-CIF" #id, \
+ .playback = { \
+ .stream_name = "TX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "TX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_mixer_out_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_mixer_dais[] = {
+ /* Mixer Input */
+ IN_DAI(1),
+ IN_DAI(2),
+ IN_DAI(3),
+ IN_DAI(4),
+ IN_DAI(5),
+ IN_DAI(6),
+ IN_DAI(7),
+ IN_DAI(8),
+ IN_DAI(9),
+ IN_DAI(10),
+
+ /* Mixer Output */
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ OUT_DAI(4),
+ OUT_DAI(5),
+};
+
+#define ADDER_CTRL_DECL(name, reg) \
+ static const struct snd_kcontrol_new name[] = { \
+ SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0), \
+ SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0), \
+ SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0), \
+ SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0), \
+ }
+
+ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);
+
+#define GAIN_CTRL(id) \
+ SOC_SINGLE_EXT("RX" #id " Gain Volume", \
+ MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
+ 0x20000, 0, tegra210_mixer_get_gain, \
+ tegra210_mixer_put_gain), \
+ SOC_SINGLE_EXT("RX" #id " Instant Gain Volume", \
+ MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
+ 0x20000, 0, tegra210_mixer_get_gain, \
+ tegra210_mixer_put_gain),
+
+/* Volume controls for all MIXER inputs */
+static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = {
+ GAIN_CTRL(1)
+ GAIN_CTRL(2)
+ GAIN_CTRL(3)
+ GAIN_CTRL(4)
+ GAIN_CTRL(5)
+ GAIN_CTRL(6)
+ GAIN_CTRL(7)
+ GAIN_CTRL(8)
+ GAIN_CTRL(9)
+ GAIN_CTRL(10)
+};
+
+static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0),
+ SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0, adder1,
+ ARRAY_SIZE(adder1)),
+ SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0, adder2,
+ ARRAY_SIZE(adder2)),
+ SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0, adder3,
+ ARRAY_SIZE(adder3)),
+ SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0, adder4,
+ ARRAY_SIZE(adder4)),
+ SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0, adder5,
+ ARRAY_SIZE(adder5)),
+};
+
+#define RX_ROUTES(id, sname) \
+ { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \
+ { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \
+ { "RX" #id, NULL, "RX" #id "-CIF-" sname }
+
+#define MIXER_RX_ROUTES(id) \
+ RX_ROUTES(id, "Playback"), \
+ RX_ROUTES(id, "Capture")
+
+#define ADDER_ROUTES(id, sname) \
+ { "Adder" #id, "RX1", "RX1" }, \
+ { "Adder" #id, "RX2", "RX2" }, \
+ { "Adder" #id, "RX3", "RX3" }, \
+ { "Adder" #id, "RX4", "RX4" }, \
+ { "Adder" #id, "RX5", "RX5" }, \
+ { "Adder" #id, "RX6", "RX6" }, \
+ { "Adder" #id, "RX7", "RX7" }, \
+ { "Adder" #id, "RX8", "RX8" }, \
+ { "Adder" #id, "RX9", "RX9" }, \
+ { "Adder" #id, "RX10", "RX10" }, \
+ { "TX" #id, NULL, "Adder" #id }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } \
+
+#define TX_ROUTES(id, sname) \
+ ADDER_ROUTES(1, sname), \
+ ADDER_ROUTES(2, sname), \
+ ADDER_ROUTES(3, sname), \
+ ADDER_ROUTES(4, sname), \
+ ADDER_ROUTES(5, sname)
+
+#define MIXER_TX_ROUTES(id) \
+ TX_ROUTES(id, "Playback"), \
+ TX_ROUTES(id, "Capture")
+
+static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {
+ /* Input */
+ MIXER_RX_ROUTES(1),
+ MIXER_RX_ROUTES(2),
+ MIXER_RX_ROUTES(3),
+ MIXER_RX_ROUTES(4),
+ MIXER_RX_ROUTES(5),
+ MIXER_RX_ROUTES(6),
+ MIXER_RX_ROUTES(7),
+ MIXER_RX_ROUTES(8),
+ MIXER_RX_ROUTES(9),
+ MIXER_RX_ROUTES(10),
+ /* Output */
+ MIXER_TX_ROUTES(1),
+ MIXER_TX_ROUTES(2),
+ MIXER_TX_ROUTES(3),
+ MIXER_TX_ROUTES(4),
+ MIXER_TX_ROUTES(5),
+};
+
+static const struct snd_soc_component_driver tegra210_mixer_cmpnt = {
+ .dapm_widgets = tegra210_mixer_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_mixer_widgets),
+ .dapm_routes = tegra210_mixer_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_mixer_routes),
+ .controls = tegra210_mixer_gain_ctls,
+ .num_controls = ARRAY_SIZE(tegra210_mixer_gain_ctls),
+};
+
+static bool tegra210_mixer_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_RX_LIMIT)
+ reg = MIXER_REG_BASE(reg);
+ else if (reg < TEGRA210_MIXER_TX_LIMIT)
+ reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_RX1_SOFT_RESET:
+ case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL:
+
+ case TEGRA210_MIXER_TX1_ENABLE:
+ case TEGRA210_MIXER_TX1_SOFT_RESET:
+ case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
+
+ case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG:
+ case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_mixer_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_RX_LIMIT)
+ reg = MIXER_REG_BASE(reg);
+ else if (reg < TEGRA210_MIXER_TX_LIMIT)
+ reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT:
+ case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
+ case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_mixer_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_RX_LIMIT)
+ reg = MIXER_REG_BASE(reg);
+ else if (reg < TEGRA210_MIXER_TX_LIMIT)
+ reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_RX1_SOFT_RESET:
+ case TEGRA210_MIXER_RX1_STATUS:
+
+ case TEGRA210_MIXER_TX1_SOFT_RESET:
+ case TEGRA210_MIXER_TX1_STATUS:
+ case TEGRA210_MIXER_TX1_INT_STATUS:
+ case TEGRA210_MIXER_TX1_INT_SET:
+
+ case TEGRA210_MIXER_SOFT_RESET:
+ case TEGRA210_MIXER_STATUS:
+ case TEGRA210_MIXER_INT_STATUS:
+ case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL:
+ case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
+ case TEGRA210_MIXER_PEAKM_RAM_CTRL:
+ case TEGRA210_MIXER_PEAKM_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_mixer_precious_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
+ case TEGRA210_MIXER_PEAKM_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tegra210_mixer_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_MIXER_CTRL,
+ .writeable_reg = tegra210_mixer_wr_reg,
+ .readable_reg = tegra210_mixer_rd_reg,
+ .volatile_reg = tegra210_mixer_volatile_reg,
+ .precious_reg = tegra210_mixer_precious_reg,
+ .reg_defaults = tegra210_mixer_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_mixer_of_match[] = {
+ { .compatible = "nvidia,tegra210-amixer" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
+
+static int tegra210_mixer_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_mixer *mixer;
+ void __iomem *regs;
+ int err, i;
+
+ mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+ if (!mixer)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, mixer);
+
+ /* Use default gain value for all MIXER inputs */
+ for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
+ mixer->gain_value[i] = gain_params.gain_value;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ mixer->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra210_mixer_regmap_config);
+ if (IS_ERR(mixer->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(mixer->regmap);
+ }
+
+ regcache_cache_only(mixer->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt,
+ tegra210_mixer_dais,
+ ARRAY_SIZE(tegra210_mixer_dais));
+ if (err) {
+ dev_err(dev, "can't register MIXER component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int 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 = {
+ SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend,
+ tegra210_mixer_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_mixer_driver = {
+ .driver = {
+ .name = "tegra210_mixer",
+ .of_match_table = tegra210_mixer_of_match,
+ .pm = &tegra210_mixer_pm_ops,
+ },
+ .probe = tegra210_mixer_platform_probe,
+ .remove = tegra210_mixer_platform_remove,
+};
+module_platform_driver(tegra210_mixer_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra210_mixer.h b/sound/soc/tegra/tegra210_mixer.h
new file mode 100644
index 000000000000..a330530fbc61
--- /dev/null
+++ b/sound/soc/tegra/tegra210_mixer.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_mixer.h - Definitions for Tegra210 MIXER driver
+ *
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_MIXER_H__
+#define __TEGRA210_MIXER_H__
+
+/* XBAR_RX related MIXER offsets */
+#define TEGRA210_MIXER_RX1_SOFT_RESET 0x04
+#define TEGRA210_MIXER_RX1_STATUS 0x10
+#define TEGRA210_MIXER_RX1_CIF_CTRL 0x24
+#define TEGRA210_MIXER_RX1_CTRL 0x28
+#define TEGRA210_MIXER_RX1_PEAK_CTRL 0x2c
+#define TEGRA210_MIXER_RX1_SAMPLE_COUNT 0x30
+
+/* XBAR_TX related MIXER offsets */
+#define TEGRA210_MIXER_TX1_ENABLE 0x280
+#define TEGRA210_MIXER_TX1_SOFT_RESET 0x284
+#define TEGRA210_MIXER_TX1_STATUS 0x290
+#define TEGRA210_MIXER_TX1_INT_STATUS 0x294
+#define TEGRA210_MIXER_TX1_INT_MASK 0x298
+#define TEGRA210_MIXER_TX1_INT_SET 0x29c
+#define TEGRA210_MIXER_TX1_INT_CLEAR 0x2a0
+#define TEGRA210_MIXER_TX1_CIF_CTRL 0x2a4
+#define TEGRA210_MIXER_TX1_ADDER_CONFIG 0x2a8
+
+/* MIXER related offsets */
+#define TEGRA210_MIXER_ENABLE 0x400
+#define TEGRA210_MIXER_SOFT_RESET 0x404
+#define TEGRA210_MIXER_CG 0x408
+#define TEGRA210_MIXER_STATUS 0x410
+#define TEGRA210_MIXER_INT_STATUS 0x414
+#define TEGRA210_MIXER_GAIN_CFG_RAM_CTRL 0x42c
+#define TEGRA210_MIXER_GAIN_CFG_RAM_DATA 0x430
+#define TEGRA210_MIXER_PEAKM_RAM_CTRL 0x434
+#define TEGRA210_MIXER_PEAKM_RAM_DATA 0x438
+#define TEGRA210_MIXER_CTRL 0x43c
+
+#define TEGRA210_MIXER_TX2_ADDER_CONFIG (TEGRA210_MIXER_TX1_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX3_ADDER_CONFIG (TEGRA210_MIXER_TX2_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX4_ADDER_CONFIG (TEGRA210_MIXER_TX3_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX5_ADDER_CONFIG (TEGRA210_MIXER_TX4_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
+
+#define TEGRA210_MIXER_TX2_ENABLE (TEGRA210_MIXER_TX1_ENABLE + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX3_ENABLE (TEGRA210_MIXER_TX2_ENABLE + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX4_ENABLE (TEGRA210_MIXER_TX3_ENABLE + TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX5_ENABLE (TEGRA210_MIXER_TX4_ENABLE + TEGRA210_MIXER_REG_STRIDE)
+
+/* Fields in TEGRA210_MIXER_ENABLE */
+#define TEGRA210_MIXER_ENABLE_SHIFT 0
+#define TEGRA210_MIXER_ENABLE_MASK (1 << TEGRA210_MIXER_ENABLE_SHIFT)
+#define TEGRA210_MIXER_EN (1 << TEGRA210_MIXER_ENABLE_SHIFT)
+
+/* Fields in TEGRA210_MIXER_GAIN_CFG_RAM_CTRL */
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 0x0
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE 0x10
+
+#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT 14
+#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT)
+#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT)
+
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT 13
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT)
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT)
+
+#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT 12
+#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT)
+#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT)
+
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT 0
+#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK (0x1ff << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT)
+
+#define TEGRA210_MIXER_REG_STRIDE 0x40
+#define TEGRA210_MIXER_RX_MAX 10
+#define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_REG_STRIDE)
+#define TEGRA210_MIXER_TX_MAX 5
+#define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_REG_STRIDE))
+
+#define REG_CFG_DONE_TRIGGER 0xf
+#define VAL_CFG_DONE_TRIGGER 0x1
+
+#define NUM_GAIN_POLY_COEFFS 9
+#define NUM_DURATION_PARMS 4
+
+struct tegra210_mixer_gain_params {
+ int poly_coeff[NUM_GAIN_POLY_COEFFS];
+ int gain_value;
+ int duration[NUM_DURATION_PARMS];
+};
+
+struct tegra210_mixer {
+ int gain_value[TEGRA210_MIXER_RX_MAX];
+ struct regmap *regmap;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c
new file mode 100644
index 000000000000..7b9c7006e419
--- /dev/null
+++ b/sound/soc/tegra/tegra210_mvc.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_mvc.c - Tegra210 MVC driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_mvc.h"
+#include "tegra_cif.h"
+
+static const struct reg_default tegra210_mvc_reg_defaults[] = {
+ { TEGRA210_MVC_RX_INT_MASK, 0x00000001},
+ { TEGRA210_MVC_RX_CIF_CTRL, 0x00007700},
+ { TEGRA210_MVC_TX_INT_MASK, 0x00000001},
+ { TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
+ { TEGRA210_MVC_CG, 0x1},
+ { TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
+ { TEGRA210_MVC_INIT_VOL, 0x00800000},
+ { TEGRA210_MVC_TARGET_VOL, 0x00800000},
+ { TEGRA210_MVC_DURATION, 0x000012c0},
+ { TEGRA210_MVC_DURATION_INV, 0x0006d3a0},
+ { TEGRA210_MVC_POLY_N1, 0x0000007d},
+ { TEGRA210_MVC_POLY_N2, 0x00000271},
+ { TEGRA210_MVC_PEAK_CTRL, 0x000012c0},
+ { TEGRA210_MVC_CFG_RAM_CTRL, 0x00004000},
+};
+
+static const struct tegra210_mvc_gain_params gain_params = {
+ .poly_coeff = { 23738319, 659403, -3680,
+ 15546680, 2530732, -120985,
+ 12048422, 5527252, -785042 },
+ .poly_n1 = 16,
+ .poly_n2 = 63,
+ .duration = 150,
+ .duration_inv = 14316558,
+};
+
+static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev)
+{
+ struct tegra210_mvc *mvc = dev_get_drvdata(dev);
+
+ regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &(mvc->ctrl_value));
+
+ regcache_cache_only(mvc->regmap, true);
+ regcache_mark_dirty(mvc->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev)
+{
+ struct tegra210_mvc *mvc = dev_get_drvdata(dev);
+
+ regcache_cache_only(mvc->regmap, false);
+ regcache_sync(mvc->regmap);
+
+ regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value);
+ regmap_update_bits(mvc->regmap,
+ TEGRA210_MVC_SWITCH,
+ TEGRA210_MVC_VOLUME_SWITCH_MASK,
+ TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
+
+ return 0;
+}
+
+static void tegra210_mvc_write_ram(struct regmap *regmap)
+{
+ int i;
+
+ regmap_write(regmap, TEGRA210_MVC_CFG_RAM_CTRL,
+ TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN |
+ TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN |
+ TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE);
+
+ for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++)
+ regmap_write(regmap, TEGRA210_MVC_CFG_RAM_DATA,
+ gain_params.poly_coeff[i]);
+}
+
+static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
+{
+ /*
+ * Volume control read from mixer control is with
+ * 100x scaling; for CURVE_POLY the reg range
+ * is 0-100 (linear, Q24) and for CURVE_LINEAR
+ * it is -120dB to +40dB (Q8)
+ */
+ if (mvc->curve_type == CURVE_POLY) {
+ if (val > 10000)
+ val = 10000;
+ mvc->volume[chan] = ((val * (1<<8)) / 100) << 16;
+ } else {
+ val -= 12000;
+ mvc->volume[chan] = (val * (1<<8)) / 100;
+ }
+}
+
+static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ u8 mute_mask;
+ u32 val;
+
+ pm_runtime_get_sync(cmpnt->dev);
+ regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val);
+ pm_runtime_put(cmpnt->dev);
+
+ mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) &
+ TEGRA210_MUTE_MASK_EN;
+
+ ucontrol->value.integer.value[0] = mute_mask;
+
+ return 0;
+}
+
+static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int value;
+ u8 mute_mask;
+ int err;
+
+ pm_runtime_get_sync(cmpnt->dev);
+
+ /* Check if VOLUME_SWITCH is triggered */
+ err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
+ value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
+ 10, 10000);
+ if (err < 0)
+ goto end;
+
+ mute_mask = ucontrol->value.integer.value[0];
+
+ err = regmap_update_bits(mvc->regmap, mc->reg,
+ TEGRA210_MVC_MUTE_MASK,
+ mute_mask << TEGRA210_MVC_MUTE_SHIFT);
+ if (err < 0)
+ goto end;
+
+ return 1;
+
+end:
+ pm_runtime_put(cmpnt->dev);
+ return err;
+}
+
+static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
+ s32 val = mvc->volume[chan];
+
+ if (mvc->curve_type == CURVE_POLY) {
+ val = ((val >> 16) * 100) >> 8;
+ } else {
+ val = (val * 100) >> 8;
+ val += 12000;
+ }
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg = mc->reg;
+ unsigned int value;
+ u8 chan;
+ int err;
+
+ pm_runtime_get_sync(cmpnt->dev);
+
+ /* Check if VOLUME_SWITCH is triggered */
+ err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
+ value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
+ 10, 10000);
+ if (err < 0)
+ goto end;
+
+ chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
+
+ tegra210_mvc_conv_vol(mvc, chan,
+ ucontrol->value.integer.value[0]);
+
+ /* Configure init volume same as target volume */
+ regmap_write(mvc->regmap,
+ TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
+ mvc->volume[chan]);
+
+ regmap_write(mvc->regmap, reg, mvc->volume[chan]);
+
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
+ TEGRA210_MVC_VOLUME_SWITCH_MASK,
+ TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
+
+ return 1;
+
+end:
+ pm_runtime_put(cmpnt->dev);
+ return err;
+}
+
+static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
+ struct device *dev)
+{
+ int i;
+
+ /* Change volume to default init for new curve type */
+ if (mvc->curve_type == CURVE_POLY) {
+ for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
+ mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
+ } else {
+ for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
+ mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
+ }
+
+ pm_runtime_get_sync(dev);
+
+ /* Program curve type */
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_CURVE_TYPE_MASK,
+ mvc->curve_type <<
+ TEGRA210_MVC_CURVE_TYPE_SHIFT);
+
+ /* Init volume for all channels */
+ for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) {
+ regmap_write(mvc->regmap,
+ TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i),
+ mvc->volume[i]);
+ regmap_write(mvc->regmap,
+ TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i),
+ mvc->volume[i]);
+ }
+
+ /* Trigger volume switch */
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
+ TEGRA210_MVC_VOLUME_SWITCH_MASK,
+ TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
+
+ pm_runtime_put(dev);
+}
+
+static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = mvc->curve_type;
+
+ return 0;
+}
+
+static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ int value;
+
+ regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value);
+ if (value & TEGRA210_MVC_EN) {
+ dev_err(cmpnt->dev,
+ "Curve type can't be set when MVC is running\n");
+ return -EINVAL;
+ }
+
+ if (mvc->curve_type == ucontrol->value.integer.value[0])
+ return 0;
+
+ mvc->curve_type = ucontrol->value.integer.value[0];
+
+ tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
+
+ return 1;
+}
+
+static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ unsigned int channels, audio_bits;
+ struct tegra_cif_conf cif_conf;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ tegra_set_cif(mvc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
+ int err, val;
+
+ /*
+ * Soft Reset: Below performs module soft reset which clears
+ * all FSM logic, flushes flow control of FIFO and resets the
+ * state register. It also brings module back to disabled
+ * state (without flushing the data in the pipe).
+ */
+ regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
+
+ err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET,
+ val, !val, 10, 10000);
+ if (err < 0) {
+ dev_err(dev, "SW reset failed, err = %d\n", err);
+ return err;
+ }
+
+ /* Set RX CIF */
+ err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL);
+ if (err) {
+ dev_err(dev, "Can't set MVC RX CIF: %d\n", err);
+ return err;
+ }
+
+ /* Set TX CIF */
+ err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL);
+ if (err) {
+ dev_err(dev, "Can't set MVC TX CIF: %d\n", err);
+ return err;
+ }
+
+ tegra210_mvc_write_ram(mvc->regmap);
+
+ /* Program poly_n1, poly_n2, duration */
+ regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1);
+ regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2);
+ regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration);
+
+ /* Program duration_inv */
+ regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV,
+ gain_params.duration_inv);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
+ .hw_params = tegra210_mvc_hw_params,
+};
+
+static const char * const tegra210_mvc_curve_type_text[] = {
+ "Poly",
+ "Linear",
+};
+
+static const struct soc_enum tegra210_mvc_curve_type_ctrl =
+ SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text);
+
+#define TEGRA210_MVC_VOL_CTRL(chan) \
+ SOC_SINGLE_EXT("Channel" #chan " Volume", \
+ TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \
+ (chan - 1)), \
+ 0, 16000, 0, tegra210_mvc_get_vol, \
+ tegra210_mvc_put_vol)
+
+static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
+ /* Per channel volume control */
+ TEGRA210_MVC_VOL_CTRL(1),
+ TEGRA210_MVC_VOL_CTRL(2),
+ TEGRA210_MVC_VOL_CTRL(3),
+ TEGRA210_MVC_VOL_CTRL(4),
+ TEGRA210_MVC_VOL_CTRL(5),
+ TEGRA210_MVC_VOL_CTRL(6),
+ TEGRA210_MVC_VOL_CTRL(7),
+ TEGRA210_MVC_VOL_CTRL(8),
+
+ /* Per channel mute */
+ SOC_SINGLE_EXT("Per Chan Mute Mask",
+ TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
+ tegra210_mvc_get_mute, tegra210_mvc_put_mute),
+
+ SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
+ tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
+};
+
+static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
+ /* Input */
+ {
+ .name = "MVC-RX-CIF",
+ .playback = {
+ .stream_name = "RX-CIF-Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "RX-CIF-Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ },
+
+ /* Output */
+ {
+ .name = "MVC-TX-CIF",
+ .playback = {
+ .stream_name = "TX-CIF-Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "TX-CIF-Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tegra210_mvc_dai_ops,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE,
+ TEGRA210_MVC_EN_SHIFT, 0),
+};
+
+#define MVC_ROUTES(sname) \
+ { "RX XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX-CIF-" sname, NULL, "TX" }, \
+ { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \
+ { "XBAR-RX", NULL, "TX XBAR-" sname }
+
+static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
+ { "TX", NULL, "RX" },
+ MVC_ROUTES("Playback"),
+ MVC_ROUTES("Capture"),
+};
+
+static const struct snd_soc_component_driver tegra210_mvc_cmpnt = {
+ .dapm_widgets = tegra210_mvc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets),
+ .dapm_routes = tegra210_mvc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes),
+ .controls = tegra210_mvc_vol_ctrl,
+ .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl),
+};
+
+static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL:
+ case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL:
+ case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG:
+ case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MVC_RX_STATUS:
+ case TEGRA210_MVC_RX_INT_STATUS:
+ case TEGRA210_MVC_RX_INT_SET:
+
+ case TEGRA210_MVC_TX_STATUS:
+ case TEGRA210_MVC_TX_INT_STATUS:
+ case TEGRA210_MVC_TX_INT_SET:
+
+ case TEGRA210_MVC_SOFT_RESET:
+ case TEGRA210_MVC_STATUS:
+ case TEGRA210_MVC_INT_STATUS:
+ case TEGRA210_MVC_SWITCH:
+ case TEGRA210_MVC_CFG_RAM_CTRL:
+ case TEGRA210_MVC_CFG_RAM_DATA:
+ case TEGRA210_MVC_PEAK_VALUE:
+ case TEGRA210_MVC_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tegra210_mvc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_MVC_CONFIG_ERR_TYPE,
+ .writeable_reg = tegra210_mvc_wr_reg,
+ .readable_reg = tegra210_mvc_rd_reg,
+ .volatile_reg = tegra210_mvc_volatile_reg,
+ .reg_defaults = tegra210_mvc_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_mvc_of_match[] = {
+ { .compatible = "nvidia,tegra210-mvc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
+
+static int tegra210_mvc_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_mvc *mvc;
+ void __iomem *regs;
+ int err;
+
+ mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL);
+ if (!mvc)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, mvc);
+
+ mvc->curve_type = CURVE_LINEAR;
+ mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ mvc->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra210_mvc_regmap_config);
+ if (IS_ERR(mvc->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(mvc->regmap);
+ }
+
+ regcache_cache_only(mvc->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt,
+ tegra210_mvc_dais,
+ ARRAY_SIZE(tegra210_mvc_dais));
+ if (err) {
+ dev_err(dev, "can't register MVC component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
+
+ return 0;
+}
+
+static int 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 = {
+ SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend,
+ tegra210_mvc_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_mvc_driver = {
+ .driver = {
+ .name = "tegra210-mvc",
+ .of_match_table = tegra210_mvc_of_match,
+ .pm = &tegra210_mvc_pm_ops,
+ },
+ .probe = tegra210_mvc_platform_probe,
+ .remove = tegra210_mvc_platform_remove,
+};
+module_platform_driver(tegra210_mvc_driver)
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h
new file mode 100644
index 000000000000..def29c4c7257
--- /dev/null
+++ b/sound/soc/tegra/tegra210_mvc.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_mvc.h - Definitions for Tegra210 MVC driver
+ *
+ * Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_MVC_H__
+#define __TEGRA210_MVC_H__
+
+/*
+ * MVC_RX registers are with respect to XBAR.
+ * The data comes from XBAR to MVC.
+ */
+#define TEGRA210_MVC_RX_STATUS 0x0c
+#define TEGRA210_MVC_RX_INT_STATUS 0x10
+#define TEGRA210_MVC_RX_INT_MASK 0x14
+#define TEGRA210_MVC_RX_INT_SET 0x18
+#define TEGRA210_MVC_RX_INT_CLEAR 0x1c
+#define TEGRA210_MVC_RX_CIF_CTRL 0x20
+
+/*
+ * MVC_TX registers are with respect to XBAR.
+ * The data goes out of MVC.
+ */
+#define TEGRA210_MVC_TX_STATUS 0x4c
+#define TEGRA210_MVC_TX_INT_STATUS 0x50
+#define TEGRA210_MVC_TX_INT_MASK 0x54
+#define TEGRA210_MVC_TX_INT_SET 0x58
+#define TEGRA210_MVC_TX_INT_CLEAR 0x5c
+#define TEGRA210_MVC_TX_CIF_CTRL 0x60
+
+/* Register offsets from TEGRA210_MVC*_BASE */
+#define TEGRA210_MVC_ENABLE 0x80
+#define TEGRA210_MVC_SOFT_RESET 0x84
+#define TEGRA210_MVC_CG 0x88
+#define TEGRA210_MVC_STATUS 0x90
+#define TEGRA210_MVC_INT_STATUS 0x94
+#define TEGRA210_MVC_CTRL 0xa8
+#define TEGRA210_MVC_SWITCH 0xac
+#define TEGRA210_MVC_INIT_VOL 0xb0
+#define TEGRA210_MVC_TARGET_VOL 0xd0
+#define TEGRA210_MVC_DURATION 0xf0
+#define TEGRA210_MVC_DURATION_INV 0xf4
+#define TEGRA210_MVC_POLY_N1 0xf8
+#define TEGRA210_MVC_POLY_N2 0xfc
+#define TEGRA210_MVC_PEAK_CTRL 0x100
+#define TEGRA210_MVC_CFG_RAM_CTRL 0x104
+#define TEGRA210_MVC_CFG_RAM_DATA 0x108
+#define TEGRA210_MVC_PEAK_VALUE 0x10c
+#define TEGRA210_MVC_CONFIG_ERR_TYPE 0x12c
+
+/* Fields in TEGRA210_MVC_ENABLE */
+#define TEGRA210_MVC_EN_SHIFT 0
+#define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT)
+
+#define TEGRA210_MVC_MUTE_SHIFT 8
+#define TEGRA210_MUTE_MASK_EN 0xff
+#define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
+#define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
+
+#define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30
+#define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
+#define TEGRA210_MVC_PER_CHAN_CTRL_EN (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
+
+#define TEGRA210_MVC_CURVE_TYPE_SHIFT 1
+#define TEGRA210_MVC_CURVE_TYPE_MASK (1 << TEGRA210_MVC_CURVE_TYPE_SHIFT)
+
+#define TEGRA210_MVC_VOLUME_SWITCH_SHIFT 2
+#define TEGRA210_MVC_VOLUME_SWITCH_MASK (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT)
+#define TEGRA210_MVC_VOLUME_SWITCH_TRIGGER (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT)
+#define TEGRA210_MVC_CTRL_DEFAULT 0x40000003
+
+#define TEGRA210_MVC_INIT_VOL_DEFAULT_POLY 0x01000000
+#define TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR 0x00000000
+
+/* Fields in TEGRA210_MVC ram ctrl */
+#define TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT 14
+#define TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT)
+
+#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
+#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
+
+#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
+#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
+
+#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT 0
+#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK (0x1ff << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT)
+
+#define REG_SIZE 4
+#define TEGRA210_MVC_MAX_CHAN_COUNT 8
+#define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i))
+
+#define NUM_GAIN_POLY_COEFFS 9
+
+enum {
+ CURVE_POLY,
+ CURVE_LINEAR,
+};
+
+struct tegra210_mvc_gain_params {
+ int poly_coeff[NUM_GAIN_POLY_COEFFS];
+ int poly_n1;
+ int poly_n2;
+ int duration;
+ int duration_inv;
+};
+
+struct tegra210_mvc {
+ int volume[TEGRA210_MVC_MAX_CHAN_COUNT];
+ unsigned int curve_type;
+ unsigned int ctrl_value;
+ struct regmap *regmap;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c
new file mode 100644
index 000000000000..dc477ee1b82c
--- /dev/null
+++ b/sound/soc/tegra/tegra210_sfc.c
@@ -0,0 +1,3549 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_sfc.c - Tegra210 SFC driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_sfc.h"
+#include "tegra_cif.h"
+
+#define UNSUPP_CONV ((void *)(-EOPNOTSUPP))
+#define BYPASS_CONV NULL
+
+static const struct reg_default tegra210_sfc_reg_defaults[] = {
+ { TEGRA210_SFC_RX_INT_MASK, 0x00000001},
+ { TEGRA210_SFC_RX_CIF_CTRL, 0x00007700},
+ { TEGRA210_SFC_TX_INT_MASK, 0x00000001},
+ { TEGRA210_SFC_TX_CIF_CTRL, 0x00007700},
+ { TEGRA210_SFC_CG, 0x1},
+ { TEGRA210_SFC_CFG_RAM_CTRL, 0x00004000},
+};
+
+static const int tegra210_sfc_rates[TEGRA210_SFC_NUM_RATES] = {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+/* coeff RAM tables required for SFC */
+static u32 coef_8to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00235204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000001//output gain
+};
+
+static u32 coef_8to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_8to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00230204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000001//output gain
+};
+
+static u32 coef_8to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0000a105,//header
+ 0x000005e1,//input gain
+ 0x00dca92f, 0xff45647a, 0x0046b59c,
+ 0x00429d1e, 0xff4fec62, 0x00516d30,
+ 0xffdea779, 0xff5e08ba, 0x0060185e,
+ 0xffafbab2, 0xff698d5a, 0x006ce3ae,
+ 0xff9a82d2, 0xff704674, 0x007633c5,
+ 0xff923433, 0xff721128, 0x007cff42,
+ 0x00000003//output gain
+};
+
+static u32 coef_8to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_8to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0156105,//interpolation + IIR filter
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000002,//ouptut gain
+ 0x0021a102,//interpolation + IIR filter
+ 0x00000e00,//input gain
+ 0x00e2e000, 0xff6e1a00, 0x002aaa00,
+ 0x00610a00, 0xff5dda00, 0x003ccc00,
+ 0x00163a00, 0xff3c0400, 0x00633200,
+ 0x00000003,//Output gain
+ 0x00000204,//Farrow filter
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_8to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00156105,//interpolation + IIR Filter
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000002,//ouptut gain
+ 0x0000a102,//interpolation + IIR filter
+ 0x00000e00,//input gain
+ 0x00e2e000, 0xff6e1a00, 0x002aaa00,
+ 0x00610a00, 0xff5dda00, 0x003ccc00,
+ 0x00163a00, 0xff3c0400, 0x00633200,
+ 0x00000003//output gain
+};
+
+static u32 coef_8to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x0024a102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000003,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_8to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x0000a102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000003//output gain
+};
+
+static u32 coef_11to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000002,//output gain
+ 0x00239204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_11to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00009204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_11to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_11to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_11to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00009204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_11to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_11to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_11to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002//output gain
+};
+
+static u32 coef_11to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_16to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_16to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000fa103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000003,//output gain
+ 0x001a5204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_16to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00235204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000001//output gain
+};
+
+static u32 coef_16to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0015a105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000003,//output gain
+ 0x00005105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000001//output gain
+};
+
+static u32 coef_16to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_16to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00156105,//interpolation + IIR filter
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000002,//output gain
+ 0x0021a102,//interpolation + IIR filter
+ 0x00000e00,//input gain
+ 0x00e2e000, 0xff6e1a00, 0x002aaa00,
+ 0x00610a00, 0xff5dda00, 0x003ccc00,
+ 0x00163a00, 0xff3c0400, 0x00633200,
+ 0x00000003,//output gain
+ 0x002c0204,//Farrow Filter
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005101,//IIR Filter + Decimator
+ 0x0000203c,//input gain
+ 0x00f52d35, 0xff2e2162, 0x005a21e0,
+ 0x00c6f0f0, 0xff2ecd69, 0x006fa78d,
+ 0x00000001//output gain
+};
+
+static u32 coef_16to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0000a105,//interpolation + IIR Filter
+ 0x00000784,//input gain
+ 0x00cc516e, 0xff2c9639, 0x005ad5b3,
+ 0x0013ad0d, 0xff3d4799, 0x0063ce75,
+ 0xffb6f398, 0xff5138d1, 0x006e9e1f,
+ 0xff9186e5, 0xff5f96a4, 0x0076a86e,
+ 0xff82089c, 0xff676b81, 0x007b9f8a,
+ 0xff7c48a5, 0xff6a31e7, 0x007ebb7b,
+ 0x00000003//output gain
+};
+
+static u32 coef_16to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_16to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0000a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003//output gain
+};
+
+static u32 coef_16to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x0024a102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000003,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_16to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x0000a102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000003//output gain
+};
+
+static u32 coef_22to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000002,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_22to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_22to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000002,//output gain
+ 0x00239204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_22to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00235204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d029,//input gain
+ 0x00f2a98b, 0xff92aa71, 0x001fcd16,
+ 0x00ae9004, 0xffb85140, 0x0041813a,
+ 0x007f8ed1, 0xffd585fc, 0x006a69e6,
+ 0x00000001//output gain
+};
+
+static u32 coef_22to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00009204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_22to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_22to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_22to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_22to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_22to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002//output gain
+};
+
+static u32 coef_22to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_24to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00009105,//header
+ 0x000005e1,//input gain
+ 0x00dca92f, 0xff45647a, 0x0046b59c,
+ 0x00429d1e, 0xff4fec62, 0x00516d30,
+ 0xffdea779, 0xff5e08ba, 0x0060185e,
+ 0xffafbab2, 0xff698d5a, 0x006ce3ae,
+ 0xff9a82d2, 0xff704674, 0x007633c5,
+ 0xff923433, 0xff721128, 0x007cff42,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000f6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x001a5204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00156105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000002,//output gain
+ 0x00009105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d029,//input gain
+ 0x00f2a98b, 0xff92aa71, 0x001fcd16,
+ 0x00ae9004, 0xffb85140, 0x0041813a,
+ 0x007f8ed1, 0xffd585fc, 0x006a69e6,
+ 0x00000002,//output gain
+ 0x001b6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x00265204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00009102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00230204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_24to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x002f0204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x00000138,//input gain
+ 0x00d5d232, 0xff2a3bf8, 0x005a785c,
+ 0x0034001b, 0xff283109, 0x006462a6,
+ 0xffe6746a, 0xff1fb09c, 0x00758a91,
+ 0x00000001//output gain
+};
+
+static u32 coef_24to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_24to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_24to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002//output gain
+};
+
+static u32 coef_32to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000ca102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000003,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x0000d102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000fa103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000003,//output gain
+ 0x001a5204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000ca102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000003,//output gain
+ 0x0000d102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00235204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0015a105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000003,//output gain
+ 0x00005105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00230204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000001//output gain
+};
+
+static u32 coef_32to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x0000a105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000003//output gain
+};
+
+static u32 coef_32to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0018a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003,//output gain
+ 0x00000204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_32to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x0000a102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000003//output gain
+};
+
+static u32 coef_44to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00120104,//IIR Filter
+ 0x00000af2,//input gain
+ 0x0057eebe, 0xff1e9863, 0x00652604,
+ 0xff7206ea, 0xff22ad7e, 0x006d47e1,
+ 0xff42a4d7, 0xff26e722, 0x0075fd83,
+ 0xff352f66, 0xff29312b, 0x007b986b,
+ 0xff310a07, 0xff296f51, 0x007eca7c,
+ 0x00000001,//output gain
+ 0x001d9204,//Farrow Filter + decimation
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005105,//IIR Filter + Decimator
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00126104,//IIR Filter + interpolation
+ 0x00000af2,//input gain
+ 0x0057eebe, 0xff1e9863, 0x00652604,
+ 0xff7206ea, 0xff22ad7e, 0x006d47e1,
+ 0xff42a4d7, 0xff26e722, 0x0075fd83,
+ 0xff352f66, 0xff29312b, 0x007b986b,
+ 0xff310a07, 0xff296f51, 0x007eca7c,
+ 0x00000002,//output gain
+ 0x001d9204,//Farrow Filter + decimation
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005105,//IIR Filter + Decimator
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0000015f,//input gain
+ 0x00a7909c, 0xff241c71, 0x005f5e00,
+ 0xffca77f4, 0xff20dd50, 0x006855eb,
+ 0xff86c552, 0xff18137a, 0x00773648,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000002,//output gain
+ 0x00239204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00235204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d029,//input gain
+ 0x00f2a98b, 0xff92aa71, 0x001fcd16,
+ 0x00ae9004, 0xffb85140, 0x0041813a,
+ 0x007f8ed1, 0xffd585fc, 0x006a69e6,
+ 0x00000001//output gain
+};
+
+static u32 coef_44to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_44to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_44to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_44to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_48to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c9102,//IIR Filter + Decimator
+ 0x00000e00,//input gain
+ 0x00e2e000, 0xff6e1a00, 0x002aaa00,
+ 0x00610a00, 0xff5dda00, 0x003ccc00,
+ 0x00163a00, 0xff3c0400, 0x00633200,
+ 0x00000001,//output gain
+ 0x00005105,//IIR Filter + Decimator
+ 0x0000d649,//input gain
+ 0x00e87afb, 0xff5f69d0, 0x003df3cf,
+ 0x007ce488, 0xff99a5c8, 0x0056a6a0,
+ 0x00344928, 0xffcba3e5, 0x006be470,
+ 0x00137aa7, 0xffe60276, 0x00773410,
+ 0x0005fa2a, 0xfff1ac11, 0x007c795b,
+ 0x00012d36, 0xfff5eca2, 0x007f10ef,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00009105,//IIR Filter + Decimator
+ 0x00000784,//input gain
+ 0x00cc516e, 0xff2c9639, 0x005ad5b3,
+ 0x0013ad0d, 0xff3d4799, 0x0063ce75,
+ 0xffb6f398, 0xff5138d1, 0x006e9e1f,
+ 0xff9186e5, 0xff5f96a4, 0x0076a86e,
+ 0xff82089c, 0xff676b81, 0x007b9f8a,
+ 0xff7c48a5, 0xff6a31e7, 0x007ebb7b,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000f6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x001a5204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00156105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000002,//output gain
+ 0x00009105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d029,//input gain
+ 0x00f2a98b, 0xff92aa71, 0x001fcd16,
+ 0x00ae9004, 0xffb85140, 0x0041813a,
+ 0x007f8ed1, 0xffd585fc, 0x006a69e6,
+ 0x00000002,//output gain
+ 0x001b6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x00265204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00230204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002//output gain
+};
+
+static u32 coef_48to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00246102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x002f0204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x00000138,//input gain
+ 0x00d5d232, 0xff2a3bf8, 0x005a785c,
+ 0x0034001b, 0xff283109, 0x006462a6,
+ 0xffe6746a, 0xff1fb09c, 0x00758a91,
+ 0x00000001//output gain
+};
+
+static u32 coef_48to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000002,//output gain
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_88to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x00000057,//input gain
+ 0x00a8e717, 0xff1c748d, 0x0065b976,
+ 0xffcbccab, 0xff190aff, 0x006cc1cf,
+ 0xff871ce1, 0xff10d878, 0x0078cfc5,
+ 0x00000001,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000001,//output gain
+ 0x00185102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000001,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000001,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000002,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_88to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_88to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002//output gain
+};
+
+static u32 coef_88to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000002,//output gain
+ 0x00186102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_96to8[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c9102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000001,//output gain
+ 0x00185102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to11[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000001,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c9102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00009105,//header
+ 0x00000292,//input gain
+ 0x00e4320a, 0xff41d2d9, 0x004911ac,
+ 0x005dd9e3, 0xff4c7d80, 0x0052103e,
+ 0xfff8ebef, 0xff5b6fab, 0x005f0a0d,
+ 0xffc4b414, 0xff68582c, 0x006b38e5,
+ 0xffabb861, 0xff704bec, 0x0074de52,
+ 0xffa19f4c, 0xff729059, 0x007c7e90,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000f6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x001a5204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000f6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x001a0204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000f6103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002,//output gain
+ 0x001b6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00260204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000001//output gain
+};
+
+static u32 coef_96to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00006103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000002//output gain
+};
+
+static u32 coef_176to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x00000057,//input gain
+ 0x00a8e717, 0xff1c748d, 0x0065b976,
+ 0xffcbccab, 0xff190aff, 0x006cc1cf,
+ 0xff871ce1, 0xff10d878, 0x0078cfc5,
+ 0x00000001,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000001,//output gain
+ 0x00185102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x00000138,//input gain
+ 0x00d5d232, 0xff2a3bf8, 0x005a785c,
+ 0x0034001b, 0xff283109, 0x006462a6,
+ 0xffe6746a, 0xff1fb09c, 0x00758a91,
+ 0x00000001,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x000005f3,//input gain
+ 0x00d816d6, 0xff385383, 0x004fe566,
+ 0x003c548d, 0xff38c23d, 0x005d0b1c,
+ 0xfff02f7d, 0xff31e983, 0x0072d65d,
+ 0x00000001,//output gain
+ 0x00179204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x00001685,//input gain
+ 0x00f53ae9, 0xff52f196, 0x003e3e08,
+ 0x00b9f857, 0xff5d8985, 0x0050070a,
+ 0x008c3e86, 0xff6053f0, 0x006d98ef,
+ 0x00000001,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000001//output gain
+};
+
+static u32 coef_176to192[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000002,//output gain
+ 0x00005204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000
+};
+
+static u32 coef_192to16[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c9102,//header
+ 0x0000007d,//input gain
+ 0x007d1f20, 0xff1a540e, 0x00678bf9,
+ 0xff916625, 0xff16b0ff, 0x006e433a,
+ 0xff5af660, 0xff0eb91f, 0x00797356,
+ 0x00000001,//output gain
+ 0x00185102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to22[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c0102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000001,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to24[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000001,//output gain
+ 0x00185102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to32[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c9102,//header
+ 0x000005d6,//input gain
+ 0x00c6543e, 0xff342935, 0x0052f116,
+ 0x000a1d78, 0xff3330c0, 0x005f88a3,
+ 0xffbee7c0, 0xff2b5ba5, 0x0073eb26,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to44[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00235102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to48[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c5102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001,//output gain
+ 0x00005102,//header
+ 0x0001d727,//input gain
+ 0x00fc2fc7, 0xff9bb27b, 0x001c564c,
+ 0x00e55557, 0xffcadd5b, 0x003d80ba,
+ 0x00d13397, 0xfff232f8, 0x00683337,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to88[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00175204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x000013d9,//input gain
+ 0x00ebd477, 0xff4ce383, 0x0042049d,
+ 0x0089c278, 0xff54414d, 0x00531ded,
+ 0x004a5e07, 0xff53cf41, 0x006efbdc,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to96[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x00005103,//header
+ 0x000001e0,//input gain
+ 0x00de44c0, 0xff380b7f, 0x004ffc73,
+ 0x00494b44, 0xff3d493a, 0x005908bf,
+ 0xffe9a3c8, 0xff425647, 0x006745f7,
+ 0xffc42d61, 0xff40a6c7, 0x00776709,
+ 0x00000001//output gain
+};
+
+static u32 coef_192to176[TEGRA210_SFC_COEF_RAM_DEPTH] = {
+ 0x000c6102,//header
+ 0x000000af,//input gain
+ 0x00c65663, 0xff23d2ce, 0x005f97d6,
+ 0x00086ad6, 0xff20ec4f, 0x00683201,
+ 0xffbbbef6, 0xff184447, 0x00770963,
+ 0x00000002,//output gain
+ 0x00170204,//farrow
+ 0x000aaaab,
+ 0xffaaaaab,
+ 0xfffaaaab,
+ 0x00555555,
+ 0xff600000,
+ 0xfff55555,
+ 0x00155555,
+ 0x00055555,
+ 0xffeaaaab,
+ 0x00200000,
+ 0x00005102,//header
+ 0x0000010a,//input gain
+ 0x00c93dc4, 0xff26f5f6, 0x005d1041,
+ 0x001002c4, 0xff245b76, 0x00666002,
+ 0xffc30a45, 0xff1baecd, 0x00765921,
+ 0x00000001//output gain
+};
+
+/*
+ * Coefficient table for various sample rate conversions. The sample
+ * rates available are as per tegra210_sfc_rates[].
+ */
+static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = {
+ /* Convertions from 8 kHz */
+ {
+ BYPASS_CONV,
+ coef_8to11,
+ coef_8to16,
+ coef_8to22,
+ coef_8to24,
+ coef_8to32,
+ coef_8to44,
+ coef_8to48,
+ coef_8to88,
+ coef_8to96,
+ UNSUPP_CONV,
+ UNSUPP_CONV,
+ },
+ /* Convertions from 11.025 kHz */
+ {
+ coef_11to8,
+ BYPASS_CONV,
+ coef_11to16,
+ coef_11to22,
+ coef_11to24,
+ coef_11to32,
+ coef_11to44,
+ coef_11to48,
+ coef_11to88,
+ coef_11to96,
+ UNSUPP_CONV,
+ UNSUPP_CONV,
+ },
+ /* Convertions from 16 kHz */
+ {
+ coef_16to8,
+ coef_16to11,
+ BYPASS_CONV,
+ coef_16to22,
+ coef_16to24,
+ coef_16to32,
+ coef_16to44,
+ coef_16to48,
+ coef_16to88,
+ coef_16to96,
+ coef_16to176,
+ coef_16to192,
+ },
+ /* Convertions from 22.05 kHz */
+ {
+ coef_22to8,
+ coef_22to11,
+ coef_22to16,
+ BYPASS_CONV,
+ coef_22to24,
+ coef_22to32,
+ coef_22to44,
+ coef_22to48,
+ coef_22to88,
+ coef_22to96,
+ coef_22to176,
+ coef_22to192,
+ },
+ /* Convertions from 24 kHz */
+ {
+ coef_24to8,
+ coef_24to11,
+ coef_24to16,
+ coef_24to22,
+ BYPASS_CONV,
+ coef_24to32,
+ coef_24to44,
+ coef_24to48,
+ coef_24to88,
+ coef_24to96,
+ coef_24to176,
+ coef_24to192,
+ },
+ /* Convertions from 32 kHz */
+ {
+ coef_32to8,
+ coef_32to11,
+ coef_32to16,
+ coef_32to22,
+ coef_32to24,
+ BYPASS_CONV,
+ coef_32to44,
+ coef_32to48,
+ coef_32to88,
+ coef_32to96,
+ coef_32to176,
+ coef_32to192,
+ },
+ /* Convertions from 44.1 kHz */
+ {
+ coef_44to8,
+ coef_44to11,
+ coef_44to16,
+ coef_44to22,
+ coef_44to24,
+ coef_44to32,
+ BYPASS_CONV,
+ coef_44to48,
+ coef_44to88,
+ coef_44to96,
+ coef_44to176,
+ coef_44to192,
+ },
+ /* Convertions from 48 kHz */
+ {
+ coef_48to8,
+ coef_48to11,
+ coef_48to16,
+ coef_48to22,
+ coef_48to24,
+ coef_48to32,
+ coef_48to44,
+ BYPASS_CONV,
+ coef_48to88,
+ coef_48to96,
+ coef_48to176,
+ coef_48to192,
+ },
+ /* Convertions from 88.2 kHz */
+ {
+ coef_88to8,
+ coef_88to11,
+ coef_88to16,
+ coef_88to22,
+ coef_88to24,
+ coef_88to32,
+ coef_88to44,
+ coef_88to48,
+ BYPASS_CONV,
+ coef_88to96,
+ coef_88to176,
+ coef_88to192,
+ },
+ /* Convertions from 96 kHz */
+ { coef_96to8,
+ coef_96to11,
+ coef_96to16,
+ coef_96to22,
+ coef_96to24,
+ coef_96to32,
+ coef_96to44,
+ coef_96to48,
+ coef_96to88,
+ BYPASS_CONV,
+ coef_96to176,
+ coef_96to192,
+ },
+ /* Convertions from 176.4 kHz */
+ {
+ UNSUPP_CONV,
+ UNSUPP_CONV,
+ coef_176to16,
+ coef_176to22,
+ coef_176to24,
+ coef_176to32,
+ coef_176to44,
+ coef_176to48,
+ coef_176to88,
+ coef_176to96,
+ BYPASS_CONV,
+ coef_176to192,
+ },
+ /* Convertions from 192 kHz */
+ {
+ UNSUPP_CONV,
+ UNSUPP_CONV,
+ coef_192to16,
+ coef_192to22,
+ coef_192to24,
+ coef_192to32,
+ coef_192to44,
+ coef_192to48,
+ coef_192to88,
+ coef_192to96,
+ coef_192to176,
+ BYPASS_CONV,
+ },
+};
+
+static int __maybe_unused tegra210_sfc_runtime_suspend(struct device *dev)
+{
+ struct tegra210_sfc *sfc = dev_get_drvdata(dev);
+
+ regcache_cache_only(sfc->regmap, true);
+ regcache_mark_dirty(sfc->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_sfc_runtime_resume(struct device *dev)
+{
+ struct tegra210_sfc *sfc = dev_get_drvdata(dev);
+
+ regcache_cache_only(sfc->regmap, false);
+ regcache_sync(sfc->regmap);
+
+ return 0;
+}
+
+static inline void tegra210_sfc_write_ram(struct regmap *regmap,
+ s32 *data)
+{
+ int i;
+
+ regmap_write(regmap, TEGRA210_SFC_CFG_RAM_CTRL,
+ TEGRA210_SFC_RAM_CTRL_SEQ_ACCESS_EN |
+ TEGRA210_SFC_RAM_CTRL_ADDR_INIT_EN |
+ TEGRA210_SFC_RAM_CTRL_RW_WRITE);
+
+ for (i = 0; i < TEGRA210_SFC_COEF_RAM_DEPTH; i++)
+ regmap_write(regmap, TEGRA210_SFC_CFG_RAM_DATA, data[i]);
+}
+
+static int tegra210_sfc_write_coeff_ram(struct snd_soc_component *cmpnt)
+{
+ struct tegra210_sfc *sfc = dev_get_drvdata(cmpnt->dev);
+ s32 *coeff_ram;
+
+ /* Bypass */
+ if (sfc->srate_in == sfc->srate_out)
+ return 0;
+
+ coeff_ram = coef_addr_table[sfc->srate_in][sfc->srate_out];
+ if (IS_ERR_OR_NULL(coeff_ram)) {
+ dev_err(cmpnt->dev,
+ "Conversion from %d to %d Hz is not supported\n",
+ sfc->srate_in, sfc->srate_out);
+
+ return PTR_ERR_OR_ZERO(coeff_ram);
+ }
+
+ tegra210_sfc_write_ram(sfc->regmap, coeff_ram);
+
+ regmap_update_bits(sfc->regmap,
+ TEGRA210_SFC_COEF_RAM,
+ TEGRA210_SFC_COEF_RAM_EN,
+ TEGRA210_SFC_COEF_RAM_EN);
+
+ return 0;
+}
+
+static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ unsigned int channels, audio_bits, path;
+ struct tegra_cif_conf cif_conf;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = TEGRA_ACIF_BITS_32;
+
+ if (reg == TEGRA210_SFC_RX_CIF_CTRL)
+ path = SFC_RX_PATH;
+ else
+ path = SFC_TX_PATH;
+
+ cif_conf.stereo_conv = sfc->stereo_to_mono[path];
+ cif_conf.mono_conv = sfc->mono_to_stereo[path];
+
+ tegra_set_cif(sfc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc)
+{
+ u32 val;
+
+ /*
+ * Soft Reset: Below performs module soft reset which clears
+ * all FSM logic, flushes flow control of FIFO and resets the
+ * state register. It also brings module back to disabled
+ * state (without flushing the data in the pipe).
+ */
+ regmap_update_bits(sfc->regmap, TEGRA210_SFC_SOFT_RESET,
+ TEGRA210_SFC_SOFT_RESET_EN, 1);
+
+ return regmap_read_poll_timeout(sfc->regmap,
+ TEGRA210_SFC_SOFT_RESET,
+ val,
+ !(val & TEGRA210_SFC_SOFT_RESET_EN),
+ 10, 10000);
+}
+
+static int tegra210_sfc_rate_to_idx(struct device *dev, int rate,
+ int *rate_idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tegra210_sfc_rates); i++) {
+ if (rate == tegra210_sfc_rates[i]) {
+ *rate_idx = i;
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "Sample rate %d Hz is not supported\n", rate);
+
+ return -EOPNOTSUPP;
+}
+
+static int tegra210_sfc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+ int err;
+
+ regmap_update_bits(sfc->regmap, TEGRA210_SFC_COEF_RAM,
+ TEGRA210_SFC_COEF_RAM_EN, 0);
+
+ err = tegra210_sfc_soft_reset(sfc);
+ if (err < 0) {
+ dev_err(dai->dev, "Failed to reset SFC in %s, err = %d\n",
+ __func__, err);
+
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->dev;
+ int err;
+
+ err = tegra210_sfc_rate_to_idx(dev, params_rate(params),
+ &sfc->srate_in);
+ if (err < 0)
+ return err;
+
+ err = tegra210_sfc_set_audio_cif(sfc, params, TEGRA210_SFC_RX_CIF_CTRL);
+ if (err < 0) {
+ dev_err(dev, "Can't set SFC RX CIF: %d\n", err);
+ return err;
+ }
+
+ regmap_write(sfc->regmap, TEGRA210_SFC_RX_FREQ, sfc->srate_in);
+
+ return err;
+}
+
+static int tegra210_sfc_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->dev;
+ int err;
+
+ err = tegra210_sfc_rate_to_idx(dev, params_rate(params),
+ &sfc->srate_out);
+ if (err < 0)
+ return err;
+
+ err = tegra210_sfc_set_audio_cif(sfc, params, TEGRA210_SFC_TX_CIF_CTRL);
+ if (err < 0) {
+ dev_err(dev, "Can't set SFC TX CIF: %d\n", err);
+ return err;
+ }
+
+ regmap_write(sfc->regmap, TEGRA210_SFC_TX_FREQ, sfc->srate_out);
+
+ return 0;
+}
+
+static int tegra210_sfc_init(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ return tegra210_sfc_write_coeff_ram(cmpnt);
+}
+
+static int tegra210_sfc_get_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_component_get_drvdata(cmpnt);
+
+ if (strstr(kcontrol->id.name, "Input Stereo To Mono"))
+ ucontrol->value.integer.value[0] =
+ sfc->stereo_to_mono[SFC_RX_PATH];
+ else if (strstr(kcontrol->id.name, "Input Mono To Stereo"))
+ ucontrol->value.integer.value[0] =
+ sfc->mono_to_stereo[SFC_RX_PATH];
+ else if (strstr(kcontrol->id.name, "Output Stereo To Mono"))
+ ucontrol->value.integer.value[0] =
+ sfc->stereo_to_mono[SFC_TX_PATH];
+ else if (strstr(kcontrol->id.name, "Output Mono To Stereo"))
+ ucontrol->value.integer.value[0] =
+ sfc->mono_to_stereo[SFC_TX_PATH];
+
+ return 0;
+}
+
+static int tegra210_sfc_put_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_component_get_drvdata(cmpnt);
+ int value = ucontrol->value.integer.value[0];
+
+ if (strstr(kcontrol->id.name, "Input Stereo To Mono"))
+ sfc->stereo_to_mono[SFC_RX_PATH] = value;
+ else if (strstr(kcontrol->id.name, "Input Mono To Stereo"))
+ sfc->mono_to_stereo[SFC_RX_PATH] = value;
+ else if (strstr(kcontrol->id.name, "Output Stereo To Mono"))
+ sfc->stereo_to_mono[SFC_TX_PATH] = value;
+ else if (strstr(kcontrol->id.name, "Output Mono To Stereo"))
+ sfc->mono_to_stereo[SFC_TX_PATH] = value;
+ else
+ return 0;
+
+ return 1;
+}
+
+static const struct snd_soc_dai_ops tegra210_sfc_in_dai_ops = {
+ .hw_params = tegra210_sfc_in_hw_params,
+ .startup = tegra210_sfc_startup,
+};
+
+static const struct snd_soc_dai_ops tegra210_sfc_out_dai_ops = {
+ .hw_params = tegra210_sfc_out_hw_params,
+};
+
+static struct snd_soc_dai_driver tegra210_sfc_dais[] = {
+ {
+ .name = "SFC-RX-CIF",
+ .playback = {
+ .stream_name = "RX-CIF-Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "RX-CIF-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tegra210_sfc_in_dai_ops,
+ },
+ {
+ .name = "SFC-TX-CIF",
+ .playback = {
+ .stream_name = "TX-CIF-Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "TX-CIF-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tegra210_sfc_out_dai_ops,
+ },
+};
+
+static const struct snd_soc_dapm_widget tegra210_sfc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, TEGRA210_SFC_ENABLE,
+ TEGRA210_SFC_EN_SHIFT, 0,
+ tegra210_sfc_init, SND_SOC_DAPM_PRE_PMU),
+};
+
+#define RESAMPLE_ROUTE(sname) \
+ { "RX XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX-CIF-" sname, NULL, "TX" }, \
+ { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \
+ { "XBAR-RX", NULL, "TX XBAR-" sname }
+
+static const struct snd_soc_dapm_route tegra210_sfc_routes[] = {
+ { "TX", NULL, "RX" },
+ RESAMPLE_ROUTE("Playback"),
+ RESAMPLE_ROUTE("Capture"),
+};
+
+static const char * const tegra210_sfc_stereo_conv_text[] = {
+ "CH0", "CH1", "AVG",
+};
+
+static const char * const tegra210_sfc_mono_conv_text[] = {
+ "Zero", "Copy",
+};
+
+static const struct soc_enum tegra210_sfc_stereo_conv_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(tegra210_sfc_stereo_conv_text),
+ tegra210_sfc_stereo_conv_text);
+
+static const struct soc_enum tegra210_sfc_mono_conv_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(tegra210_sfc_mono_conv_text),
+ tegra210_sfc_mono_conv_text);
+
+static const struct snd_kcontrol_new tegra210_sfc_controls[] = {
+ SOC_ENUM_EXT("Input Stereo To Mono", tegra210_sfc_stereo_conv_enum,
+ tegra210_sfc_get_control, tegra210_sfc_put_control),
+ SOC_ENUM_EXT("Input Mono To Stereo", tegra210_sfc_mono_conv_enum,
+ tegra210_sfc_get_control, tegra210_sfc_put_control),
+ SOC_ENUM_EXT("Output Stereo To Mono", tegra210_sfc_stereo_conv_enum,
+ tegra210_sfc_get_control, tegra210_sfc_put_control),
+ SOC_ENUM_EXT("Output Mono To Stereo", tegra210_sfc_mono_conv_enum,
+ tegra210_sfc_get_control, tegra210_sfc_put_control),
+};
+
+static const struct snd_soc_component_driver tegra210_sfc_cmpnt = {
+ .dapm_widgets = tegra210_sfc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_sfc_widgets),
+ .dapm_routes = tegra210_sfc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_sfc_routes),
+ .controls = tegra210_sfc_controls,
+ .num_controls = ARRAY_SIZE(tegra210_sfc_controls),
+};
+
+static bool tegra210_sfc_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_RX_INT_MASK ... TEGRA210_SFC_RX_FREQ:
+ case TEGRA210_SFC_TX_INT_MASK ... TEGRA210_SFC_TX_FREQ:
+ case TEGRA210_SFC_ENABLE ... TEGRA210_SFC_CG:
+ case TEGRA210_SFC_COEF_RAM ... TEGRA210_SFC_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_sfc_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_RX_STATUS ... TEGRA210_SFC_RX_FREQ:
+ case TEGRA210_SFC_TX_STATUS ... TEGRA210_SFC_TX_FREQ:
+ case TEGRA210_SFC_ENABLE ... TEGRA210_SFC_INT_STATUS:
+ case TEGRA210_SFC_COEF_RAM ... TEGRA210_SFC_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_sfc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_RX_STATUS:
+ case TEGRA210_SFC_RX_INT_STATUS:
+ case TEGRA210_SFC_RX_INT_SET:
+
+ case TEGRA210_SFC_TX_STATUS:
+ case TEGRA210_SFC_TX_INT_STATUS:
+ case TEGRA210_SFC_TX_INT_SET:
+
+ case TEGRA210_SFC_SOFT_RESET:
+ case TEGRA210_SFC_STATUS:
+ case TEGRA210_SFC_INT_STATUS:
+ case TEGRA210_SFC_CFG_RAM_CTRL:
+ case TEGRA210_SFC_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_sfc_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tegra210_sfc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_SFC_CFG_RAM_DATA,
+ .writeable_reg = tegra210_sfc_wr_reg,
+ .readable_reg = tegra210_sfc_rd_reg,
+ .volatile_reg = tegra210_sfc_volatile_reg,
+ .precious_reg = tegra210_sfc_precious_reg,
+ .reg_defaults = tegra210_sfc_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_sfc_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_sfc_of_match[] = {
+ { .compatible = "nvidia,tegra210-sfc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_sfc_of_match);
+
+static int tegra210_sfc_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_sfc *sfc;
+ void __iomem *regs;
+ int err;
+
+ sfc = devm_kzalloc(dev, sizeof(*sfc), GFP_KERNEL);
+ if (!sfc)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sfc);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ sfc->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra210_sfc_regmap_config);
+ if (IS_ERR(sfc->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(sfc->regmap);
+ }
+
+ regcache_cache_only(sfc->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_sfc_cmpnt,
+ tegra210_sfc_dais,
+ ARRAY_SIZE(tegra210_sfc_dais));
+ if (err) {
+ dev_err(dev, "can't register SFC component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static int 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 = {
+ SET_RUNTIME_PM_OPS(tegra210_sfc_runtime_suspend,
+ tegra210_sfc_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_sfc_driver = {
+ .driver = {
+ .name = "tegra210-sfc",
+ .of_match_table = tegra210_sfc_of_match,
+ .pm = &tegra210_sfc_pm_ops,
+ },
+ .probe = tegra210_sfc_platform_probe,
+ .remove = tegra210_sfc_platform_remove,
+};
+module_platform_driver(tegra210_sfc_driver)
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 SFC ASoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra210_sfc.h b/sound/soc/tegra/tegra210_sfc.h
new file mode 100644
index 000000000000..5a6b66e297d8
--- /dev/null
+++ b/sound/soc/tegra/tegra210_sfc.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_sfc.h - Definitions for Tegra210 SFC driver
+ *
+ * Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_SFC_H__
+#define __TEGRA210_SFC_H__
+
+/*
+ * SFC_RX registers are with respect to XBAR.
+ * The data comes from XBAR to SFC.
+ */
+#define TEGRA210_SFC_RX_STATUS 0x0c
+#define TEGRA210_SFC_RX_INT_STATUS 0x10
+#define TEGRA210_SFC_RX_INT_MASK 0x14
+#define TEGRA210_SFC_RX_INT_SET 0x18
+#define TEGRA210_SFC_RX_INT_CLEAR 0x1c
+#define TEGRA210_SFC_RX_CIF_CTRL 0x20
+#define TEGRA210_SFC_RX_FREQ 0x24
+
+/*
+ * SFC_TX registers are with respect to XBAR.
+ * The data goes out of SFC.
+ */
+#define TEGRA210_SFC_TX_STATUS 0x4c
+#define TEGRA210_SFC_TX_INT_STATUS 0x50
+#define TEGRA210_SFC_TX_INT_MASK 0x54
+#define TEGRA210_SFC_TX_INT_SET 0x58
+#define TEGRA210_SFC_TX_INT_CLEAR 0x5c
+#define TEGRA210_SFC_TX_CIF_CTRL 0x60
+#define TEGRA210_SFC_TX_FREQ 0x64
+
+/* Register offsets from TEGRA210_SFC*_BASE */
+#define TEGRA210_SFC_ENABLE 0x80
+#define TEGRA210_SFC_SOFT_RESET 0x84
+#define TEGRA210_SFC_CG 0x88
+#define TEGRA210_SFC_STATUS 0x8c
+#define TEGRA210_SFC_INT_STATUS 0x90
+#define TEGRA210_SFC_COEF_RAM 0xbc
+#define TEGRA210_SFC_CFG_RAM_CTRL 0xc0
+#define TEGRA210_SFC_CFG_RAM_DATA 0xc4
+
+/* Fields in TEGRA210_SFC_ENABLE */
+#define TEGRA210_SFC_EN_SHIFT 0
+#define TEGRA210_SFC_EN (1 << TEGRA210_SFC_EN_SHIFT)
+
+#define TEGRA210_SFC_NUM_RATES 12
+
+/* Fields in TEGRA210_SFC_COEF_RAM */
+#define TEGRA210_SFC_COEF_RAM_EN BIT(0)
+
+#define TEGRA210_SFC_SOFT_RESET_EN BIT(0)
+
+/* Coefficients */
+#define TEGRA210_SFC_COEF_RAM_DEPTH 64
+#define TEGRA210_SFC_RAM_CTRL_RW_WRITE (1 << 14)
+#define TEGRA210_SFC_RAM_CTRL_ADDR_INIT_EN (1 << 13)
+#define TEGRA210_SFC_RAM_CTRL_SEQ_ACCESS_EN (1 << 12)
+
+
+enum tegra210_sfc_path {
+ SFC_RX_PATH,
+ SFC_TX_PATH,
+ SFC_PATHS,
+};
+
+struct tegra210_sfc {
+ unsigned int mono_to_stereo[SFC_PATHS];
+ unsigned int stereo_to_mono[SFC_PATHS];
+ unsigned int srate_out;
+ unsigned int srate_in;
+ struct regmap *regmap;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c
index 735909310a26..b95438c3dbf7 100644
--- a/sound/soc/tegra/tegra_asoc_machine.c
+++ b/sound/soc/tegra/tegra_asoc_machine.c
@@ -313,7 +313,7 @@ static int tegra_machine_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops tegra_machine_snd_ops = {
+static const struct snd_soc_ops tegra_machine_snd_ops = {
.hw_params = tegra_machine_hw_params,
};
@@ -341,9 +341,34 @@ tegra_machine_parse_phandle(struct device *dev, const char *name)
return np;
}
+static void tegra_machine_unregister_codec(void *pdev)
+{
+ platform_device_unregister(pdev);
+}
+
+static int tegra_machine_register_codec(struct device *dev, const char *name)
+{
+ struct platform_device *pdev;
+ int err;
+
+ if (!name)
+ return 0;
+
+ pdev = platform_device_register_simple(name, -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ err = devm_add_action_or_reset(dev, tegra_machine_unregister_codec,
+ pdev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
int tegra_asoc_machine_probe(struct platform_device *pdev)
{
- struct device_node *np_codec, *np_i2s;
+ struct device_node *np_codec, *np_i2s, *np_ac97;
const struct tegra_asoc_data *asoc;
struct device *dev = &pdev->dev;
struct tegra_machine *machine;
@@ -404,17 +429,30 @@ int tegra_asoc_machine_probe(struct platform_device *pdev)
return err;
}
- np_codec = tegra_machine_parse_phandle(dev, "nvidia,audio-codec");
- if (IS_ERR(np_codec))
- return PTR_ERR(np_codec);
+ if (asoc->set_ac97) {
+ err = tegra_machine_register_codec(dev, asoc->codec_dev_name);
+ if (err)
+ return err;
+
+ np_ac97 = tegra_machine_parse_phandle(dev, "nvidia,ac97-controller");
+ if (IS_ERR(np_ac97))
+ return PTR_ERR(np_ac97);
- np_i2s = tegra_machine_parse_phandle(dev, "nvidia,i2s-controller");
- if (IS_ERR(np_i2s))
- return PTR_ERR(np_i2s);
+ card->dai_link->cpus->of_node = np_ac97;
+ card->dai_link->platforms->of_node = np_ac97;
+ } else {
+ np_codec = tegra_machine_parse_phandle(dev, "nvidia,audio-codec");
+ if (IS_ERR(np_codec))
+ return PTR_ERR(np_codec);
- card->dai_link->cpus->of_node = np_i2s;
- card->dai_link->codecs->of_node = np_codec;
- card->dai_link->platforms->of_node = np_i2s;
+ np_i2s = tegra_machine_parse_phandle(dev, "nvidia,i2s-controller");
+ if (IS_ERR(np_i2s))
+ return PTR_ERR(np_i2s);
+
+ card->dai_link->cpus->of_node = np_i2s;
+ card->dai_link->codecs->of_node = np_codec;
+ card->dai_link->platforms->of_node = np_i2s;
+ }
if (asoc->add_common_controls) {
card->controls = tegra_machine_controls;
@@ -589,6 +627,7 @@ static struct snd_soc_card snd_soc_tegra_wm9712 = {
static const struct tegra_asoc_data tegra_wm9712_data = {
.card = &snd_soc_tegra_wm9712,
.add_common_dapm_widgets = true,
+ .codec_dev_name = "wm9712-codec",
.set_ac97 = true,
};
@@ -686,6 +725,7 @@ static struct snd_soc_dai_link tegra_tlv320aic23_dai = {
};
static struct snd_soc_card snd_soc_tegra_trimslice = {
+ .name = "tegra-trimslice",
.components = "codec:tlv320aic23",
.dai_link = &tegra_tlv320aic23_dai,
.num_links = 1,
diff --git a/sound/soc/tegra/tegra_asoc_machine.h b/sound/soc/tegra/tegra_asoc_machine.h
index 8ee0ec814f67..d6a8d1320551 100644
--- a/sound/soc/tegra/tegra_asoc_machine.h
+++ b/sound/soc/tegra/tegra_asoc_machine.h
@@ -13,6 +13,7 @@ struct snd_soc_pcm_runtime;
struct tegra_asoc_data {
unsigned int (*mclk_rate)(unsigned int srate);
+ const char *codec_dev_name;
struct snd_soc_card *card;
unsigned int mclk_id;
bool hp_jack_gpio_active_low;
diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig
index 1d9fe3fca193..40110e9a9e8a 100644
--- a/sound/soc/ti/Kconfig
+++ b/sound/soc/ti/Kconfig
@@ -212,7 +212,7 @@ config SND_SOC_DM365_VOICE_CODEC
Say Y if you want to add support for SoC On-chip voice codec
endchoice
-config SND_SOC_DM365_VOICE_CODEC_MODULE
+config SND_SOC_DM365_SELECT_VOICE_CODECS
def_tristate y
depends on SND_SOC_DM365_VOICE_CODEC && SND_SOC
select MFD_DAVINCI_VOICECODEC
diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c
index b043a0070d20..68d69e32681a 100644
--- a/sound/soc/ti/davinci-evm.c
+++ b/sound/soc/ti/davinci-evm.c
@@ -73,7 +73,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops evm_ops = {
+static const struct snd_soc_ops evm_ops = {
.startup = evm_startup,
.shutdown = evm_shutdown,
.hw_params = evm_hw_params,
diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c
index 2e3d1eea77c1..da809c7f25a4 100644
--- a/sound/soc/ti/omap-abe-twl6040.c
+++ b/sound/soc/ti/omap-abe-twl6040.c
@@ -96,7 +96,7 @@ static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops omap_abe_dmic_ops = {
+static const struct snd_soc_ops omap_abe_dmic_ops = {
.hw_params = omap_abe_dmic_hw_params,
};
diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c
index 2c39c7a2fd7d..3e654e708f78 100644
--- a/sound/soc/ux500/mop500_ab8500.c
+++ b/sound/soc/ux500/mop500_ab8500.c
@@ -348,7 +348,7 @@ static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-struct snd_soc_ops mop500_ab8500_ops[] = {
+const struct snd_soc_ops mop500_ab8500_ops[] = {
{
.hw_params = mop500_ab8500_hw_params,
.hw_free = mop500_ab8500_hw_free,
diff --git a/sound/soc/ux500/mop500_ab8500.h b/sound/soc/ux500/mop500_ab8500.h
index 8138a4e9aaf5..087ef246d87d 100644
--- a/sound/soc/ux500/mop500_ab8500.h
+++ b/sound/soc/ux500/mop500_ab8500.h
@@ -11,7 +11,7 @@
#ifndef MOP500_AB8500_H
#define MOP500_AB8500_H
-extern struct snd_soc_ops mop500_ab8500_ops[];
+extern const struct snd_soc_ops mop500_ab8500_ops[];
int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd);
void mop500_ab8500_remove(struct snd_soc_card *card);
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
index 43a2a62d66f7..49629d4bb327 100644
--- a/sound/usb/6fire/comm.c
+++ b/sound/usb/6fire/comm.c
@@ -95,7 +95,7 @@ static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
int actual_len;
ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
- buffer, buffer[1] + 2, &actual_len, HZ);
+ buffer, buffer[1] + 2, &actual_len, 1000);
if (ret < 0)
return ret;
else if (actual_len != buffer[1] + 2)
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index 8981e61f2da4..c51abc54d2f8 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -160,7 +160,7 @@ static int usb6fire_fw_ezusb_write(struct usb_device *device,
{
return usb_control_msg_send(device, 0, type,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, 0, data, len, HZ, GFP_KERNEL);
+ value, 0, data, len, 1000, GFP_KERNEL);
}
static int usb6fire_fw_ezusb_read(struct usb_device *device,
@@ -168,7 +168,7 @@ static int usb6fire_fw_ezusb_read(struct usb_device *device,
{
return usb_control_msg_recv(device, 0, type,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, 0, data, len, HZ, GFP_KERNEL);
+ value, 0, data, len, 1000, GFP_KERNEL);
}
static int usb6fire_fw_fpga_write(struct usb_device *device,
@@ -178,7 +178,7 @@ static int usb6fire_fw_fpga_write(struct usb_device *device,
int ret;
ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,
- &actual_len, HZ);
+ &actual_len, 1000);
if (ret < 0)
return ret;
else if (actual_len != len)
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 5b19901f305a..87f042d06ce0 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -74,8 +74,9 @@ struct snd_usb_endpoint {
atomic_t state; /* running state */
- void (*prepare_data_urb) (struct snd_usb_substream *subs,
- struct urb *urb);
+ int (*prepare_data_urb) (struct snd_usb_substream *subs,
+ struct urb *urb,
+ bool in_stream_lock);
void (*retire_data_urb) (struct snd_usb_substream *subs,
struct urb *urb);
@@ -94,9 +95,9 @@ struct snd_usb_endpoint {
struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */
unsigned int nurbs; /* # urbs */
- unsigned int nominal_queue_size; /* total buffer sizes in URBs */
unsigned long active_mask; /* bitmask of active urbs */
unsigned long unlink_mask; /* bitmask of unlinked urbs */
+ atomic_t submitted_urbs; /* currently submitted urbs */
char *syncbuf; /* sync buffer for all sync URBs */
dma_addr_t sync_dma; /* DMA address of syncbuf */
@@ -125,6 +126,7 @@ struct snd_usb_endpoint {
int skip_packets; /* quirks for devices to ignore the first n packets
in a stream */
bool implicit_fb_sync; /* syncs with implicit feedback */
+ bool lowlatency_playback; /* low-latency playback mode */
bool need_setup; /* (re-)need for configure? */
/* for hw constraints */
@@ -136,6 +138,7 @@ struct snd_usb_endpoint {
unsigned int cur_period_frames;
unsigned int cur_period_bytes;
unsigned int cur_buffer_periods;
+ unsigned char cur_clock;
spinlock_t lock;
struct list_head list;
@@ -188,7 +191,7 @@ struct snd_usb_substream {
} dsd_dop;
bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
- bool early_playback_start; /* early start needed for playback? */
+ bool lowlatency_playback; /* low-latency playback mode */
struct media_ctl *media_ctl;
};
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 81d5ce07d548..4dfe76416794 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -271,7 +271,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
return -EINVAL;
}
- /* first, see if the ID we're looking for is a clock source already */
+ /* first, see if the ID we're looking at is a clock source already */
source = snd_usb_find_clock_source(chip, entity_id, proto);
if (source) {
entity_id = GET_VAL(source, proto, bClockID);
@@ -297,7 +297,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
goto find_source;
}
- /* the entity ID we are looking for is a selector.
+ /* the entity ID we are looking at is a selector.
* find out what it currently selects */
ret = uac_clock_selector_get_val(chip, clock_id);
if (ret < 0) {
@@ -496,6 +496,10 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
union uac23_clock_source_desc *cs_desc;
cs_desc = snd_usb_find_clock_source(chip, clock, fmt->protocol);
+
+ if (!cs_desc)
+ return 0;
+
if (fmt->protocol == UAC_VERSION_3)
bmControls = le32_to_cpu(cs_desc->v3.bmControls);
else
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 533919a28856..743b8287cfcd 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
* This won't be used for implicit feedback which takes the packet size
* returned from the sync source
*/
-static int slave_next_packet_size(struct snd_usb_endpoint *ep)
+static int slave_next_packet_size(struct snd_usb_endpoint *ep,
+ unsigned int avail)
{
unsigned long flags;
+ unsigned int phase;
int ret;
if (ep->fill_max)
return ep->maxframesize;
spin_lock_irqsave(&ep->lock, flags);
- ep->phase = (ep->phase & 0xffff)
- + (ep->freqm << ep->datainterval);
- ret = min(ep->phase >> 16, ep->maxframesize);
+ phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
+ ret = min(phase >> 16, ep->maxframesize);
+ if (avail && ret >= avail)
+ ret = -EAGAIN;
+ else
+ ep->phase = phase;
spin_unlock_irqrestore(&ep->lock, flags);
return ret;
@@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep)
* Return the number of samples to be sent in the next packet
* for adaptive and synchronous endpoints
*/
-static int next_packet_size(struct snd_usb_endpoint *ep)
+static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail)
{
+ unsigned int sample_accum;
int ret;
if (ep->fill_max)
return ep->maxframesize;
- ep->sample_accum += ep->sample_rem;
- if (ep->sample_accum >= ep->pps) {
- ep->sample_accum -= ep->pps;
+ sample_accum = ep->sample_accum + ep->sample_rem;
+ if (sample_accum >= ep->pps) {
+ sample_accum -= ep->pps;
ret = ep->packsize[1];
} else {
ret = ep->packsize[0];
}
+ if (avail && ret >= avail)
+ ret = -EAGAIN;
+ else
+ ep->sample_accum = sample_accum;
return ret;
}
@@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep)
/*
* snd_usb_endpoint_next_packet_size: Return the number of samples to be sent
* in the next packet
+ *
+ * If the size is equal or exceeds @avail, don't proceed but return -EAGAIN
+ * Exception: @avail = 0 for skipping the check.
*/
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx, int idx)
+ struct snd_urb_ctx *ctx, int idx,
+ unsigned int avail)
{
- if (ctx->packet_size[idx])
- return ctx->packet_size[idx];
- else if (ep->sync_source)
- return slave_next_packet_size(ep);
+ unsigned int packet;
+
+ packet = ctx->packet_size[idx];
+ if (packet) {
+ if (avail && packet >= avail)
+ return -EAGAIN;
+ return packet;
+ }
+
+ if (ep->sync_source)
+ return slave_next_packet_size(ep, avail);
else
- return next_packet_size(ep);
+ return next_packet_size(ep, avail);
}
static void call_retire_callback(struct snd_usb_endpoint *ep,
@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
unsigned int length;
int counts;
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
length = counts * ep->stride; /* number of silent bytes */
offset = offs * ep->stride + extra * i;
urb->iso_frame_desc[i].offset = offset;
@@ -286,8 +307,9 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
-static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx)
+static int prepare_outbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx,
+ bool in_stream_lock)
{
struct urb *urb = ctx->urb;
unsigned char *cp = urb->transfer_buffer;
@@ -299,9 +321,9 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
case SND_USB_ENDPOINT_TYPE_DATA:
data_subs = READ_ONCE(ep->data_subs);
if (data_subs && ep->prepare_data_urb)
- ep->prepare_data_urb(data_subs, urb);
- else /* no data provider, so send silence */
- prepare_silent_urb(ep, ctx);
+ return ep->prepare_data_urb(data_subs, urb, in_stream_lock);
+ /* no data provider, so send silence */
+ prepare_silent_urb(ep, ctx);
break;
case SND_USB_ENDPOINT_TYPE_SYNC:
@@ -330,13 +352,14 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
break;
}
+ return 0;
}
/*
* Prepare a CAPTURE or SYNC urb for submission to the bus.
*/
-static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *urb_ctx)
+static int prepare_inbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
{
int i, offs;
struct urb *urb = urb_ctx->urb;
@@ -361,6 +384,7 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
urb->iso_frame_desc[0].offset = 0;
break;
}
+ return 0;
}
/* notify an error as XRUN to the assigned PCM data substream */
@@ -396,6 +420,16 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
return p;
}
+static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ spin_unlock_irqrestore(&ep->lock, flags);
+}
+
/*
* Send output urbs that have been prepared previously. URBs are dequeued
* from ep->ready_playback_urbs and in case there aren't any available
@@ -406,12 +440,14 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
* is that host controllers don't guarantee the order in which they return
* inbound and outbound packets to their submitters.
*
- * This function is only used for implicit feedback endpoints. For endpoints
- * driven by dedicated sync endpoints, URBs are immediately re-submitted
- * from their completion handler.
+ * This function is used both for implicit feedback endpoints and in low-
+ * latency playback mode.
*/
-static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
+ bool in_stream_lock)
{
+ bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep);
+
while (ep_state_running(ep)) {
unsigned long flags;
@@ -420,14 +456,14 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
int err, i;
spin_lock_irqsave(&ep->lock, flags);
- if (ep->next_packet_queued > 0 &&
+ if ((!implicit_fb || ep->next_packet_queued > 0) &&
!list_empty(&ep->ready_playback_urbs)) {
/* take URB out of FIFO */
ctx = list_first_entry(&ep->ready_playback_urbs,
struct snd_urb_ctx, ready_list);
list_del_init(&ctx->ready_list);
-
- packet = next_packet_fifo_dequeue(ep);
+ if (implicit_fb)
+ packet = next_packet_fifo_dequeue(ep);
}
spin_unlock_irqrestore(&ep->lock, flags);
@@ -435,11 +471,24 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
return;
/* copy over the length information */
- for (i = 0; i < packet->packets; i++)
- ctx->packet_size[i] = packet->packet_size[i];
+ if (implicit_fb) {
+ for (i = 0; i < packet->packets; i++)
+ ctx->packet_size[i] = packet->packet_size[i];
+ }
/* call the data handler to fill in playback data */
- prepare_outbound_urb(ep, ctx);
+ err = prepare_outbound_urb(ep, ctx, in_stream_lock);
+ /* can be stopped during prepare callback */
+ if (unlikely(!ep_state_running(ep)))
+ break;
+ if (err < 0) {
+ /* push back to ready list again for -EAGAIN */
+ if (err == -EAGAIN)
+ push_back_to_ready_list(ep, ctx);
+ else
+ notify_xrun(ep);
+ return;
+ }
err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
if (err < 0) {
@@ -451,6 +500,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
}
set_bit(ctx->index, &ep->active_mask);
+ atomic_inc(&ep->submitted_urbs);
}
}
@@ -461,7 +511,6 @@ static void snd_complete_urb(struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_endpoint *ep = ctx->ep;
- unsigned long flags;
int err;
if (unlikely(urb->status == -ENOENT || /* unlinked */
@@ -482,16 +531,20 @@ static void snd_complete_urb(struct urb *urb)
if (unlikely(!ep_state_running(ep)))
goto exit_clear;
- if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
- spin_lock_irqsave(&ep->lock, flags);
- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ /* in low-latency and implicit-feedback modes, push back the
+ * URB to ready list at first, then process as much as possible
+ */
+ if (ep->lowlatency_playback ||
+ snd_usb_endpoint_implicit_feedback_sink(ep)) {
+ push_back_to_ready_list(ep, ctx);
clear_bit(ctx->index, &ep->active_mask);
- spin_unlock_irqrestore(&ep->lock, flags);
- queue_pending_output_urbs(ep);
+ snd_usb_queue_pending_output_urbs(ep, false);
+ atomic_dec(&ep->submitted_urbs); /* decrement at last */
return;
}
- prepare_outbound_urb(ep, ctx);
+ /* in non-lowlatency mode, no error handling for prepare */
+ prepare_outbound_urb(ep, ctx, false);
/* can be stopped during prepare callback */
if (unlikely(!ep_state_running(ep)))
goto exit_clear;
@@ -513,6 +566,7 @@ static void snd_complete_urb(struct urb *urb)
exit_clear:
clear_bit(ctx->index, &ep->active_mask);
+ atomic_dec(&ep->submitted_urbs);
}
/*
@@ -596,6 +650,7 @@ int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type)
ep->type = type;
ep->ep_num = ep_num;
INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ atomic_set(&ep->submitted_urbs, 0);
is_playback = ((ep_num & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
ep_num &= USB_ENDPOINT_NUMBER_MASK;
@@ -722,6 +777,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->cur_period_frames = params_period_size(params);
ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes;
ep->cur_buffer_periods = params_periods(params);
+ ep->cur_clock = fp->clock;
if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
endpoint_set_syncinterval(chip, ep);
@@ -781,14 +837,19 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip,
* Pass NULL to deactivate each callback.
*/
void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep,
- void (*prepare)(struct snd_usb_substream *subs,
- struct urb *urb),
+ int (*prepare)(struct snd_usb_substream *subs,
+ struct urb *urb,
+ bool in_stream_lock),
void (*retire)(struct snd_usb_substream *subs,
struct urb *urb),
struct snd_usb_substream *data_subs)
{
ep->prepare_data_urb = prepare;
ep->retire_data_urb = retire;
+ if (data_subs)
+ ep->lowlatency_playback = data_subs->lowlatency_playback;
+ else
+ ep->lowlatency_playback = false;
WRITE_ONCE(ep->data_subs, data_subs);
}
@@ -833,6 +894,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
ep->altsetting = 0;
ep->cur_audiofmt = NULL;
ep->cur_rate = 0;
+ ep->cur_clock = 0;
ep->iface_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
}
@@ -859,7 +921,7 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
return 0;
do {
- alive = bitmap_weight(&ep->active_mask, ep->nurbs);
+ alive = atomic_read(&ep->submitted_urbs);
if (!alive)
break;
@@ -893,9 +955,10 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
*
* This function moves the EP to STOPPING state if it's being RUNNING.
*/
-static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
+static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
{
unsigned int i;
+ unsigned long flags;
if (!force && atomic_read(&ep->running))
return -EBUSY;
@@ -903,9 +966,14 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING))
return 0;
+ spin_lock_irqsave(&ep->lock, flags);
INIT_LIST_HEAD(&ep->ready_playback_urbs);
ep->next_packet_head = 0;
ep->next_packet_queued = 0;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (keep_pending)
+ return 0;
for (i = 0; i < ep->nurbs; i++) {
if (test_bit(i, &ep->active_mask)) {
@@ -930,7 +998,7 @@ static int release_urbs(struct snd_usb_endpoint *ep, bool force)
snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL);
/* stop and unlink urbs */
- err = stop_urbs(ep, force);
+ err = stop_urbs(ep, force, false);
if (err)
return err;
@@ -1132,10 +1200,6 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
INIT_LIST_HEAD(&u->ready_list);
}
- /* total buffer bytes of all URBs plus the next queue;
- * referred in pcm.c
- */
- ep->nominal_queue_size = maxsize * urb_packs * (ep->nurbs + 1);
return 0;
out_of_memory:
@@ -1340,6 +1404,25 @@ unlock:
return err;
}
+/* get the current rate set to the given clock by any endpoint */
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
+{
+ struct snd_usb_endpoint *ep;
+ int rate = 0;
+
+ if (!clock)
+ return 0;
+ mutex_lock(&chip->mutex);
+ list_for_each_entry(ep, &chip->ep_list, list) {
+ if (ep->cur_clock == clock && ep->cur_rate) {
+ rate = ep->cur_rate;
+ break;
+ }
+ }
+ mutex_unlock(&chip->mutex);
+ return rate;
+}
+
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
@@ -1355,6 +1438,7 @@ unlock:
*/
int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
+ bool is_playback = usb_pipeout(ep->pipe);
int err;
unsigned int i;
@@ -1391,13 +1475,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
if (snd_usb_endpoint_implicit_feedback_sink(ep) &&
!(ep->chip->quirk_flags & QUIRK_FLAG_PLAYBACK_FIRST)) {
- for (i = 0; i < ep->nurbs; i++) {
- struct snd_urb_ctx *ctx = ep->urb + i;
- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
- }
-
usb_audio_dbg(ep->chip, "No URB submission due to implicit fb sync\n");
- return 0;
+ i = 0;
+ goto fill_rest;
}
for (i = 0; i < ep->nurbs; i++) {
@@ -1406,10 +1486,18 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
if (snd_BUG_ON(!urb))
goto __error;
- if (usb_pipeout(ep->pipe)) {
- prepare_outbound_urb(ep, urb->context);
- } else {
- prepare_inbound_urb(ep, urb->context);
+ if (is_playback)
+ err = prepare_outbound_urb(ep, urb->context, true);
+ else
+ err = prepare_inbound_urb(ep, urb->context);
+ if (err < 0) {
+ /* stop filling at applptr */
+ if (err == -EAGAIN)
+ break;
+ usb_audio_dbg(ep->chip,
+ "EP 0x%x: failed to prepare urb: %d\n",
+ ep->ep_num, err);
+ goto __error;
}
err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -1420,14 +1508,29 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
goto __error;
}
set_bit(i, &ep->active_mask);
+ atomic_inc(&ep->submitted_urbs);
+ }
+
+ if (!i) {
+ usb_audio_dbg(ep->chip, "XRUN at starting EP 0x%x\n",
+ ep->ep_num);
+ goto __error;
}
usb_audio_dbg(ep->chip, "%d URBs submitted for EP 0x%x\n",
- ep->nurbs, ep->ep_num);
+ i, ep->ep_num);
+
+ fill_rest:
+ /* put the remaining URBs to ready list */
+ if (is_playback) {
+ for (; i < ep->nurbs; i++)
+ push_back_to_ready_list(ep, ep->urb + i);
+ }
+
return 0;
__error:
- snd_usb_endpoint_stop(ep);
+ snd_usb_endpoint_stop(ep, false);
return -EPIPE;
}
@@ -1435,6 +1538,7 @@ __error:
* snd_usb_endpoint_stop: stop an snd_usb_endpoint
*
* @ep: the endpoint to stop (may be NULL)
+ * @keep_pending: keep in-flight URBs
*
* A call to this function will decrement the running count of the endpoint.
* In case the last user has requested the endpoint stop, the URBs will
@@ -1445,7 +1549,7 @@ __error:
* The caller needs to synchronize the pending stop operation via
* snd_usb_endpoint_sync_pending_stop().
*/
-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending)
{
if (!ep)
return;
@@ -1460,7 +1564,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
if (!atomic_dec_return(&ep->running)) {
if (ep->sync_source)
WRITE_ONCE(ep->sync_source->sync_sink, NULL);
- stop_urbs(ep, false);
+ stop_urbs(ep, false, keep_pending);
}
}
@@ -1575,7 +1679,7 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
}
spin_unlock_irqrestore(&ep->lock, flags);
- queue_pending_output_urbs(ep);
+ snd_usb_queue_pending_output_urbs(ep, false);
return;
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index a668f675b52b..6a9af04cf175 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -19,6 +19,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock);
bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep,
@@ -29,14 +30,15 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip,
struct snd_usb_endpoint *data_ep,
struct snd_usb_endpoint *sync_ep);
void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep,
- void (*prepare)(struct snd_usb_substream *subs,
- struct urb *urb),
+ int (*prepare)(struct snd_usb_substream *subs,
+ struct urb *urb,
+ bool in_stream_lock),
void (*retire)(struct snd_usb_substream *subs,
struct urb *urb),
struct snd_usb_substream *data_subs);
int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending);
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
@@ -45,6 +47,9 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx, int idx);
+ struct snd_urb_ctx *ctx, int idx,
+ unsigned int avail);
+void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
+ bool in_stream_lock);
#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 50efccbffb8a..f5e676a51b30 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -414,6 +414,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
case USB_ID(0x0e41, 0x4242): /* Line6 Helix Rack */
case USB_ID(0x0e41, 0x4244): /* Line6 Helix LT */
case USB_ID(0x0e41, 0x4246): /* Line6 HX-Stomp */
+ case USB_ID(0x0e41, 0x4253): /* Line6 HX-Stomp XL */
case USB_ID(0x0e41, 0x4247): /* Line6 Pod Go */
case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */
case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */
diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c
index 23767a14d126..70319c822c10 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -54,8 +54,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
/* Fixed EP */
/* FIXME: check the availability of generic matching */
- IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */
- IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */
IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */
IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */
IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 9602929b7de9..59faa5a9a714 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -113,12 +113,12 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
(char *)frag_buf, frag_size,
- &partial, LINE6_TIMEOUT * HZ);
+ &partial, LINE6_TIMEOUT);
} else {
retval = usb_bulk_msg(line6->usbdev,
usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
(char *)frag_buf, frag_size,
- &partial, LINE6_TIMEOUT * HZ);
+ &partial, LINE6_TIMEOUT);
}
if (retval) {
@@ -347,7 +347,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg_send(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
(datalen << 8) | 0x21, address, NULL, 0,
- LINE6_TIMEOUT * HZ, GFP_KERNEL);
+ LINE6_TIMEOUT, GFP_KERNEL);
if (ret) {
dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
goto exit;
@@ -360,7 +360,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg_recv(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x0012, 0x0000, &len, 1,
- LINE6_TIMEOUT * HZ, GFP_KERNEL);
+ LINE6_TIMEOUT, GFP_KERNEL);
if (ret) {
dev_err(line6->ifcdev,
"receive length failed (error %d)\n", ret);
@@ -387,7 +387,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
/* receive the result: */
ret = usb_control_msg_recv(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT * HZ,
+ 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT,
GFP_KERNEL);
if (ret)
dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
@@ -417,7 +417,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg_send(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
- 0x0022, address, data, datalen, LINE6_TIMEOUT * HZ,
+ 0x0022, address, data, datalen, LINE6_TIMEOUT,
GFP_KERNEL);
if (ret) {
dev_err(line6->ifcdev,
@@ -430,7 +430,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg_recv(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x0012, 0x0000, status, 1, LINE6_TIMEOUT * HZ,
+ 0x0012, 0x0000, status, 1, LINE6_TIMEOUT,
GFP_KERNEL);
if (ret) {
dev_err(line6->ifcdev,
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 71d3da1db8c8..ecf3a2b39c7e 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -27,7 +27,7 @@
#define LINE6_FALLBACK_INTERVAL 10
#define LINE6_FALLBACK_MAXPACKETSIZE 16
-#define LINE6_TIMEOUT 1
+#define LINE6_TIMEOUT 1000
#define LINE6_BUFSIZE_LISTEN 64
#define LINE6_MIDI_MESSAGE_MAXLEN 256
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 28794a35949d..b24bc82f89e3 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -190,7 +190,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
ret = usb_control_msg_send(usbdev, 0,
0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x11, 0,
- NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+ NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
if (ret) {
dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
goto exit;
@@ -200,7 +200,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
ret = usb_control_msg_recv(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x11, 0x0,
- init_bytes, 3, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+ init_bytes, 3, LINE6_TIMEOUT, GFP_KERNEL);
if (ret) {
dev_err(pod->line6.ifcdev,
"receive length failed (error %d)\n", ret);
@@ -220,7 +220,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
USB_REQ_SET_FEATURE,
USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
1, 0,
- NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+ NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
exit:
return ret;
}
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 4e5693c97aa4..e33df58740a9 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -128,7 +128,7 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
ret = usb_control_msg_send(usbdev, 0, 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
- cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ,
+ cmd1, cmd2, NULL, 0, LINE6_TIMEOUT,
GFP_KERNEL);
if (ret) {
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index 5834d1dc317e..4f6b20ed29dd 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -1000,7 +1000,7 @@ static int detect_usb_format(struct ua101 *ua)
fmt_playback->bSubframeSize * ua->playback.channels;
epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc;
- if (!usb_endpoint_is_isoc_in(epd)) {
+ if (!usb_endpoint_is_isoc_in(epd) || usb_endpoint_maxp(epd) == 0) {
dev_err(&ua->dev->dev, "invalid capture endpoint\n");
return -ENXIO;
}
@@ -1008,7 +1008,7 @@ static int detect_usb_format(struct ua101 *ua)
ua->capture.max_packet_bytes = usb_endpoint_maxp(epd);
epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc;
- if (!usb_endpoint_is_isoc_out(epd)) {
+ if (!usb_endpoint_is_isoc_out(epd) || usb_endpoint_maxp(epd) == 0) {
dev_err(&ua->dev->dev, "invalid playback endpoint\n");
return -ENXIO;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 8e030b1c061a..6e7bac8203ba 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -361,9 +361,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
memset(buf, 0, sizeof(buf));
- ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
- if (ret)
- goto error;
+ if (snd_usb_lock_shutdown(chip))
+ return -EIO;
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
@@ -372,8 +371,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
snd_usb_unlock_shutdown(chip);
if (ret < 0) {
-error:
- usb_audio_err(chip,
+ usb_audio_dbg(chip,
"cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
request, validx, idx, cval->val_type);
return ret;
@@ -1208,12 +1206,32 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
}
+/* forcibly initialize the current mixer value; if GET_CUR fails, set to
+ * the minimum as default
+ */
+static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
+{
+ int val, err;
+
+ err = snd_usb_get_cur_mix_value(cval, ch, idx, &val);
+ if (!err)
+ return;
+ if (!cval->head.mixer->ignore_ctl_error)
+ usb_audio_warn(cval->head.mixer->chip,
+ "%d:%d: failed to get current value for ch %d (%d)\n",
+ cval->head.id, mixer_ctrl_intf(cval->head.mixer),
+ ch, err);
+ snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
+}
+
/*
* retrieve the minimum and maximum values for the specified control
*/
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int default_min, struct snd_kcontrol *kctl)
{
+ int i, idx;
+
/* for failsafe */
cval->min = default_min;
cval->max = cval->min + 1;
@@ -1226,7 +1244,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
} else {
int minchn = 0;
if (cval->cmask) {
- int i;
for (i = 0; i < MAX_CHANNELS; i++)
if (cval->cmask & (1 << i)) {
minchn = i + 1;
@@ -1327,6 +1344,19 @@ no_res_check:
}
}
+ /* initialize all elements */
+ if (!cval->cmask) {
+ init_cur_mix_raw(cval, 0, 0);
+ } else {
+ idx = 0;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (cval->cmask & (1 << i)) {
+ init_cur_mix_raw(cval, i + 1, idx);
+ idx++;
+ }
+ }
+ }
+
return 0;
}
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 46082dc57be0..d489c1de3bae 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -2795,6 +2795,7 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
#define SND_DJM_750_IDX 0x1
#define SND_DJM_850_IDX 0x2
#define SND_DJM_900NXS2_IDX 0x3
+#define SND_DJM_750MK2_IDX 0x4
#define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
@@ -2984,10 +2985,40 @@ static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
SND_DJM_CTL("Ch5 Input", 900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
};
+// DJM-750MK2
+static const u16 snd_djm_opts_750mk2_cap1[] = {
+ 0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
+static const u16 snd_djm_opts_750mk2_cap2[] = {
+ 0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
+static const u16 snd_djm_opts_750mk2_cap3[] = {
+ 0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
+static const u16 snd_djm_opts_750mk2_cap4[] = {
+ 0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
+static const u16 snd_djm_opts_750mk2_cap5[] = {
+ 0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
+
+static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
+static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
+static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
+
+
+static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
+ SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Ch1 Input", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Ch2 Input", 750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Ch3 Input", 750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Ch4 Input", 750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Ch5 Input", 750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Ch1 Output", 750mk2_pb1, 0, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Ch2 Output", 750mk2_pb2, 1, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Ch3 Output", 750mk2_pb3, 2, SND_DJM_WINDEX_PB)
+};
+
static const struct snd_djm_device snd_djm_devices[] = {
SND_DJM_DEVICE(250mk2),
SND_DJM_DEVICE(750),
+ SND_DJM_DEVICE(750mk2),
SND_DJM_DEVICE(850),
SND_DJM_DEVICE(900nxs2)
};
@@ -3235,6 +3266,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */
err = snd_djm_controls_create(mixer, SND_DJM_750_IDX);
break;
+ case USB_ID(0x2b73, 0x001b): /* Pioneer DJ DJM-750MK2 */
+ err = snd_djm_controls_create(mixer, SND_DJM_750MK2_IDX);
+ break;
case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */
err = snd_djm_controls_create(mixer, SND_DJM_850_IDX);
break;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 5dc9266180e3..95ec8eec1bb0 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -219,16 +219,16 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip,
return 0;
}
-static bool stop_endpoints(struct snd_usb_substream *subs)
+static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
{
bool stopped = 0;
if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
- snd_usb_endpoint_stop(subs->sync_endpoint);
+ snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending);
stopped = true;
}
if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
- snd_usb_endpoint_stop(subs->data_endpoint);
+ snd_usb_endpoint_stop(subs->data_endpoint, keep_pending);
stopped = true;
}
return stopped;
@@ -261,7 +261,7 @@ static int start_endpoints(struct snd_usb_substream *subs)
return 0;
error:
- stop_endpoints(subs);
+ stop_endpoints(subs, false);
return err;
}
@@ -437,7 +437,7 @@ static int configure_endpoints(struct snd_usb_audio *chip,
if (subs->data_endpoint->need_setup) {
/* stop any running stream beforehand */
- if (stop_endpoints(subs))
+ if (stop_endpoints(subs, false))
sync_pending_stops(subs);
err = snd_usb_endpoint_configure(chip, subs->data_endpoint);
if (err < 0)
@@ -572,7 +572,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs->cur_audiofmt = NULL;
mutex_unlock(&chip->mutex);
if (!snd_usb_lock_shutdown(chip)) {
- if (stop_endpoints(subs))
+ if (stop_endpoints(subs, false))
sync_pending_stops(subs);
close_endpoints(chip, subs);
snd_usb_unlock_shutdown(chip);
@@ -581,6 +581,26 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+/* check whether early start is needed for playback stream */
+static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
+ struct snd_usb_substream *subs)
+{
+ struct snd_usb_audio *chip = subs->stream->chip;
+
+ if (subs->direction == SNDRV_PCM_STREAM_CAPTURE)
+ return false;
+ /* disabled via module option? */
+ if (!chip->lowlatency)
+ return false;
+ /* free-wheeling mode? (e.g. dmix) */
+ if (runtime->stop_threshold > runtime->buffer_size)
+ return false;
+ /* implicit feedback mode has own operation mode */
+ if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
+ return false;
+ return true;
+}
+
/*
* prepare callback
*
@@ -614,13 +634,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
subs->period_elapsed_pending = 0;
runtime->delay = 0;
- /* check whether early start is needed for playback stream */
- subs->early_playback_start =
- subs->direction == SNDRV_PCM_STREAM_PLAYBACK &&
- (!chip->lowlatency ||
- (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes));
-
- if (subs->early_playback_start)
+ subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
+ if (!subs->lowlatency_playback)
ret = start_endpoints(subs);
unlock:
@@ -734,6 +749,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
+ struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax, r;
@@ -745,6 +761,14 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
+ r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
+ if (r > 0) {
+ if (!snd_interval_test(it, r))
+ continue;
+ rmin = min(rmin, r);
+ rmax = max(rmax, r);
+ continue;
+ }
if (fp->rate_table && fp->nr_rates) {
for (i = 0; i < fp->nr_rates; i++) {
r = fp->rate_table[i];
@@ -1056,6 +1080,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
if (err < 0)
return err;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (fp->implicit_fb) {
+ runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ break;
+ }
+ }
+
return 0;
}
@@ -1068,6 +1099,10 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
int ret;
runtime->hw = snd_usb_hardware;
+ /* need an explicit sync to catch applptr update in low-latency mode */
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
+ as->chip->lowlatency)
+ runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR;
runtime->private_data = subs;
subs->pcm_substream = substream;
/* runtime PM is also done there */
@@ -1320,44 +1355,66 @@ static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
return bytes;
}
-static void prepare_playback_urb(struct snd_usb_substream *subs,
- struct urb *urb)
+static int prepare_playback_urb(struct snd_usb_substream *subs,
+ struct urb *urb,
+ bool in_stream_lock)
{
struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
struct snd_usb_endpoint *ep = subs->data_endpoint;
struct snd_urb_ctx *ctx = urb->context;
- unsigned int counts, frames, bytes;
+ unsigned int frames, bytes;
+ int counts;
+ unsigned int transfer_done, frame_limit, avail = 0;
int i, stride, period_elapsed = 0;
unsigned long flags;
+ int err = 0;
stride = ep->stride;
frames = 0;
ctx->queued = 0;
urb->number_of_packets = 0;
+
spin_lock_irqsave(&subs->lock, flags);
- subs->frame_limit += ep->max_urb_frames;
+ frame_limit = subs->frame_limit + ep->max_urb_frames;
+ transfer_done = subs->transfer_done;
+
+ if (subs->lowlatency_playback &&
+ runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
+ unsigned int hwptr = subs->hwptr_done / stride;
+
+ /* calculate the byte offset-in-buffer of the appl_ptr */
+ avail = (runtime->control->appl_ptr - runtime->hw_ptr_base)
+ % runtime->buffer_size;
+ if (avail <= hwptr)
+ avail += runtime->buffer_size;
+ avail -= hwptr;
+ }
+
for (i = 0; i < ctx->packets; i++) {
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
+ if (counts < 0)
+ break;
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;
urb->iso_frame_desc[i].length = counts * stride;
frames += counts;
+ avail -= counts;
urb->number_of_packets++;
- subs->transfer_done += counts;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- subs->frame_limit = 0;
+ transfer_done += counts;
+ if (transfer_done >= runtime->period_size) {
+ transfer_done -= runtime->period_size;
+ frame_limit = 0;
period_elapsed = 1;
if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
- if (subs->transfer_done > 0) {
+ if (transfer_done > 0) {
/* FIXME: fill-max mode is not
* supported yet */
- frames -= subs->transfer_done;
- counts -= subs->transfer_done;
+ frames -= transfer_done;
+ counts -= transfer_done;
urb->iso_frame_desc[i].length =
counts * stride;
- subs->transfer_done = 0;
+ transfer_done = 0;
}
i++;
if (i < ctx->packets) {
@@ -1371,13 +1428,19 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
}
}
/* finish at the period boundary or after enough frames */
- if ((period_elapsed ||
- subs->transfer_done >= subs->frame_limit) &&
+ if ((period_elapsed || transfer_done >= frame_limit) &&
!snd_usb_endpoint_implicit_feedback_sink(ep))
break;
}
- bytes = frames * stride;
+ if (!frames) {
+ err = -EAGAIN;
+ goto unlock;
+ }
+
+ bytes = frames * stride;
+ subs->transfer_done = transfer_done;
+ subs->frame_limit = frame_limit;
if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
subs->cur_audiofmt->dsd_dop)) {
fill_playback_urb_dsd_dop(subs, urb, bytes);
@@ -1403,14 +1466,23 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
subs->trigger_tstamp_pending_update = false;
}
- if (period_elapsed && !subs->running && !subs->early_playback_start) {
+ if (period_elapsed && !subs->running && subs->lowlatency_playback) {
subs->period_elapsed_pending = 1;
period_elapsed = 0;
}
+
+ unlock:
spin_unlock_irqrestore(&subs->lock, flags);
+ if (err < 0)
+ return err;
urb->transfer_buffer_length = bytes;
- if (period_elapsed)
- snd_pcm_period_elapsed(subs->pcm_substream);
+ if (period_elapsed) {
+ if (in_stream_lock)
+ snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream);
+ else
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ }
+ return 0;
}
/*
@@ -1442,6 +1514,27 @@ static void retire_playback_urb(struct snd_usb_substream *subs,
snd_pcm_period_elapsed(subs->pcm_substream);
}
+/* PCM ack callback for the playback stream;
+ * this plays a role only when the stream is running in low-latency mode.
+ */
+static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+ struct snd_usb_endpoint *ep;
+
+ if (!subs->lowlatency_playback || !subs->running)
+ return 0;
+ ep = subs->data_endpoint;
+ if (!ep)
+ return 0;
+ /* When no more in-flight URBs available, try to process the pending
+ * outputs here
+ */
+ if (!ep->active_mask)
+ snd_usb_queue_pending_output_urbs(ep, true);
+ return 0;
+}
+
static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -1457,7 +1550,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
prepare_playback_urb,
retire_playback_urb,
subs);
- if (!subs->early_playback_start &&
+ if (subs->lowlatency_playback &&
cmd == SNDRV_PCM_TRIGGER_START) {
err = start_endpoints(subs);
if (err < 0) {
@@ -1473,7 +1566,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
return 0;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- stop_endpoints(subs);
+ stop_endpoints(subs, substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING);
snd_usb_endpoint_set_callback(subs->data_endpoint,
NULL, NULL, NULL);
subs->running = 0;
@@ -1521,7 +1614,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
return 0;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- stop_endpoints(subs);
+ stop_endpoints(subs, false);
fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_usb_endpoint_set_callback(subs->data_endpoint,
@@ -1545,6 +1638,7 @@ static const struct snd_pcm_ops snd_usb_playback_ops = {
.trigger = snd_usb_substream_playback_trigger,
.sync_stop = snd_usb_pcm_sync_stop,
.pointer = snd_usb_pcm_pointer,
+ .ack = snd_usb_pcm_playback_ack,
};
static const struct snd_pcm_ops snd_usb_capture_ops = {
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 2af8c68fac27..b1522e43173e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3894,6 +3894,64 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
/*
+ * Pioneer DJ DJM-750MK2
+ * 10 channels playback & 12 channels capture @ 48kHz S24LE
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001b),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 12,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x82,
+ .ep_idx = 1,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC|
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 48000 }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ /*
* Pioneer DJ DJM-850
* 8 channels playback and 8 channels capture @ 44.1/48/96kHz S24LE
* Playback on EP 0x05
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 8929d9abe8aa..64e1c20311ed 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1749,6 +1749,7 @@ static const struct registration_quirk registration_quirks[] = {
REG_QUIRK_ENTRY(0x0951, 0x16ea, 2), /* Kingston HyperX Cloud Flight S */
REG_QUIRK_ENTRY(0x0ecb, 0x1f46, 2), /* JBL Quantum 600 */
REG_QUIRK_ENTRY(0x0ecb, 0x1f47, 2), /* JBL Quantum 800 */
+ REG_QUIRK_ENTRY(0x0ecb, 0x1f4c, 2), /* JBL Quantum 400 */
REG_QUIRK_ENTRY(0x0ecb, 0x2039, 2), /* JBL Quantum 400 */
REG_QUIRK_ENTRY(0x0ecb, 0x203c, 2), /* JBL Quantum 600 */
REG_QUIRK_ENTRY(0x0ecb, 0x203e, 2), /* JBL Quantum 800 */
@@ -1887,6 +1888,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x21b4, 0x0081, /* AudioQuest DragonFly */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
+ QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index c39cc6851e2d..cfc1ea53978d 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -668,14 +668,15 @@ static void i_usx2y_04int(struct urb *urb)
static int usx2y_rate_set(struct usx2ydev *usx2y, int rate)
{
- int err = 0, i;
- struct snd_usx2y_urb_seq *us = NULL;
- int *usbdata = NULL;
- const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100;
+ int err = 0, i;
+ struct snd_usx2y_urb_seq *us = NULL;
+ int *usbdata = NULL;
+ const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100;
struct urb *urb;
if (usx2y->rate != rate) {
- us = kzalloc(sizeof(*us) + sizeof(struct urb *) * NOOF_SETRATE_URBS, GFP_KERNEL);
+ us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS),
+ GFP_KERNEL);
if (!us) {
err = -ENOMEM;
goto cleanup;
diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c
index f88c8f29cbd8..aca2dc1989ba 100644
--- a/sound/virtio/virtio_pcm_msg.c
+++ b/sound/virtio/virtio_pcm_msg.c
@@ -20,7 +20,7 @@ struct virtio_pcm_msg {
struct virtio_snd_pcm_xfer xfer;
struct virtio_snd_pcm_status status;
size_t length;
- struct scatterlist sgs[0];
+ struct scatterlist sgs[];
};
/**
@@ -146,8 +146,7 @@ int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss,
int sg_num = virtsnd_pcm_sg_num(data, period_bytes);
struct virtio_pcm_msg *msg;
- msg = kzalloc(sizeof(*msg) + sizeof(*msg->sgs) * (sg_num + 2),
- GFP_KERNEL);
+ msg = kzalloc(struct_size(msg, sgs, sg_num + 2), GFP_KERNEL);
if (!msg)
return -ENOMEM;