summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/Makefile1
-rw-r--r--sound/core/control_led.c2
-rw-r--r--sound/core/info_oss.c6
-rw-r--r--sound/core/jack.c3
-rw-r--r--sound/core/memalloc.c53
-rw-r--r--sound/core/oss/pcm_oss.c2
-rw-r--r--sound/core/pcm.c6
-rw-r--r--sound/core/pcm_dmaengine.c5
-rw-r--r--sound/core/pcm_lib.c17
-rw-r--r--sound/core/seq/seq_queue.c14
-rw-r--r--sound/core/seq/seq_virmidi.c11
-rw-r--r--sound/core/sgbuf.c201
-rw-r--r--sound/drivers/virmidi.c3
-rw-r--r--sound/hda/ext/hdac_ext_stream.c216
-rw-r--r--sound/hda/hdac_stream.c30
-rw-r--r--sound/hda/intel-dsp-config.c53
-rw-r--r--sound/hda/intel-nhlt.c102
-rw-r--r--sound/isa/gus/gus_mem.c22
-rw-r--r--sound/pci/ac97/ac97_pcm.c2
-rw-r--r--sound/pci/hda/Kconfig33
-rw-r--r--sound/pci/hda/Makefile10
-rw-r--r--sound/pci/hda/cs35l41_hda.c528
-rw-r--r--sound/pci/hda/cs35l41_hda.h69
-rw-r--r--sound/pci/hda/cs35l41_hda_i2c.c66
-rw-r--r--sound/pci/hda/cs35l41_hda_spi.c63
-rw-r--r--sound/pci/hda/hda_auto_parser.c6
-rw-r--r--sound/pci/hda/hda_bind.c7
-rw-r--r--sound/pci/hda/hda_codec.c45
-rw-r--r--sound/pci/hda/hda_component.h20
-rw-r--r--sound/pci/hda/hda_controller.c1
-rw-r--r--sound/pci/hda/hda_generic.h2
-rw-r--r--sound/pci/hda/hda_intel.c11
-rw-r--r--sound/pci/hda/hda_jack.c11
-rw-r--r--sound/pci/hda/hda_jack.h1
-rw-r--r--sound/pci/hda/hda_local.h1
-rw-r--r--sound/pci/hda/hda_tegra.c43
-rw-r--r--sound/pci/hda/patch_cs8409-tables.c3
-rw-r--r--sound/pci/hda/patch_cs8409.c9
-rw-r--r--sound/pci/hda/patch_cs8409.h1
-rw-r--r--sound/pci/hda/patch_hdmi.c2
-rw-r--r--sound/pci/hda/patch_realtek.c198
-rw-r--r--sound/pci/mixart/mixart_core.c5
-rw-r--r--sound/pci/mixart/mixart_core.h10
-rw-r--r--sound/ppc/beep.c2
-rw-r--r--sound/soc/amd/Kconfig9
-rw-r--r--sound/soc/amd/Makefile2
-rw-r--r--sound/soc/amd/acp-config.c124
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c20
-rw-r--r--sound/soc/amd/acp-pcm-dma.c15
-rw-r--r--sound/soc/amd/acp-rt5645.c4
-rw-r--r--sound/soc/amd/acp.h1
-rw-r--r--sound/soc/amd/acp/Kconfig8
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c19
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c33
-rw-r--r--sound/soc/amd/acp/acp-mach.h10
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c21
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c8
-rw-r--r--sound/soc/amd/mach-config.h28
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c2
-rw-r--r--sound/soc/atmel/mikroe-proto.c6
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c32
-rw-r--r--sound/soc/bcm/bcm63xx-i2s.h1
-rw-r--r--sound/soc/bcm/bcm63xx-pcm-whistler.c13
-rw-r--r--sound/soc/codecs/Kconfig36
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/adau1701.c94
-rw-r--r--sound/soc/codecs/ak4118.c18
-rw-r--r--sound/soc/codecs/ak4375.c610
-rw-r--r--sound/soc/codecs/cs35l35.c2
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c19
-rw-r--r--sound/soc/codecs/cs35l41-lib.c (renamed from sound/soc/codecs/cs35l41-tables.c)578
-rw-r--r--sound/soc/codecs/cs35l41-spi.c20
-rw-r--r--sound/soc/codecs/cs35l41.c893
-rw-r--r--sound/soc/codecs/cs35l41.h749
-rw-r--r--sound/soc/codecs/cs4265.c15
-rw-r--r--sound/soc/codecs/cs42l42.c94
-rw-r--r--sound/soc/codecs/cs42l42.h6
-rw-r--r--sound/soc/codecs/es7241.c28
-rw-r--r--sound/soc/codecs/hdac_hda.c22
-rw-r--r--sound/soc/codecs/jz4770.c9
-rw-r--r--sound/soc/codecs/max9759.c28
-rw-r--r--sound/soc/codecs/max98373-sdw.c2
-rw-r--r--sound/soc/codecs/max9860.c12
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c7
-rw-r--r--sound/soc/codecs/mt6660.c5
-rw-r--r--sound/soc/codecs/pcm3168a.c22
-rw-r--r--sound/soc/codecs/rt1308-sdw.c2
-rw-r--r--sound/soc/codecs/rt1316-sdw.c2
-rw-r--r--sound/soc/codecs/rt5640.c169
-rw-r--r--sound/soc/codecs/rt5640.h11
-rw-r--r--sound/soc/codecs/rt5663.c12
-rw-r--r--sound/soc/codecs/rt5682-sdw.c2
-rw-r--r--sound/soc/codecs/rt5682.c7
-rw-r--r--sound/soc/codecs/rt5682s.c34
-rw-r--r--sound/soc/codecs/rt700.c2
-rw-r--r--sound/soc/codecs/rt711-sdca.c2
-rw-r--r--sound/soc/codecs/rt711.c2
-rw-r--r--sound/soc/codecs/rt715-sdca.c2
-rw-r--r--sound/soc/codecs/rt715.c2
-rw-r--r--sound/soc/codecs/sdw-mockup.c2
-rw-r--r--sound/soc/codecs/sgtl5000.c5
-rw-r--r--sound/soc/codecs/simple-amplifier.c10
-rw-r--r--sound/soc/codecs/simple-mux.c10
-rw-r--r--sound/soc/codecs/ssm2305.c11
-rw-r--r--sound/soc/codecs/sta350.h2
-rw-r--r--sound/soc/codecs/tfa989x.c20
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1317
-rw-r--r--sound/soc/codecs/tlv320aic26.h6
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c121
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h2
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c76
-rw-r--r--sound/soc/codecs/wcd9335.c17
-rw-r--r--sound/soc/codecs/wcd934x.c6
-rw-r--r--sound/soc/codecs/wcd938x.c8
-rw-r--r--sound/soc/codecs/wm_adsp.c61
-rw-r--r--sound/soc/codecs/wm_adsp.h8
-rw-r--r--sound/soc/codecs/wsa881x.c2
-rw-r--r--sound/soc/codecs/zl38060.c4
-rw-r--r--sound/soc/fsl/Kconfig1
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c15
-rw-r--r--sound/soc/fsl/fsl_asrc.c69
-rw-r--r--sound/soc/fsl/fsl_mqs.c2
-rw-r--r--sound/soc/fsl/imx-card.c49
-rw-r--r--sound/soc/fsl/imx-hdmi.c2
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c4
-rw-r--r--sound/soc/fsl/imx-spdif.c4
-rw-r--r--sound/soc/generic/audio-graph-card.c5
-rw-r--r--sound/soc/generic/audio-graph-card2.c4
-rw-r--r--sound/soc/generic/simple-card-utils.c45
-rw-r--r--sound/soc/generic/simple-card.c3
-rw-r--r--sound/soc/generic/test-component.c5
-rw-r--r--sound/soc/img/img-i2s-in.c8
-rw-r--r--sound/soc/img/img-i2s-out.c24
-rw-r--r--sound/soc/img/img-parallel-out.c24
-rw-r--r--sound/soc/img/img-spdif-in.c8
-rw-r--r--sound/soc/img/img-spdif-out.c24
-rw-r--r--sound/soc/img/pistachio-internal-dac.c9
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c14
-rw-r--r--sound/soc/intel/boards/Kconfig20
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c2
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c2
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c86
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c2
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c180
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h16
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c651
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c119
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h7
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c179
-rw-r--r--sound/soc/intel/boards/sof_sdw.c158
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h7
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt715.c7
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt715_sdca.c7
-rw-r--r--sound/soc/intel/catpt/dsp.c14
-rw-r--r--sound/soc/intel/catpt/pcm.c37
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c48
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c102
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c11
-rw-r--r--sound/soc/intel/skylake/skl-topology.c29
-rw-r--r--sound/soc/intel/skylake/skl-topology.h1
-rw-r--r--sound/soc/intel/skylake/skl.c4
-rw-r--r--sound/soc/intel/skylake/skl.h4
-rw-r--r--sound/soc/mediatek/Kconfig2
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c2
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c8
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c7
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c7
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c7
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c2
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c21
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c26
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c2
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c17
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.c12
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c9
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c73
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c147
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c470
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-reg.h1
-rw-r--r--sound/soc/meson/aiu.c36
-rw-r--r--sound/soc/meson/axg-fifo.c16
-rw-r--r--sound/soc/meson/axg-pdm.c25
-rw-r--r--sound/soc/meson/axg-spdifin.c17
-rw-r--r--sound/soc/meson/axg-spdifout.c17
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c50
-rw-r--r--sound/soc/meson/axg-tdm-interface.c25
-rw-r--r--sound/soc/meson/meson-card-utils.c8
-rw-r--r--sound/soc/meson/t9015.c14
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c8
-rw-r--r--sound/soc/qcom/Kconfig1
-rw-r--r--sound/soc/qcom/apq8016_sbc.c134
-rw-r--r--sound/soc/qcom/common.c20
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c14
-rw-r--r--sound/soc/qcom/sc7180.c24
-rw-r--r--sound/soc/qcom/sdm845.c14
-rw-r--r--sound/soc/qcom/sm8250.c4
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c10
-rw-r--r--sound/soc/samsung/aries_wm8994.c17
-rw-r--r--sound/soc/samsung/arndale.c5
-rw-r--r--sound/soc/samsung/idma.c2
-rw-r--r--sound/soc/samsung/littlemill.c5
-rw-r--r--sound/soc/samsung/lowland.c5
-rw-r--r--sound/soc/samsung/odroid.c4
-rw-r--r--sound/soc/samsung/smdk_wm8994.c4
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c4
-rw-r--r--sound/soc/samsung/snow.c9
-rw-r--r--sound/soc/samsung/speyside.c5
-rw-r--r--sound/soc/samsung/tm2_wm5110.c3
-rw-r--r--sound/soc/samsung/tobermory.c5
-rw-r--r--sound/soc/sh/rz-ssi.c7
-rw-r--r--sound/soc/soc-component.c28
-rw-r--r--sound/soc/soc-core.c51
-rw-r--r--sound/soc/soc-dai.c40
-rw-r--r--sound/soc/soc-pcm.c380
-rw-r--r--sound/soc/soc-topology.c2
-rw-r--r--sound/soc/sof/Kconfig18
-rw-r--r--sound/soc/sof/Makefile4
-rw-r--r--sound/soc/sof/amd/Kconfig33
-rw-r--r--sound/soc/sof/amd/Makefile11
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h78
-rw-r--r--sound/soc/sof/amd/acp-ipc.c187
-rw-r--r--sound/soc/sof/amd/acp-loader.c199
-rw-r--r--sound/soc/sof/amd/acp-pcm.c82
-rw-r--r--sound/soc/sof/amd/acp-stream.c181
-rw-r--r--sound/soc/sof/amd/acp-trace.c84
-rw-r--r--sound/soc/sof/amd/acp.c446
-rw-r--r--sound/soc/sof/amd/acp.h226
-rw-r--r--sound/soc/sof/amd/pci-rn.c165
-rw-r--r--sound/soc/sof/amd/renoir.c186
-rw-r--r--sound/soc/sof/control.c61
-rw-r--r--sound/soc/sof/core.c135
-rw-r--r--sound/soc/sof/debug.c142
-rw-r--r--sound/soc/sof/imx/Kconfig46
-rw-r--r--sound/soc/sof/imx/imx-common.c28
-rw-r--r--sound/soc/sof/imx/imx-common.h11
-rw-r--r--sound/soc/sof/imx/imx-ops.h10
-rw-r--r--sound/soc/sof/imx/imx8.c220
-rw-r--r--sound/soc/sof/imx/imx8m.c260
-rw-r--r--sound/soc/sof/intel/apl.c7
-rw-r--r--sound/soc/sof/intel/atom.c64
-rw-r--r--sound/soc/sof/intel/atom.h4
-rw-r--r--sound/soc/sof/intel/bdw.c71
-rw-r--r--sound/soc/sof/intel/byt.c9
-rw-r--r--sound/soc/sof/intel/cnl.c34
-rw-r--r--sound/soc/sof/intel/hda-codec.c3
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c2
-rw-r--r--sound/soc/sof/intel/hda-dai.c97
-rw-r--r--sound/soc/sof/intel/hda-dsp.c52
-rw-r--r--sound/soc/sof/intel/hda-ipc.c48
-rw-r--r--sound/soc/sof/intel/hda-loader.c104
-rw-r--r--sound/soc/sof/intel/hda-pcm.c127
-rw-r--r--sound/soc/sof/intel/hda-stream.c109
-rw-r--r--sound/soc/sof/intel/hda.c139
-rw-r--r--sound/soc/sof/intel/hda.h22
-rw-r--r--sound/soc/sof/intel/icl.c73
-rw-r--r--sound/soc/sof/intel/pci-tng.c9
-rw-r--r--sound/soc/sof/intel/shim.h11
-rw-r--r--sound/soc/sof/intel/tgl.c47
-rw-r--r--sound/soc/sof/ipc.c134
-rw-r--r--sound/soc/sof/loader.c16
-rw-r--r--sound/soc/sof/mediatek/Kconfig33
-rw-r--r--sound/soc/sof/mediatek/Makefile2
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h49
-rw-r--r--sound/soc/sof/mediatek/mt8195/Makefile3
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c158
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.h28
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-loader.c56
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c463
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h158
-rw-r--r--sound/soc/sof/ops.c47
-rw-r--r--sound/soc/sof/ops.h93
-rw-r--r--sound/soc/sof/pcm.c118
-rw-r--r--sound/soc/sof/pm.c10
-rw-r--r--sound/soc/sof/sof-audio.c239
-rw-r--r--sound/soc/sof/sof-audio.h17
-rw-r--r--sound/soc/sof/sof-of-dev.c68
-rw-r--r--sound/soc/sof/sof-of-dev.h17
-rw-r--r--sound/soc/sof/sof-pci-dev.c19
-rw-r--r--sound/soc/sof/sof-priv.h82
-rw-r--r--sound/soc/sof/sof-probes.c2
-rw-r--r--sound/soc/sof/sof-probes.h2
-rw-r--r--sound/soc/sof/topology.c292
-rw-r--r--sound/soc/sof/trace.c18
-rw-r--r--sound/soc/sof/xtensa/core.c44
-rw-r--r--sound/soc/stm/stm32_adfsdm.c5
-rw-r--r--sound/soc/stm/stm32_i2s.c66
-rw-r--r--sound/soc/stm/stm32_sai.c37
-rw-r--r--sound/soc/stm/stm32_sai_sub.c29
-rw-r--r--sound/soc/stm/stm32_spdifrx.c48
-rw-r--r--sound/soc/sunxi/sun4i-codec.c3
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c115
-rw-r--r--sound/soc/sunxi/sun8i-codec.c56
-rw-r--r--sound/soc/tegra/tegra20_i2s.c49
-rw-r--r--sound/soc/tegra/tegra20_spdif.c197
-rw-r--r--sound/soc/tegra/tegra20_spdif.h1
-rw-r--r--sound/soc/tegra/tegra210_mvc.c209
-rw-r--r--sound/soc/tegra/tegra210_mvc.h5
-rw-r--r--sound/soc/tegra/tegra_pcm.c6
-rw-r--r--sound/soc/tegra/tegra_pcm.h1
-rw-r--r--sound/soc/ti/davinci-mcasp.c21
-rw-r--r--sound/soc/ti/j721e-evm.c10
-rw-r--r--sound/soc/uniphier/Kconfig2
-rw-r--r--sound/soc/xilinx/xlnx_spdif.c10
-rw-r--r--sound/sparc/dbri.c6
-rw-r--r--sound/usb/card.c7
-rw-r--r--sound/usb/format.c2
-rw-r--r--sound/usb/mixer.c5
-rw-r--r--sound/usb/mixer.h2
-rw-r--r--sound/usb/mixer_maps.c17
-rw-r--r--sound/usb/mixer_quirks.c4
-rw-r--r--sound/usb/mixer_quirks.h2
-rw-r--r--sound/usb/power.h10
-rw-r--r--sound/usb/quirks.c2
-rw-r--r--sound/usb/usx2y/usbusx2y.c2
-rw-r--r--sound/x86/intel_hdmi_audio.c4
317 files changed, 13717 insertions, 4541 deletions
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 79e1407cd0de..350d704ced98 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -19,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/control_led.c b/sound/core/control_led.c
index a95332b2b90b..207828f30983 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -509,7 +509,7 @@ static char *parse_string(char *s, char *val, size_t val_size)
return s;
}
-static char *parse_iface(char *s, unsigned int *val)
+static char *parse_iface(char *s, snd_ctl_elem_iface_t *val)
{
if (!strncasecmp(s, "card", 4))
*val = SNDRV_CTL_ELEM_IFACE_CARD;
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index 1ba887c7954e..ebc714b2f46b 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -32,10 +32,8 @@ int snd_oss_info_register(int dev, int num, char *string)
mutex_lock(&strings);
if (string == NULL) {
x = snd_sndstat_strings[num][dev];
- if (x) {
- kfree(x);
- x = NULL;
- }
+ kfree(x);
+ x = NULL;
} else {
x = kstrdup(string, GFP_KERNEL);
if (x == NULL) {
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 537df1e98f8a..d1e3055f2b6a 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -62,10 +62,13 @@ static int snd_jack_dev_free(struct snd_device *device)
struct snd_card *card = device->card;
struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
+ down_write(&card->controls_rwsem);
list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
+ up_write(&card->controls_rwsem);
+
if (jack->private_free)
jack->private_free(jack);
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 9fc971a704a9..d1fcd1d5adae 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -620,6 +620,52 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = {
.get_chunk_size = snd_dma_noncontig_get_chunk_size,
};
+/* x86-specific SG-buffer with WC pages */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it)))
+
+static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p = snd_dma_noncontig_alloc(dmab, size);
+ struct sg_table *sgt = dmab->private_data;
+ struct sg_page_iter iter;
+
+ if (!p)
+ return NULL;
+ for_each_sgtable_page(sgt, &iter, 0)
+ set_memory_wc(sg_wc_address(&iter), 1);
+ return p;
+}
+
+static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
+{
+ struct sg_table *sgt = dmab->private_data;
+ struct sg_page_iter iter;
+
+ for_each_sgtable_page(sgt, &iter, 0)
+ set_memory_wb(sg_wc_address(&iter), 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);
+ return dma_mmap_noncontiguous(dmab->dev.dev, area,
+ dmab->bytes, dmab->private_data);
+}
+
+static 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_noncontig_get_addr,
+ .get_page = snd_dma_noncontig_get_page,
+ .get_chunk_size = snd_dma_noncontig_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
+
/*
* Non-coherent pages allocator
*/
@@ -679,14 +725,13 @@ static const struct snd_malloc_ops *dma_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/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 20a0a4771b9a..3ee9edf85815 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -2065,7 +2065,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
int err, cmd;
#ifdef OSS_DEBUG
- pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger);
+ pr_debug("pcm_oss: trigger = 0x%x\n", trigger);
#endif
psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 6fd3677685d7..ba4a987ed1c6 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -810,7 +810,11 @@ EXPORT_SYMBOL(snd_pcm_new_internal);
static void free_chmap(struct snd_pcm_str *pstr)
{
if (pstr->chmap_kctl) {
- snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+ struct snd_card *card = pstr->pcm->card;
+
+ down_write(&card->controls_rwsem);
+ snd_ctl_remove(card, pstr->chmap_kctl);
+ up_write(&card->controls_rwsem);
pstr->chmap_kctl = NULL;
}
}
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 1fc2fa077574..af6f717e1e7e 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
* @dma_data: DAI DMA data
* @slave_config: DMA slave configuration
*
- * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and
- * slave_id fields of the DMA slave config from the same fields of the DAI DMA
+ * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width
+ * fields of the DMA slave config from the same fields of the DAI DMA
* data struct. The src and dst fields will be initialized depending on the
* direction of the substream. If the substream is a playback stream the dst
* fields will be initialized, if it is a capture stream the src fields will be
@@ -124,7 +124,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
slave_config->src_addr_width = dma_data->addr_width;
}
- slave_config->slave_id = dma_data->slave_id;
slave_config->peripheral_config = dma_data->peripheral_config;
slave_config->peripheral_size = dma_data->peripheral_size;
}
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 4f4b4739f987..f2090025236b 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -2128,11 +2128,28 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr;
+ snd_pcm_sframes_t diff;
int ret;
if (old_appl_ptr == appl_ptr)
return 0;
+ if (appl_ptr >= runtime->boundary)
+ return -EINVAL;
+ /*
+ * check if a rewind is requested by the application
+ */
+ if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) {
+ diff = appl_ptr - old_appl_ptr;
+ if (diff >= 0) {
+ if (diff > runtime->buffer_size)
+ return -EINVAL;
+ } else {
+ if (runtime->boundary + diff > runtime->buffer_size)
+ return -EINVAL;
+ }
+ }
+
runtime->control->appl_ptr = appl_ptr;
if (substream->ops->ack) {
ret = substream->ops->ack(substream);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index d6c02dea976c..bc933104c3ee 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -235,12 +235,15 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
/* -------------------------------------------------------- */
+#define MAX_CELL_PROCESSES_IN_QUEUE 1000
+
void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
{
unsigned long flags;
struct snd_seq_event_cell *cell;
snd_seq_tick_time_t cur_tick;
snd_seq_real_time_t cur_time;
+ int processed = 0;
if (q == NULL)
return;
@@ -263,6 +266,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
if (!cell)
break;
snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
/* Process time queue... */
@@ -272,14 +277,19 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
if (!cell)
break;
snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
+ out:
/* free lock */
spin_lock_irqsave(&q->check_lock, flags);
if (q->check_again) {
q->check_again = 0;
- spin_unlock_irqrestore(&q->check_lock, flags);
- goto __again;
+ if (processed < MAX_CELL_PROCESSES_IN_QUEUE) {
+ spin_unlock_irqrestore(&q->check_lock, flags);
+ goto __again;
+ }
}
q->check_blocked = 0;
spin_unlock_irqrestore(&q->check_lock, flags);
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 4abc38c70cae..f5cae49500c8 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -263,6 +263,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
}
/*
+ * drain output work queue
+ */
+static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ flush_work(&vmidi->output_work);
+}
+
+/*
* subscribe callback - allow output to rawmidi device
*/
static int snd_virmidi_subscribe(void *private_data,
@@ -336,6 +346,7 @@ static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
.open = snd_virmidi_output_open,
.close = snd_virmidi_output_close,
.trigger = snd_virmidi_output_trigger,
+ .drain = snd_virmidi_output_drain,
};
/*
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/drivers/virmidi.c b/sound/drivers/virmidi.c
index 7f7eed6faaae..58012de90c38 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -90,15 +90,12 @@ static int snd_virmidi_probe(struct platform_device *devptr)
}
for (idx = 0; idx < midi_devs[dev]; idx++) {
struct snd_rawmidi *rmidi;
- struct snd_virmidi_dev *rdev;
err = snd_virmidi_new(card, idx, &rmidi);
if (err < 0)
return err;
- rdev = rmidi->private_data;
vmidi->midi[idx] = rmidi;
strcpy(rmidi->name, "Virtual Raw MIDI");
- rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
}
strcpy(card->driver, "VirMIDI");
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 37154ed43bd5..d2b5724b463f 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -18,7 +18,7 @@
/**
* snd_hdac_ext_stream_init - initialize each stream (aka device)
* @bus: HD-audio core bus
- * @stream: HD-audio ext core stream object to initialize
+ * @hext_stream: HD-audio ext core stream object to initialize
* @idx: stream index number
* @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
* @tag: the tag id to assign
@@ -27,34 +27,34 @@
* invoke hdac stream initialization routine
*/
void snd_hdac_ext_stream_init(struct hdac_bus *bus,
- struct hdac_ext_stream *stream,
- int idx, int direction, int tag)
+ struct hdac_ext_stream *hext_stream,
+ int idx, int direction, int tag)
{
if (bus->ppcap) {
- stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
+ hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
AZX_PPHC_INTERVAL * idx;
- stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
+ hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
AZX_PPLC_MULTI * bus->num_streams +
AZX_PPLC_INTERVAL * idx;
}
if (bus->spbcap) {
- stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
+ hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_SPIB;
- stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
+ hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_MAXFIFO;
}
if (bus->drsmcap)
- stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
+ hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
AZX_DRSM_INTERVAL * idx;
- stream->decoupled = false;
- snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
+ hext_stream->decoupled = false;
+ snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
@@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
* @dir: direction of streams
*/
int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
- int num_stream, int dir)
+ int num_stream, int dir)
{
int stream_tag = 0;
int i, tag, idx = start_idx;
for (i = 0; i < num_stream; i++) {
- struct hdac_ext_stream *stream =
- kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
+ struct hdac_ext_stream *hext_stream =
+ kzalloc(sizeof(*hext_stream), GFP_KERNEL);
+ if (!hext_stream)
return -ENOMEM;
tag = ++stream_tag;
- snd_hdac_ext_stream_init(bus, stream, idx, dir, tag);
+ snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
idx++;
}
@@ -95,22 +95,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
void snd_hdac_stream_free_all(struct hdac_bus *bus)
{
struct hdac_stream *s, *_s;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
- stream = stream_to_hdac_ext_stream(s);
- snd_hdac_ext_stream_decouple(bus, stream, false);
+ hext_stream = stream_to_hdac_ext_stream(s);
+ snd_hdac_ext_stream_decouple(bus, hext_stream, false);
list_del(&s->list);
- kfree(stream);
+ kfree(hext_stream);
}
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
bool decouple)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
u32 val;
int mask = AZX_PPCTL_PROCEN(hstream->index);
@@ -121,76 +121,76 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
else if (!decouple && val)
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
- stream->decoupled = decouple;
+ hext_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
+ * @hext_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)
+ struct hdac_ext_stream *hext_stream, bool decouple)
{
spin_lock_irq(&bus->reg_lock);
- snd_hdac_ext_stream_decouple_locked(bus, stream, decouple);
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
spin_unlock_irq(&bus->reg_lock);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
/**
* snd_hdac_ext_link_stream_start - start a stream
- * @stream: HD-audio ext core stream to start
+ * @hext_stream: HD-audio ext core stream to start
*/
-void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream)
{
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL,
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
/**
* snd_hdac_ext_link_stream_clear - stop a stream DMA
- * @stream: HD-audio ext core stream to stop
+ * @hext_stream: HD-audio ext core stream to stop
*/
-void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream)
{
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
/**
* snd_hdac_ext_link_stream_reset - reset a stream
- * @stream: HD-audio ext core stream to reset
+ * @hext_stream: HD-audio ext core stream to reset
*/
-void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream)
+void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream)
{
unsigned char val;
int timeout;
- snd_hdac_ext_link_stream_clear(stream);
+ snd_hdac_ext_link_stream_clear(hext_stream);
- snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL,
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
udelay(3);
timeout = 50;
do {
- val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) &
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
AZX_PPLCCTL_STRST;
if (val)
break;
udelay(3);
} while (--timeout);
val &= ~AZX_PPLCCTL_STRST;
- writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
udelay(3);
timeout = 50;
/* waiting for hardware to report that the stream is out of reset */
do {
- val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
if (!val)
break;
udelay(3);
@@ -201,24 +201,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
/**
* snd_hdac_ext_link_stream_setup - set up the SD for streaming
- * @stream: HD-audio ext core stream to set up
+ * @hext_stream: HD-audio ext core stream to set up
* @fmt: stream format
*/
-int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt)
+int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
unsigned int val;
/* make sure the run bit is zero for SD */
- snd_hdac_ext_link_stream_clear(stream);
+ snd_hdac_ext_link_stream_clear(hext_stream);
/* program the stream_tag */
- val = readl(stream->pplc_addr + AZX_REG_PPLCCTL);
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
val = (val & ~AZX_PPLCCTL_STRM_MASK) |
(hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
- writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
/* program the stream format */
- writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT);
+ writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
return 0;
}
@@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
* @stream: stream id
*/
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
- int stream)
+ int stream)
{
snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
}
@@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
static struct hdac_ext_stream *
hdac_ext_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
+ struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
+ struct hdac_stream *hstream = NULL;
if (!bus->ppcap) {
dev_err(bus->dev, "stream type not supported\n");
@@ -261,22 +261,22 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
}
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,
- hstream);
- if (stream->direction != substream->stream)
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
continue;
/* check if decoupled stream and not in use is available */
- if (hstream->decoupled && !hstream->link_locked) {
- res = hstream;
+ if (hext_stream->decoupled && !hext_stream->link_locked) {
+ res = hext_stream;
break;
}
- if (!hstream->link_locked) {
- snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
- res = hstream;
+ if (!hext_stream->link_locked) {
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
+ res = hext_stream;
break;
}
}
@@ -290,10 +290,10 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
static struct hdac_ext_stream *
hdac_ext_host_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
+ struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
+ struct hdac_stream *hstream = NULL;
if (!bus->ppcap) {
dev_err(bus->dev, "stream type not supported\n");
@@ -301,17 +301,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
}
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,
- hstream);
- if (stream->direction != substream->stream)
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
continue;
- if (!stream->opened) {
- if (!hstream->decoupled)
- snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
- res = hstream;
+ if (!hstream->opened) {
+ if (!hext_stream->decoupled)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
+ res = hext_stream;
break;
}
}
@@ -346,16 +346,17 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream,
int type)
{
- struct hdac_ext_stream *hstream = NULL;
- struct hdac_stream *stream = NULL;
+ struct hdac_ext_stream *hext_stream = NULL;
+ struct hdac_stream *hstream = NULL;
switch (type) {
case HDAC_EXT_STREAM_TYPE_COUPLED:
- stream = snd_hdac_stream_assign(bus, substream);
- if (stream)
- hstream = container_of(stream,
- struct hdac_ext_stream, hstream);
- return hstream;
+ hstream = snd_hdac_stream_assign(bus, substream);
+ if (hstream)
+ hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ return hext_stream;
case HDAC_EXT_STREAM_TYPE_HOST:
return hdac_ext_host_stream_assign(bus, substream);
@@ -371,34 +372,34 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
/**
* snd_hdac_ext_stream_release - release the assigned stream
- * @stream: HD-audio ext core stream to release
+ * @hext_stream: HD-audio ext core stream to release
* @type: type of stream (coupled, host or link stream)
*
* Release the stream that has been assigned by snd_hdac_ext_stream_assign().
*/
-void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
{
- struct hdac_bus *bus = stream->hstream.bus;
+ struct hdac_bus *bus = hext_stream->hstream.bus;
switch (type) {
case HDAC_EXT_STREAM_TYPE_COUPLED:
- snd_hdac_stream_release(&stream->hstream);
+ snd_hdac_stream_release(&hext_stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_HOST:
spin_lock_irq(&bus->reg_lock);
- if (stream->decoupled && !stream->link_locked)
- snd_hdac_ext_stream_decouple_locked(bus, stream, false);
+ if (hext_stream->decoupled && !hext_stream->link_locked)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
spin_unlock_irq(&bus->reg_lock);
- snd_hdac_stream_release(&stream->hstream);
+ snd_hdac_stream_release(&hext_stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_LINK:
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;
+ if (hext_stream->decoupled && !hext_stream->hstream.opened)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+ hext_stream->link_locked = 0;
+ hext_stream->link_substream = NULL;
spin_unlock_irq(&bus->reg_lock);
break;
@@ -437,11 +438,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
/**
* snd_hdac_ext_stream_set_spib - sets the spib value of a stream
* @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
* @value: spib value to set
*/
int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
- struct hdac_ext_stream *stream, u32 value)
+ struct hdac_ext_stream *hext_stream, u32 value)
{
if (!bus->spbcap) {
@@ -449,7 +450,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
return -EINVAL;
}
- writel(value, stream->spib_addr);
+ writel(value, hext_stream->spib_addr);
return 0;
}
@@ -458,12 +459,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
/**
* snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
* @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
*
* Return maxfifo for the stream
*/
int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
- struct hdac_ext_stream *stream)
+ struct hdac_ext_stream *hext_stream)
{
if (!bus->spbcap) {
@@ -471,27 +472,10 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
return -EINVAL;
}
- return readl(stream->fifo_addr);
+ return readl(hext_stream->fifo_addr);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
-
-/**
- * snd_hdac_ext_stop_streams - stop all stream if running
- * @bus: HD-audio core bus
- */
-void snd_hdac_ext_stop_streams(struct hdac_bus *bus)
-{
- struct hdac_stream *stream;
-
- if (bus->chip_init) {
- list_for_each_entry(stream, &bus->stream_list, list)
- snd_hdac_stream_stop(stream);
- snd_hdac_bus_stop_chip(bus);
- }
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
-
/**
* snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
* @bus: HD-audio core bus
@@ -520,11 +504,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
/**
* snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
* @bus: HD-audio core bus
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
* @value: dpib value to set
*/
int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
- struct hdac_ext_stream *stream, u32 value)
+ struct hdac_ext_stream *hext_stream, u32 value)
{
if (!bus->drsmcap) {
@@ -532,7 +516,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
return -EINVAL;
}
- writel(value, stream->dpibr_addr);
+ writel(value, hext_stream->dpibr_addr);
return 0;
}
@@ -540,12 +524,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
/**
* snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
- * @stream: hdac_ext_stream
+ * @hext_stream: hdac_ext_stream
* @value: lpib value to set
*/
-int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value)
+int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value)
{
- snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value);
+ snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value);
return 0;
}
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 9867555883c3..f3582012d22f 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -143,6 +143,22 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
/**
+ * snd_hdac_stop_streams_and_chip - stop all streams and chip if running
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus)
+{
+ struct hdac_stream *stream;
+
+ if (bus->chip_init) {
+ list_for_each_entry(stream, &bus->stream_list, list)
+ snd_hdac_stream_stop(stream);
+ snd_hdac_bus_stop_chip(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip);
+
+/**
* snd_hdac_stream_reset - reset a stream
* @azx_dev: HD-audio core stream to reset
*/
@@ -534,17 +550,11 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev,
cc->mask = CLOCKSOURCE_MASK(32);
/*
- * Converting from 24 MHz to ns means applying a 125/3 factor.
- * To avoid any saturation issues in intermediate operations,
- * the 125 factor is applied first. The division is applied
- * last after reading the timecounter value.
- * Applying the 1/3 factor as part of the multiplication
- * requires at least 20 bits for a decent precision, however
- * overflows occur after about 4 hours or less, not a option.
+ * Calculate the optimal mult/shift values. The counter wraps
+ * around after ~178.9 seconds.
*/
-
- cc->mult = 125; /* saturation after 195 years */
- cc->shift = 0;
+ clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000,
+ NSEC_PER_SEC, 178);
nsec = 0; /* audio time is elapsed time since trigger */
timecounter_init(tc, cc, nsec);
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index 4208fa8a4db5..4fb90ceb4053 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -249,13 +249,13 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .flags = FLAG_SOF,
.device = 0x02c8,
+ .codec_hid = "ESSX8336",
},
{
- .flags = FLAG_SOF,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x02c8,
- .codec_hid = "ESSX8336",
},
/* Cometlake-H */
{
@@ -278,14 +278,14 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
- .device = 0x06c8,
- },
- {
.flags = FLAG_SOF,
.device = 0x06c8,
.codec_hid = "ESSX8336",
},
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x06c8,
+ },
#endif
/* Icelake */
@@ -309,13 +309,30 @@ static const struct config_entry config_table[] = {
},
#endif
-/* JasperLake */
+/* Jasper Lake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
{
.flags = FLAG_SOF,
.device = 0x4dc8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x4dc8,
.codec_hid = "ESSX8336",
},
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = 0x4dc8,
+ },
#endif
/* Tigerlake */
@@ -334,17 +351,17 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .flags = FLAG_SOF,
.device = 0xa0c8,
+ .codec_hid = "ESSX8336",
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
- .device = 0x43c8,
+ .device = 0xa0c8,
},
{
- .flags = FLAG_SOF,
- .device = 0xa0c8,
- .codec_hid = "ESSX8336",
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x43c8,
},
#endif
@@ -374,6 +391,14 @@ static const struct config_entry config_table[] = {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x51cc,
},
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51cd,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x54c8,
+ },
#endif
};
@@ -403,7 +428,7 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
nhlt = intel_nhlt_init(&pci->dev);
if (nhlt) {
- if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC))
ret = 1;
intel_nhlt_free(nhlt);
}
diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
index e2237239d922..128476aa7c61 100644
--- a/sound/hda/intel-nhlt.c
+++ b/sound/hda/intel-nhlt.c
@@ -110,3 +110,105 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
return dmic_geo;
}
EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
+
+bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type)
+{
+ struct nhlt_endpoint *epnt;
+ int i;
+
+ if (!nhlt)
+ return false;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (epnt->linktype == link_type)
+ return true;
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+ return false;
+}
+EXPORT_SYMBOL(intel_nhlt_has_endpoint_type);
+
+static struct nhlt_specific_cfg *
+nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
+ u32 rate, u8 vbps, u8 bps)
+{
+ struct nhlt_fmt_cfg *cfg = fmt->fmt_config;
+ struct wav_fmt *wfmt;
+ u16 _bps, _vbps;
+ int i;
+
+ dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count);
+
+ for (i = 0; i < fmt->fmt_count; i++) {
+ wfmt = &cfg->fmt_ext.fmt;
+ _bps = wfmt->bits_per_sample;
+ _vbps = cfg->fmt_ext.sample.valid_bits_per_sample;
+
+ dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n",
+ wfmt->channels, _vbps, _bps, wfmt->samples_per_sec);
+
+ if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+ vbps == _vbps && bps == _bps)
+ return &cfg->config;
+
+ cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+ }
+
+ return NULL;
+}
+
+static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
+ u32 bus_id, u8 link_type, u8 dir, u8 dev_type)
+{
+ dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
+ epnt->virtual_bus_id, epnt->linktype,
+ epnt->direction, epnt->device_type);
+
+ if ((epnt->virtual_bus_id != bus_id) ||
+ (epnt->linktype != link_type) ||
+ (epnt->direction != dir))
+ return false;
+
+ /* link of type DMIC bypasses device_type check */
+ return epnt->linktype == NHLT_LINK_DMIC ||
+ epnt->device_type == dev_type;
+}
+
+struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+ u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+ u8 num_ch, u32 rate, u8 dir, u8 dev_type)
+{
+ struct nhlt_specific_cfg *cfg;
+ struct nhlt_endpoint *epnt;
+ struct nhlt_fmt *fmt;
+ int i;
+
+ if (!nhlt)
+ return NULL;
+
+ dev_dbg(dev, "Looking for configuration:\n");
+ dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n",
+ bus_id, link_type, dir, dev_type);
+ dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate);
+ dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count);
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) {
+ fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+ cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps);
+ if (cfg)
+ return cfg;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index ff9480f249fe..3e56c01c4544 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -24,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
}
}
-static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
- struct snd_gf1_mem_block * block)
+static struct snd_gf1_mem_block *
+snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block,
+ const char *name)
{
struct snd_gf1_mem_block *pblock, *nblock;
@@ -33,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
if (nblock == NULL)
return NULL;
*nblock = *block;
+ nblock->name = kstrdup(name, GFP_KERNEL);
+ if (!nblock->name) {
+ kfree(nblock);
+ return NULL;
+ }
+
pblock = alloc->first;
while (pblock) {
if (pblock->ptr > nblock->ptr) {
@@ -44,7 +51,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
else
nblock->prev->next = nblock;
mutex_unlock(&alloc->memory_mutex);
- return NULL;
+ return nblock;
}
pblock = pblock->next;
}
@@ -198,8 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne
if (share_id != NULL)
memcpy(&block.share_id, share_id, sizeof(block.share_id));
block.owner = owner;
- block.name = kstrdup(name, GFP_KERNEL);
- nblock = snd_gf1_mem_xalloc(alloc, &block);
+ nblock = snd_gf1_mem_xalloc(alloc, &block, name);
snd_gf1_mem_lock(alloc, 1);
return nblock;
}
@@ -236,14 +242,12 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
if (gus->gf1.enh_mode) {
block.ptr = 0;
block.size = 1024;
- block.name = kstrdup("InterWave LFOs", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs"))
return -ENOMEM;
}
block.ptr = gus->gf1.default_voice_address;
block.size = 4;
- block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)"))
return -ENOMEM;
#ifdef CONFIG_SND_DEBUG
snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 491de1a623cb..5fee8e89790f 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -231,7 +231,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
* If the codec doesn't support VAR, the rate must be 48000 (except
* for SPDIF).
*
- * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * The valid registers are AC97_PCM_MIC_ADC_RATE,
* AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
* AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
* if the codec supports them.
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index ab9d2746e804..febe1c2b7d9a 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -91,6 +91,39 @@ config SND_HDA_PATCH_LOADER
start up. The "patch" file can be specified via patch module
option, such as patch=hda-init.
+config SND_HDA_SCODEC_CS35L41
+ tristate
+
+config SND_HDA_SCODEC_CS35L41_I2C
+ tristate "Build CS35L41 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on SND_SOC
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ help
+ Say Y or M here to include CS35L41 I2C HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m
+
+config SND_HDA_SCODEC_CS35L41_SPI
+ tristate "Build CS35L41 HD-audio codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on SND_SOC
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ help
+ Say Y or M here to include CS35L41 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m
+
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index b8fa682ce66a..3e7bc608d45f 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -27,6 +27,11 @@ snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
+# side codecs
+snd-hda-scodec-cs35l41-objs := cs35l41_hda.o
+snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o
+snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o
+
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
@@ -45,6 +50,11 @@ obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
+# side codecs
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o
+
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
# when built in kernel
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
new file mode 100644
index 000000000000..30b40d865863
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 ALSA HDA audio driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+#include "hda_component.h"
+#include "cs35l41_hda.h"
+
+static const struct reg_sequence cs35l41_hda_config[] = {
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, //3200000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, //GLOBAL_FS = 48 kHz
+ { CS35L41_SP_ENABLES, 0x00010000 }, //ASP_RX1_EN = 1
+ { CS35L41_SP_RATE_CTRL, 0x00000021 }, //ASP_BCLK_FREQ = 3.072 MHz
+ { CS35L41_SP_FORMAT, 0x20200200 }, //24 bits, I2S, BCLK Slave, FSYNC Slave
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 }, //DACPCM1_SRC = ASPRX1
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, //AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, //AMP_GAIN_PCM 4.5 dB
+ { CS35L41_PWR_CTRL2, 0x00000001 }, //AMP_EN = 1
+};
+
+static const struct reg_sequence cs35l41_hda_start_bst[] = {
+ { CS35L41_PWR_CTRL2, 0x00000021 }, //BST_EN = 10, AMP_EN = 1
+ { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
+};
+
+static const struct reg_sequence cs35l41_hda_stop_bst[] = {
+ { CS35L41_PWR_CTRL1, 0x00000000, 3000}, // set GLOBAL_EN = 0
+};
+
+// only on amps where GPIO1 is used to control ext. VSPK switch
+static const struct reg_sequence cs35l41_start_ext_vspk[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00007414, 0x08C82222 },
+ { 0x0000742C, 0x00000009 },
+ { 0x00011008, 0x00008001 },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+//only on amps where GPIO1 is used to control ext. VSPK switch
+static const struct reg_sequence cs35l41_stop_ext_vspk[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00002014, 0x00000000, 3000}, //set GLOBAL_EN = 0
+ { 0x0000742C, 0x00000009 },
+ { 0x00007438, 0x00580941 },
+ { 0x00011008, 0x00000001 },
+ { 0x0000393C, 0x000000C0, 6000},
+ { 0x0000393C, 0x00000000 },
+ { 0x00007414, 0x00C82222 },
+ { 0x0000742C, 0x00000000 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_safe_to_active[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001, 2000 }, //GLOBAL_EN = 1
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, //AMP_VOL_PCM Mute
+ { CS35L41_PWR_CTRL2, 0x00000000 }, //AMP_EN = 0
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { 0x0000742C, 0x00000009, 2000 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_reset_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00007414, 0x08C82222 },
+ { 0x0000742C, 0x00000009 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_no_bst = {
+ .probe = cs35l41_reset_to_safe,
+ .num_probe = ARRAY_SIZE(cs35l41_reset_to_safe),
+ .open = cs35l41_hda_config,
+ .num_open = ARRAY_SIZE(cs35l41_hda_config),
+ .prepare = cs35l41_safe_to_active,
+ .num_prepare = ARRAY_SIZE(cs35l41_safe_to_active),
+ .cleanup = cs35l41_active_to_safe,
+ .num_cleanup = ARRAY_SIZE(cs35l41_active_to_safe),
+};
+
+static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_ext_bst = {
+ .open = cs35l41_hda_config,
+ .num_open = ARRAY_SIZE(cs35l41_hda_config),
+ .prepare = cs35l41_start_ext_vspk,
+ .num_prepare = ARRAY_SIZE(cs35l41_start_ext_vspk),
+ .cleanup = cs35l41_stop_ext_vspk,
+ .num_cleanup = ARRAY_SIZE(cs35l41_stop_ext_vspk),
+};
+
+static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_int_bst = {
+ .open = cs35l41_hda_config,
+ .num_open = ARRAY_SIZE(cs35l41_hda_config),
+ .prepare = cs35l41_hda_start_bst,
+ .num_prepare = ARRAY_SIZE(cs35l41_hda_start_bst),
+ .cleanup = cs35l41_hda_stop_bst,
+ .num_cleanup = ARRAY_SIZE(cs35l41_hda_stop_bst),
+};
+
+static void cs35l41_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ const struct cs35l41_hda_reg_sequence *reg_seq = cs35l41->reg_seq;
+ struct regmap *reg = cs35l41->regmap;
+ int ret = 0;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ if (reg_seq->open)
+ ret = regmap_multi_reg_write(reg, reg_seq->open, reg_seq->num_open);
+ break;
+ case HDA_GEN_PCM_ACT_PREPARE:
+ if (reg_seq->prepare)
+ ret = regmap_multi_reg_write(reg, reg_seq->prepare, reg_seq->num_prepare);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ if (reg_seq->cleanup)
+ ret = regmap_multi_reg_write(reg, reg_seq->cleanup, reg_seq->num_cleanup);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ if (reg_seq->close)
+ ret = regmap_multi_reg_write(reg, reg_seq->close, reg_seq->num_close);
+ break;
+ }
+
+ if (ret)
+ dev_warn(cs35l41->dev, "Failed to apply multi reg write: %d\n", ret);
+
+}
+
+static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
+ rx_slot);
+}
+
+static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ if (comps && cs35l41->index >= 0 && cs35l41->index < HDA_MAX_COMPONENTS)
+ comps = &comps[cs35l41->index];
+ else
+ return -EINVAL;
+
+ if (!comps->dev) {
+ comps->dev = dev;
+ strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+ comps->playback_hook = cs35l41_hda_playback_hook;
+ comps->set_channel_map = cs35l41_hda_channel_map;
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ if (comps[cs35l41->index].dev == dev)
+ memset(&comps[cs35l41->index], 0, sizeof(*comps));
+}
+
+static const struct component_ops cs35l41_hda_comp_ops = {
+ .bind = cs35l41_hda_bind,
+ .unbind = cs35l41_hda_unbind,
+};
+
+static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41,
+ const struct cs35l41_hda_hw_config *hw_cfg)
+{
+ bool internal_boost = false;
+ int ret;
+
+ if (!hw_cfg) {
+ cs35l41->reg_seq = &cs35l41_hda_reg_seq_no_bst;
+ return 0;
+ }
+
+ if (hw_cfg->bst_ind || hw_cfg->bst_cap || hw_cfg->bst_ipk)
+ internal_boost = true;
+
+ switch (hw_cfg->gpio1_func) {
+ case CS35l41_VSPK_SWITCH:
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
+ CS35L41_GPIO1_CTRL_MASK, 1 << CS35L41_GPIO1_CTRL_SHIFT);
+ break;
+ case CS35l41_SYNC:
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
+ CS35L41_GPIO1_CTRL_MASK, 2 << CS35L41_GPIO1_CTRL_SHIFT);
+ break;
+ }
+
+ switch (hw_cfg->gpio2_func) {
+ case CS35L41_INTERRUPT:
+ regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
+ CS35L41_GPIO2_CTRL_MASK, 2 << CS35L41_GPIO2_CTRL_SHIFT);
+ break;
+ }
+
+ if (internal_boost) {
+ cs35l41->reg_seq = &cs35l41_hda_reg_seq_int_bst;
+ if (!(hw_cfg->bst_ind && hw_cfg->bst_cap && hw_cfg->bst_ipk))
+ return -EINVAL;
+ ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap,
+ hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ if (ret)
+ return ret;
+ } else {
+ cs35l41->reg_seq = &cs35l41_hda_reg_seq_ext_bst;
+ }
+
+ ret = cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, (unsigned int *)&hw_cfg->spk_pos);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41,
+ const char *hid, int id)
+{
+ struct cs35l41_hda_hw_config *hw_cfg;
+ u32 values[HDA_MAX_COMPONENTS];
+ struct acpi_device *adev;
+ struct device *acpi_dev;
+ char *property;
+ size_t nval;
+ int i, ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
+ return ERR_PTR(-ENODEV);
+ }
+
+ acpi_dev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(acpi_dev, property);
+ if (ret <= 0)
+ goto no_acpi_dsd;
+
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret)
+ goto err;
+
+ cs35l41->index = -1;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l41->index = i;
+ break;
+ }
+ }
+ if (cs35l41->index == -1) {
+ dev_err(cs35l41->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* No devm_ version as CLSA0100, in no_acpi_dsd case, can't use devm version */
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(&adev->fwnode, "reset", cs35l41->index,
+ GPIOD_OUT_LOW, "cs35l41-reset");
+
+ hw_cfg = kzalloc(sizeof(*hw_cfg), GFP_KERNEL);
+ if (!hw_cfg) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ property = "cirrus,speaker-position";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret)
+ goto err_free;
+ hw_cfg->spk_pos = values[cs35l41->index];
+
+ property = "cirrus,gpio1-func";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret)
+ goto err_free;
+ hw_cfg->gpio1_func = values[cs35l41->index];
+
+ property = "cirrus,gpio2-func";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret)
+ goto err_free;
+ hw_cfg->gpio2_func = values[cs35l41->index];
+
+ property = "cirrus,boost-peak-milliamp";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ipk = values[cs35l41->index];
+
+ property = "cirrus,boost-ind-nanohenry";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ind = values[cs35l41->index];
+
+ property = "cirrus,boost-cap-microfarad";
+ ret = device_property_read_u32_array(acpi_dev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_cap = values[cs35l41->index];
+
+ put_device(acpi_dev);
+
+ return hw_cfg;
+
+err_free:
+ kfree(hw_cfg);
+err:
+ put_device(acpi_dev);
+ dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
+
+ return ERR_PTR(ret);
+
+no_acpi_dsd:
+ /*
+ * Device CLSA0100 doesn't have _DSD so a gpiod_get by the label reset won't work.
+ * And devices created by i2c-multi-instantiate don't have their device struct pointing to
+ * the correct fwnode, so acpi_dev must be used here
+ * And devm functions expect that the device requesting the resource has the correct
+ * fwnode
+ */
+ if (strncmp(hid, "CLSA0100", 8) != 0)
+ return ERR_PTR(-EINVAL);
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->reset_gpio = gpiod_get_index(acpi_dev, NULL, 0, GPIOD_OUT_HIGH);
+ cs35l41->vspk_always_on = true;
+ put_device(acpi_dev);
+
+ return NULL;
+}
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap)
+{
+ unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status;
+ struct cs35l41_hda_hw_config *acpi_hw_cfg;
+ struct cs35l41_hda *cs35l41;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = irq;
+ cs35l41->regmap = regmap;
+ dev_set_drvdata(dev, cs35l41);
+
+ acpi_hw_cfg = cs35l41_hda_read_acpi(cs35l41, device_name, id);
+ if (IS_ERR(acpi_hw_cfg))
+ return PTR_ERR(acpi_hw_cfg);
+
+ 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 {
+ if (ret != -EPROBE_DEFER)
+ dev_err(cs35l41->dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ 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;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts);
+ if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) {
+ dev_err(cs35l41->dev, "OTP Boot error\n");
+ ret = -EIO;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+ if (ret) {
+ dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret) {
+ dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+ chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (regid != chipid) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", regid, chipid);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_hda_apply_properties(cs35l41, acpi_hw_cfg);
+ if (ret)
+ goto err;
+ kfree(acpi_hw_cfg);
+ acpi_hw_cfg = NULL;
+
+ if (cs35l41->reg_seq->probe) {
+ ret = regmap_register_patch(cs35l41->regmap, cs35l41->reg_seq->probe,
+ cs35l41->reg_seq->num_probe);
+ if (ret) {
+ dev_err(cs35l41->dev, "Fail to apply probe reg patch: %d\n", ret);
+ goto err;
+ }
+ }
+
+ ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
+ if (ret) {
+ dev_err(cs35l41->dev, "Register component failed: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+
+err:
+ kfree(acpi_hw_cfg);
+ if (!cs35l41->vspk_always_on)
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_hda_probe);
+
+int cs35l41_hda_remove(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+
+ if (!cs35l41->vspk_always_on)
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_hda_remove);
+
+
+MODULE_DESCRIPTION("CS35L41 HDA Driver");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
new file mode 100644
index 000000000000..76c69a8a22f6
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * cs35l41_hda.h -- CS35L41 ALSA HDA audio driver
+ *
+ * Copyright 2021 Cirrus Logic, Inc.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#ifndef __CS35L41_HDA_H__
+#define __CS35L41_HDA_H__
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <sound/cs35l41.h>
+
+enum cs35l41_hda_spk_pos {
+ CS35l41_LEFT,
+ CS35l41_RIGHT,
+};
+
+enum cs35l41_hda_gpio_function {
+ CS35L41_NOT_USED,
+ CS35l41_VSPK_SWITCH,
+ CS35L41_INTERRUPT,
+ CS35l41_SYNC,
+};
+
+struct cs35l41_hda_reg_sequence {
+ const struct reg_sequence *probe;
+ unsigned int num_probe;
+ const struct reg_sequence *open;
+ unsigned int num_open;
+ const struct reg_sequence *prepare;
+ unsigned int num_prepare;
+ const struct reg_sequence *cleanup;
+ unsigned int num_cleanup;
+ const struct reg_sequence *close;
+ unsigned int num_close;
+};
+
+struct cs35l41_hda_hw_config {
+ unsigned int spk_pos;
+ unsigned int gpio1_func;
+ unsigned int gpio2_func;
+ int bst_ind;
+ int bst_ipk;
+ int bst_cap;
+};
+
+struct cs35l41_hda {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ const struct cs35l41_hda_reg_sequence *reg_seq;
+
+ int irq;
+ int index;
+
+ /* Don't put the AMP in reset of VSPK can not be turned off */
+ bool vspk_always_on;
+};
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap);
+int cs35l41_hda_remove(struct device *dev);
+
+#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c
new file mode 100644
index 000000000000..4a9462fb5c14
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_i2c.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 HDA I2C driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_i2c_probe(struct i2c_client *clt, const struct i2c_device_id *id)
+{
+ const char *device_name;
+
+ /* Compare against the device name so it works for I2C, normal ACPI
+ * and for ACPI by i2c-multi-instantiate matching cases
+ */
+ if (strstr(dev_name(&clt->dev), "CLSA0100"))
+ device_name = "CLSA0100";
+ else if (strstr(dev_name(&clt->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq,
+ devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c));
+}
+
+static int cs35l41_hda_i2c_remove(struct i2c_client *clt)
+{
+ return cs35l41_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l41_hda_i2c_id[] = {
+ { "cs35l41-hda", 0 },
+ {}
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ {"CLSA0100", 0 },
+ {"CSC3551", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+#endif
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41-hda",
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match),
+ },
+ .id_table = cs35l41_hda_i2c_id,
+ .probe = cs35l41_hda_i2c_probe,
+ .remove = cs35l41_hda_i2c_remove,
+};
+
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c
new file mode 100644
index 000000000000..77426e96c58f
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_spi.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 HDA SPI driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_spi_probe(struct spi_device *spi)
+{
+ const char *device_name;
+
+ /* Compare against the device name so it works for SPI, normal ACPI
+ * and for ACPI by spi-multi-instantiate matching cases
+ */
+ if (strstr(dev_name(&spi->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&spi->dev, device_name, spi->chip_select, spi->irq,
+ devm_regmap_init_spi(spi, &cs35l41_regmap_spi));
+}
+
+static int cs35l41_hda_spi_remove(struct spi_device *spi)
+{
+ return cs35l41_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l41_hda_spi_id[] = {
+ { "cs35l41-hda", 0 },
+ {}
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ { "CSC3551", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+#endif
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41_hda",
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match),
+ },
+ .id_table = cs35l41_hda_spi_id,
+ .probe = cs35l41_hda_spi_probe,
+ .remove = cs35l41_hda_spi_remove,
+};
+
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 4a854475a0e6..82c492b05667 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -92,14 +92,10 @@ static int compare_input_type(const void *ap, const void *bp)
*/
static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
{
- hda_nid_t nid;
-
switch (nums) {
case 3:
case 4:
- nid = pins[1];
- pins[1] = pins[2];
- pins[2] = nid;
+ swap(pins[1], pins[2]);
break;
}
}
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 1c8bffc3eec6..c572fb5886d5 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -14,6 +14,7 @@
#include <sound/core.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_jack.h"
/*
* find a matching codec id
@@ -156,6 +157,12 @@ static int hda_codec_driver_remove(struct device *dev)
return codec->bus->core.ext_ops->hdev_detach(&codec->core);
}
+ refcount_dec(&codec->pcm_ref);
+ snd_hda_codec_disconnect_pcms(codec);
+ snd_hda_jack_tbl_disconnect(codec);
+ wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
+ snd_power_sync_ref(codec->bus->card);
+
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 0c4a337c9fc0..7016b48227bf 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -703,20 +703,10 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
/*
* PCM device
*/
-static void release_pcm(struct kref *kref)
-{
- struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
-
- if (pcm->pcm)
- snd_device_free(pcm->codec->card, pcm->pcm);
- clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
- kfree(pcm->name);
- kfree(pcm);
-}
-
void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
{
- kref_put(&pcm->kref, release_pcm);
+ if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+ wake_up(&pcm->codec->remove_sleep);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
@@ -731,7 +721,6 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
return NULL;
pcm->codec = codec;
- kref_init(&pcm->kref);
va_start(args, fmt);
pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
va_end(args);
@@ -741,6 +730,7 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
}
list_add_tail(&pcm->list, &codec->pcm_list_head);
+ refcount_inc(&codec->pcm_ref);
return pcm;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
@@ -748,15 +738,31 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
/*
* codec destructor
*/
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ if (pcm->disconnected)
+ continue;
+ if (pcm->pcm)
+ snd_device_disconnect(codec->card, pcm->pcm);
+ snd_hda_codec_pcm_put(pcm);
+ pcm->disconnected = 1;
+ }
+}
+
static void codec_release_pcms(struct hda_codec *codec)
{
struct hda_pcm *pcm, *n;
list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
- list_del_init(&pcm->list);
+ list_del(&pcm->list);
if (pcm->pcm)
- snd_device_disconnect(codec->card, pcm->pcm);
- snd_hda_codec_pcm_put(pcm);
+ snd_device_free(pcm->codec->card, pcm->pcm);
+ clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+ kfree(pcm->name);
+ kfree(pcm);
}
}
@@ -769,6 +775,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
codec->registered = 0;
}
+ snd_hda_codec_disconnect_pcms(codec);
cancel_delayed_work_sync(&codec->jackpoll_work);
if (!codec->in_freeing)
snd_hda_ctls_clear(codec);
@@ -792,6 +799,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
remove_conn_list(codec);
snd_hdac_regmap_exit(&codec->core);
codec->configured = 0;
+ refcount_set(&codec->pcm_ref, 1); /* reset refcount */
}
EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
@@ -958,6 +966,8 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
INIT_LIST_HEAD(&codec->conn_list);
INIT_LIST_HEAD(&codec->pcm_list_head);
+ refcount_set(&codec->pcm_ref, 1);
+ init_waitqueue_head(&codec->remove_sleep);
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
codec->depop_delay = -1;
@@ -1727,8 +1737,11 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
{
int i;
struct hda_nid_item *items = codec->mixers.list;
+
+ down_write(&codec->card->controls_rwsem);
for (i = 0; i < codec->mixers.used; i++)
snd_ctl_remove(codec->card, items[i].kctl);
+ up_write(&codec->card->controls_rwsem);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
}
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
new file mode 100644
index 000000000000..2e52be6db9c2
--- /dev/null
+++ b/sound/pci/hda/hda_component.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/component.h>
+
+#define HDA_MAX_COMPONENTS 4
+#define HDA_MAX_NAME_SIZE 50
+
+struct hda_component {
+ struct device *dev;
+ char name[HDA_MAX_NAME_SIZE];
+ void (*playback_hook)(struct device *dev, int action);
+ int (*set_channel_map)(struct device *dev, unsigned int rx_num, unsigned int *rx_slot,
+ unsigned int tx_num, unsigned int *tx_slot);
+};
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 930ae4002a81..75dcb14ff20a 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -504,7 +504,6 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
snd_pcm_gettime(substream->runtime, system_ts);
nsec = timecounter_read(&azx_dev->core.tc);
- nsec = div_u64(nsec, 3); /* can be optimized */
if (audio_tstamp_config->report_delay)
nsec = azx_adjust_codec_delay(substream, nsec);
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index c43bd0f0338e..8e1bc8ea74fc 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -183,7 +183,7 @@ struct hda_gen_spec {
struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
/* for pin sensing */
- /* current status; set in hda_geneic.c */
+ /* current status; set in hda_generic.c */
unsigned int hp_jack_present:1;
unsigned int line_jack_present:1;
unsigned int speaker_muted:1; /* current status of speaker mute */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1b46b599a5cf..4b0338c4c543 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1350,8 +1350,12 @@ static void azx_free(struct azx *chip)
if (hda->freed)
return;
- if (azx_has_pm_runtime(chip) && chip->running)
+ if (azx_has_pm_runtime(chip) && chip->running) {
pm_runtime_get_noresume(&pci->dev);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_dont_use_autosuspend(&pci->dev);
+ }
+
chip->running = 0;
azx_del_card_list(chip);
@@ -2489,9 +2493,14 @@ static const struct pci_device_id azx_ids[] = {
/* Alderlake-P */
{ PCI_DEVICE(0x8086, 0x51c8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cd),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Alderlake-M */
{ PCI_DEVICE(0x8086, 0x51cc),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Alderlake-N */
+ { PCI_DEVICE(0x8086, 0x54c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Elkhart Lake */
{ PCI_DEVICE(0x8086, 0x4b55),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index f29975e3e98d..7d7786df60ea 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -158,6 +158,17 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
return jack;
}
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ if (!codec->bus->shutdown && jack->jack)
+ snd_device_disconnect(codec->card, jack->jack);
+ }
+}
+
void snd_hda_jack_tbl_clear(struct hda_codec *codec)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 2abf7aac243a..ff7d289c034b 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -69,6 +69,7 @@ struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
unsigned char tag, int dev_id);
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
void snd_hda_jack_tbl_clear(struct hda_codec *codec);
void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index d22c96eb2f8f..8621f576446b 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -137,6 +137,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
int snd_hda_codec_reset(struct hda_codec *codec);
void snd_hda_codec_register(struct hda_codec *codec);
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index ea700395bef4..773f4903550a 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -68,14 +68,20 @@
*/
#define TEGRA194_NUM_SDO_LINES 4
+struct hda_tegra_soc {
+ bool has_hda2codec_2x_reset;
+};
+
struct hda_tegra {
struct azx chip;
struct device *dev;
- struct reset_control *reset;
+ struct reset_control_bulk_data resets[3];
struct clk_bulk_data clocks[3];
+ unsigned int nresets;
unsigned int nclocks;
void __iomem *regs;
struct work_struct probe_work;
+ const struct hda_tegra_soc *soc;
};
#ifdef CONFIG_PM
@@ -170,7 +176,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
int rc;
if (!chip->running) {
- rc = reset_control_assert(hda->reset);
+ rc = reset_control_bulk_assert(hda->nresets, hda->resets);
if (rc)
return rc;
}
@@ -187,7 +193,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
} else {
usleep_range(10, 100);
- rc = reset_control_deassert(hda->reset);
+ rc = reset_control_bulk_deassert(hda->nresets, hda->resets);
if (rc)
return rc;
}
@@ -427,9 +433,17 @@ static int hda_tegra_create(struct snd_card *card,
return 0;
}
+static const struct hda_tegra_soc tegra30_data = {
+ .has_hda2codec_2x_reset = true,
+};
+
+static const struct hda_tegra_soc tegra194_data = {
+ .has_hda2codec_2x_reset = false,
+};
+
static const struct of_device_id hda_tegra_match[] = {
- { .compatible = "nvidia,tegra30-hda" },
- { .compatible = "nvidia,tegra194-hda" },
+ { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
+ { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
{},
};
MODULE_DEVICE_TABLE(of, hda_tegra_match);
@@ -449,6 +463,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
hda->dev = &pdev->dev;
chip = &hda->chip;
+ hda->soc = of_device_get_match_data(&pdev->dev);
+
err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
if (err < 0) {
@@ -456,11 +472,20 @@ static int hda_tegra_probe(struct platform_device *pdev)
return err;
}
- hda->reset = devm_reset_control_array_get_exclusive(&pdev->dev);
- if (IS_ERR(hda->reset)) {
- err = PTR_ERR(hda->reset);
+ hda->resets[hda->nresets++].id = "hda";
+ hda->resets[hda->nresets++].id = "hda2hdmi";
+ /*
+ * "hda2codec_2x" reset is not present on Tegra194. Though DT would
+ * be updated to reflect this, but to have backward compatibility
+ * below is necessary.
+ */
+ if (hda->soc->has_hda2codec_2x_reset)
+ hda->resets[hda->nresets++].id = "hda2codec_2x";
+
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets,
+ hda->resets);
+ if (err)
goto out_free;
- }
hda->clocks[hda->nclocks++].id = "hda";
hda->clocks[hda->nclocks++].id = "hda2hdmi";
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 0fb0a428428b..df0b4522babf 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -252,6 +252,7 @@ struct sub_codec cs8409_cs42l42_codec = {
.init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
+ .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -443,6 +444,7 @@ struct sub_codec dolphin_cs42l42_0 = {
.init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
+ .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -456,6 +458,7 @@ struct sub_codec dolphin_cs42l42_1 = {
.init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
+ .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 1,
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 039b9f2f8e94..aff2b5abb81e 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -628,15 +628,17 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
cs8409_i2c_write(cs42l42, 0x1120, 0x80);
- /* Wait ~100us*/
- usleep_range(100, 200);
+ /* Wait ~20ms*/
+ usleep_range(20000, 25000);
cs8409_i2c_write(cs42l42, 0x111f, 0x77);
cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
}
static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
{
- int status_changed = 0;
+ int status_changed = cs42l42->force_status_change;
+
+ cs42l42->force_status_change = 0;
/* TIP_SENSE INSERT/REMOVE */
switch (reg_ts_status) {
@@ -791,6 +793,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
cs42l42->last_page = 0;
cs42l42->hp_jack_in = 0;
cs42l42->mic_jack_in = 0;
+ cs42l42->force_status_change = 1;
/* Put CS42L42 into Reset */
gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index ade2b838590c..d0b725c7285b 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -305,6 +305,7 @@ struct sub_codec {
unsigned int hp_jack_in:1;
unsigned int mic_jack_in:1;
+ unsigned int force_status_change:1;
unsigned int suspended:1;
unsigned int paged:1;
unsigned int last_page;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ffcde7409d2a..92df4f243ec6 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1535,7 +1535,7 @@ static void update_eld(struct hda_codec *codec,
}
}
- if (!eld->eld_valid || eld->eld_size <= 0) {
+ if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) {
eld->eld_valid = false;
eld->eld_size = 0;
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 28255e752c4a..eef973661b0a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -25,6 +25,7 @@
#include "hda_auto_parser.h"
#include "hda_jack.h"
#include "hda_generic.h"
+#include "hda_component.h"
/* keep halting ALC5505 DSP, for power saving */
#define HALT_REALTEK_ALC5505
@@ -126,6 +127,10 @@ struct alc_spec {
unsigned int coef0;
struct input_dev *kb_dev;
u8 alc_mute_keycode_map[1];
+
+ /* component binding */
+ struct component_match *match;
+ struct hda_component comps[HDA_MAX_COMPONENTS];
};
/*
@@ -1924,6 +1929,7 @@ enum {
ALC887_FIXUP_ASUS_BASS,
ALC887_FIXUP_BASS_CHMAP,
ALC1220_FIXUP_GB_DUAL_CODECS,
+ ALC1220_FIXUP_GB_X570,
ALC1220_FIXUP_CLEVO_P950,
ALC1220_FIXUP_CLEVO_PB51ED,
ALC1220_FIXUP_CLEVO_PB51ED_PINS,
@@ -2113,6 +2119,29 @@ static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
}
}
+static void alc1220_fixup_gb_x570(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const hda_nid_t conn1[] = { 0x0c };
+ static const struct coef_fw gb_x570_coefs[] = {
+ WRITE_COEF(0x1a, 0x01c1),
+ WRITE_COEF(0x1b, 0x0202),
+ WRITE_COEF(0x43, 0x3005),
+ {}
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, gb_x570_coefs);
+ break;
+ }
+}
+
static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
@@ -2415,6 +2444,10 @@ static const struct hda_fixup alc882_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc1220_fixup_gb_dual_codecs,
},
+ [ALC1220_FIXUP_GB_X570] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_x570,
+ },
[ALC1220_FIXUP_CLEVO_P950] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc1220_fixup_clevo_p950,
@@ -2517,7 +2550,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
- SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570),
SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950),
SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
@@ -6497,6 +6530,133 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
}
}
+static int comp_match_dev_name(struct device *dev, void *data)
+{
+ return strcmp(dev_name(dev), data) == 0;
+}
+
+static int find_comp_by_dev_name(struct alc_spec *spec, const char *name)
+{
+ int i;
+
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
+ if (strcmp(spec->comps[i].name, name) == 0)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static int comp_bind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ return component_bind_all(dev, spec->comps);
+}
+
+static void comp_unbind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ component_unbind_all(dev, spec->comps);
+}
+
+static const struct component_master_ops comp_master_ops = {
+ .bind = comp_bind,
+ .unbind = comp_unbind,
+};
+
+static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct alc_spec *spec = cdc->spec;
+ int i;
+
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
+ if (spec->comps[i].dev)
+ spec->comps[i].playback_hook(spec->comps[i].dev, action);
+ }
+}
+
+static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+ const char *hid, int count)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct alc_spec *spec = cdc->spec;
+ char *name;
+ int ret, i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ for (i = 0; i < count; i++) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s-%s:00-cs35l41-hda.%d", bus, hid, i);
+ if (!name)
+ return;
+ component_match_add(dev, &spec->match, comp_match_dev_name, name);
+ }
+ ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
+ if (ret)
+ codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+ else
+ spec->gen.pcm_playback_hook = comp_generic_playback_hook;
+ break;
+ }
+}
+
+static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
+}
+
+static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct alc_spec *spec = cdc->spec;
+ unsigned int rx_slot;
+ int i;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ rx_slot = 0;
+ i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0");
+ if (i >= 0)
+ spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
+
+ rx_slot = 1;
+ i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1");
+ if (i >= 0)
+ spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
+ break;
+ }
+
+ comp_generic_playback_hook(hinfo, cdc, sub, action);
+}
+
+static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct alc_spec *spec = cdc->spec;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ component_match_add(dev, &spec->match, comp_match_dev_name,
+ "i2c-CLSA0100:00-cs35l41-hda.0");
+ component_match_add(dev, &spec->match, comp_match_dev_name,
+ "i2c-CLSA0100:00-cs35l41-hda.1");
+ ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
+ if (ret)
+ codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+ else
+ spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook;
+ break;
+ }
+}
+
/* for alc295_fixup_hp_top_speakers */
#include "hp_x360_helper.c"
@@ -6784,6 +6944,10 @@ enum {
ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
ALC233_FIXUP_NO_AUDIO_JACK,
ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
+ ALC285_FIXUP_LEGION_Y9000X_SPEAKERS,
+ ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ ALC287_FIXUP_LEGION_16ACHG6,
+ ALC287_FIXUP_CS35L41_I2C_2,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -8380,6 +8544,18 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
},
+ [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ },
+ [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_15imhg05_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
[ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = {
.type = HDA_FIXUP_VERBS,
//.v.verbs = legion_15imhg05_coefs,
@@ -8514,6 +8690,14 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
+ [ALC287_FIXUP_LEGION_16ACHG6] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_16achg6_speakers,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8730,6 +8914,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
@@ -8910,6 +9095,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -8921,13 +9109,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME),
+ SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
+ SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP),
- SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6),
+ SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
- SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index fb8895af0363..a047ed0f84e9 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -23,8 +23,6 @@
#define MSG_DESCRIPTOR_SIZE 0x24
#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)
-#define MSG_DEFAULT_SIZE 512
-
#define MSG_TYPE_MASK 0x00000003 /* mask for following types */
#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */
#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */
@@ -444,6 +442,9 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
struct mixart_timer_notify *notify;
notify = (struct mixart_timer_notify *)mixart_msg_data;
+ BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data));
+ if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams)))
+ break;
for(i=0; i<notify->stream_count; i++) {
u32 buffer_id = notify->streams[i].buffer_id;
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
index fbf4731a276d..2f0e29ed5d63 100644
--- a/sound/pci/mixart/mixart_core.h
+++ b/sound/pci/mixart/mixart_core.h
@@ -49,6 +49,7 @@ enum mixart_message_id {
MSG_CLOCK_SET_PROPERTIES = 0x200002,
};
+#define MSG_DEFAULT_SIZE 512
struct mixart_msg
{
@@ -251,10 +252,17 @@ struct mixart_sample_pos
u32 sample_pos_low_part;
} __attribute__((packed));
+/*
+ * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of
+ * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams,
+ * this is capped to have a total size below MSG_DEFAULT_SIZE.
+ */
+#define MIXART_MAX_TIMER_NOTIFY_STREAMS \
+ ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos))
struct mixart_timer_notify
{
u32 stream_count;
- struct mixart_sample_pos streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
+ struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS];
} __attribute__((packed));
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index 0f4bce1c0d4f..bf289783eafd 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -99,7 +99,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
return -1;
switch (code) {
- case SND_BELL: if (hz) hz = 1000;
+ case SND_BELL: if (hz) hz = 1000; break;
case SND_TONE: break;
default: return -1;
}
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 2c6af3f8f296..7a9e45094f37 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -68,7 +68,7 @@ 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
+ depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
help
This option enables machine driver for Vangogh platform
using NAU8821 and CS35L41 codecs.
@@ -96,4 +96,11 @@ config SND_SOC_AMD_YC_MACH
Say m if you have such a device.
If unsure select "N".
+config SND_AMD_ACP_CONFIG
+ tristate "AMD ACP configuration selection"
+ select SND_SOC_ACPI if ACPI
+ help
+ This option adds an auto detection to determine which ACP
+ driver modules to use
+
source "sound/soc/amd/acp/Kconfig"
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index f1d42bbda709..4b1f77930a4a 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o
snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o
snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o
+snd-acp-config-objs := acp-config.o
obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
@@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
new file mode 100644
index 000000000000..c9e1c08364f3
--- /dev/null
+++ b/sound/soc/amd/acp-config.c
@@ -0,0 +1,124 @@
+// 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>
+//
+
+/* ACP machine configuration module */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../sof/amd/acp.h"
+#include "mach-config.h"
+
+static int acp_quirk_data;
+
+static const struct config_entry config_table[] = {
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ },
+ },
+ {}
+ },
+ },
+};
+
+int snd_amd_acp_find_config(struct pci_dev *pci)
+{
+ const struct config_entry *table = config_table;
+ u16 device = pci->device;
+ int i;
+
+ /* Do not enable FLAGS on older platforms with Rev id zero */
+ if (!pci->revision)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ acp_quirk_data = table->flags;
+ return table->flags;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_amd_acp_find_config);
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-dsp",
+ .pdata = (void *)&acp_quirk_data,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-acp.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index b2065f3fe42c..3bf86c2424ae 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -522,7 +522,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "amd-da7219-play",
.stream_name = "Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = cz_da7219_init,
.dpcm_playback = 1,
.stop_dma_first = 1,
@@ -533,7 +533,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "amd-da7219-cap",
.stream_name = "Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_da7219_cap_ops,
@@ -543,7 +543,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "amd-max98357-play",
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
.stop_dma_first = 1,
.ops = &cz_max_play_ops,
@@ -554,7 +554,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "dmic0",
.stream_name = "DMIC0 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_dmic0_cap_ops,
@@ -565,7 +565,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "dmic1",
.stream_name = "DMIC1 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_dmic1_cap_ops,
@@ -578,7 +578,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.name = "amd-rt5682-play",
.stream_name = "Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = cz_rt5682_init,
.dpcm_playback = 1,
.stop_dma_first = 1,
@@ -589,7 +589,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.name = "amd-rt5682-cap",
.stream_name = "Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_rt5682_cap_ops,
@@ -599,7 +599,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.name = "amd-max98357-play",
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
.stop_dma_first = 1,
.ops = &cz_rt5682_max_play_ops,
@@ -610,7 +610,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.name = "dmic0",
.stream_name = "DMIC0 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_rt5682_dmic0_cap_ops,
@@ -621,7 +621,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.name = "dmic1",
.stream_name = "DMIC1 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.stop_dma_first = 1,
.ops = &cz_rt5682_dmic1_cap_ops,
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 1f322accd9ea..8fa2e2fde4f1 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -1003,6 +1003,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (!rtd)
return -EINVAL;
@@ -1023,7 +1024,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
}
if (bytescount > 0) {
delay = do_div(bytescount, period_bytes);
- runtime->delay = bytes_to_frames(runtime, delay);
+ adata->delay += bytes_to_frames(runtime, delay);
}
} else {
buffersize = frames_to_bytes(runtime, runtime->buffer_size);
@@ -1035,6 +1036,17 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
return bytes_to_frames(runtime, pos);
}
+static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ snd_pcm_sframes_t delay = adata->delay;
+
+ adata->delay = 0;
+
+ return delay;
+}
+
static int acp_dma_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -1198,6 +1210,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = {
.hw_params = acp_dma_hw_params,
.trigger = acp_dma_trigger,
.pointer = acp_dma_pointer,
+ .delay = acp_dma_delay,
.prepare = acp_dma_prepare,
.pcm_construct = acp_dma_new,
};
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index 6d5c547a32de..a79a46646d50 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -111,7 +111,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = {
.name = "amd-rt5645-play",
.stream_name = "RT5645_AIF1",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = cz_init,
.ops = &cz_aif1_ops,
SND_SOC_DAILINK_REG(designware1, codec, platform),
@@ -120,7 +120,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = {
.name = "amd-rt5645-cap",
.stream_name = "RT5645_AIF1",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &cz_aif1_ops,
SND_SOC_DAILINK_REG(designware2, codec, platform),
},
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index 85529ed7e5f5..db80a73aa593 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -151,6 +151,7 @@ struct audio_drv_data {
struct snd_pcm_substream *capture_i2sbt_stream;
void __iomem *acp_mmio;
u32 asic_type;
+ snd_pcm_sframes_t delay;
};
/*
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 52a1371f9e61..d5838df3064b 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -32,7 +32,7 @@ config SND_AMD_ASOC_RENOIR
config SND_SOC_AMD_MACH_COMMON
tristate
- depends on X86 && PCI && I2C
+ depends on X86 && PCI && I2C && GPIOLIB
select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
@@ -44,17 +44,15 @@ config SND_SOC_AMD_MACH_COMMON
config SND_SOC_AMD_LEGACY_MACH
tristate "AMD Legacy Machine Driver Support"
- depends on X86 && PCI && I2C
+ depends on X86 && PCI && I2C && GPIOLIB
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
+ depends on X86 && PCI && I2C && GPIOLIB
select SND_SOC_AMD_MACH_COMMON
- depends on X86 && PCI && I2C
help
This option enables SOF sound card support for ACP audio.
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index de0f8024e2fb..0ad1cf41b308 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -27,6 +27,7 @@ static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = NONE,
+ .gpio_spkr_en = EN_SPKR_GPIO_GB,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -41,15 +42,16 @@ static const struct snd_kcontrol_new acp_controls[] = {
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),
+ SND_SOC_DAPM_SPK("Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
};
static int acp_asoc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
+ unsigned int spkr_gpio;
int ret;
if (!pdev->id_entry)
@@ -67,9 +69,20 @@ static int acp_asoc_probe(struct platform_device *pdev)
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en;
acp_legacy_dai_links_create(card);
+ if (gpio_is_valid(spkr_gpio)) {
+ ret = devm_gpio_request(dev, spkr_gpio, "spkren");
+ if (ret) {
+ dev_err(dev, "(%s) gpio request failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ gpio_direction_output(spkr_gpio, 0);
+ }
+
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index 7785f12aa006..c9caade5cb74 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -71,6 +71,31 @@ static const struct snd_soc_dapm_route rt5682_map[] = {
{ "IN1P", NULL, "Headset Mic" },
};
+int event_spkr_handler(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 acp_card_drvdata *drvdata = snd_soc_card_get_drvdata(card);
+
+ if (!gpio_is_valid(drvdata->gpio_spkr_en))
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ gpio_set_value(drvdata->gpio_spkr_en, 1);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ gpio_set_value(drvdata->gpio_spkr_en, 0);
+ break;
+ default:
+ dev_warn(card->dev, "%s invalid setting\n", __func__);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(event_spkr_handler, SND_SOC_AMD_MACH);
+
/* Define card ops for RT5682 CODEC */
static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -268,8 +293,8 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = {
/* 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")));
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
+ COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
static const struct snd_soc_dapm_route rt1019_map_lr[] = {
{ "Left Spk", NULL, "Left SPO" },
@@ -278,11 +303,11 @@ static const struct snd_soc_dapm_route rt1019_map_lr[] = {
static struct snd_soc_codec_conf rt1019_conf[] = {
{
- .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
.name_prefix = "Left",
},
{
- .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"),
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
.name_prefix = "Right",
},
};
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index 5dc47cfbff10..fd6299844ebe 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -17,6 +17,12 @@
#include <linux/input.h>
#include <linux/module.h>
#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+
+#define EN_SPKR_GPIO_GB 0x11F
+#define EN_SPKR_GPIO_NK 0x146
+#define EN_SPKR_GPIO_NONE -EINVAL
enum be_id {
HEADSET_BE_ID = 0,
@@ -49,9 +55,11 @@ struct acp_card_drvdata {
unsigned int dai_fmt;
struct clk *wclk;
struct clk *bclk;
+ unsigned int gpio_spkr_en;
};
int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
int acp_legacy_dai_links_create(struct snd_soc_card *card);
-
+int event_spkr_handler(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event);
#endif
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index 854eb7214cea..07de46142655 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -27,6 +27,7 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .gpio_spkr_en = EN_SPKR_GPIO_GB,
};
static struct acp_card_drvdata sof_rt5682_max_data = {
@@ -36,6 +37,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
.hs_codec_id = RT5682,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .gpio_spkr_en = EN_SPKR_GPIO_NK,
};
static struct acp_card_drvdata sof_rt5682s_max_data = {
@@ -45,6 +47,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .gpio_spkr_en = EN_SPKR_GPIO_NK,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -58,15 +61,16 @@ static const struct snd_kcontrol_new acp_controls[] = {
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),
+ SND_SOC_DAPM_SPK("Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
};
static int acp_sof_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
+ unsigned int spkr_gpio;
int ret;
if (!pdev->id_entry)
@@ -84,9 +88,20 @@ static int acp_sof_probe(struct platform_device *pdev)
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en;
acp_sofdsp_dai_links_create(card);
+ if (gpio_is_valid(spkr_gpio)) {
+ ret = devm_gpio_request(dev, spkr_gpio, "spkren");
+ if (ret) {
+ dev_err(dev, "(%s) gpio request failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ gpio_direction_output(spkr_gpio, 0);
+ }
+
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
index e561464f7d60..dad70436d063 100644
--- a/sound/soc/amd/acp3x-rt5682-max9836.c
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -51,7 +51,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
/* set rt5682 dai fmt */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
+ | SND_SOC_DAIFMT_CBP_CFP);
if (ret < 0) {
dev_err(rtd->card->dev,
"Failed to set rt5682 dai fmt: %d\n", ret);
@@ -302,7 +302,7 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-5682-play",
.stream_name = "Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = acp3x_5682_init,
.dpcm_playback = 1,
.dpcm_capture = 1,
@@ -313,7 +313,7 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-max98357-play",
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.dpcm_playback = 1,
.ops = &acp3x_max_play_ops,
.cpus = acp3x_bt,
@@ -325,7 +325,7 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-ec-dmic0-capture",
.stream_name = "Capture DMIC0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.dpcm_capture = 1,
.ops = &acp3x_ec_cap0_ops,
SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
new file mode 100644
index 000000000000..feb3756d9ac4
--- /dev/null
+++ b/sound/soc/amd/mach-config.h
@@ -0,0 +1,28 @@
+/* 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_MACH_CONFIG_H
+#define __AMD_MACH_CONFIG_H
+
+#include <sound/soc-acpi.h>
+
+#define FLAG_AMD_SOF BIT(1)
+#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
+
+#define ACP_PCI_DEV_ID 0x15E2
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ const struct dmi_system_id *dmi_table;
+};
+
+#endif
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
index e604f4ea524f..7e66393e4153 100644
--- a/sound/soc/amd/yc/acp6x-pdm-dma.c
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -318,7 +318,7 @@ static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
+static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
.trigger = acp6x_pdm_dai_trigger,
};
diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c
index f9331f7e80fe..627564c18c27 100644
--- a/sound/soc/atmel/mikroe-proto.c
+++ b/sound/soc/atmel/mikroe-proto.c
@@ -144,9 +144,9 @@ static int snd_proto_probe(struct platform_device *pdev)
of_node_put(cpu_np);
ret = snd_soc_register_card(&snd_proto);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "snd_soc_register_card() failed: %d\n", ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
index 1b3a31296c9b..ef537de7719c 100644
--- a/sound/soc/atmel/tse850-pcm5142.c
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -371,35 +371,27 @@ static int tse850_probe(struct platform_device *pdev)
}
tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
- if (IS_ERR(tse850->add)) {
- if (PTR_ERR(tse850->add) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'add' gpio\n");
- return PTR_ERR(tse850->add);
- }
+ if (IS_ERR(tse850->add))
+ return dev_err_probe(dev, PTR_ERR(tse850->add),
+ "failed to get 'add' gpio\n");
tse850->add_cache = 1;
tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
- if (IS_ERR(tse850->loop1)) {
- if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'loop1' gpio\n");
- return PTR_ERR(tse850->loop1);
- }
+ if (IS_ERR(tse850->loop1))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop1),
+ "failed to get 'loop1' gpio\n");
tse850->loop1_cache = 1;
tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
- if (IS_ERR(tse850->loop2)) {
- if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'loop2' gpio\n");
- return PTR_ERR(tse850->loop2);
- }
+ if (IS_ERR(tse850->loop2))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop2),
+ "failed to get 'loop2' gpio\n");
tse850->loop2_cache = 1;
tse850->ana = devm_regulator_get(dev, "axentia,ana");
- if (IS_ERR(tse850->ana)) {
- if (PTR_ERR(tse850->ana) != -EPROBE_DEFER)
- dev_err(dev, "failed to get 'ana' regulator\n");
- return PTR_ERR(tse850->ana);
- }
+ if (IS_ERR(tse850->ana))
+ return dev_err_probe(dev, PTR_ERR(tse850->ana),
+ "failed to get 'ana' regulator\n");
ret = regulator_enable(tse850->ana);
if (ret < 0) {
diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h
index edc328ba53d3..f30556bec89e 100644
--- a/sound/soc/bcm/bcm63xx-i2s.h
+++ b/sound/soc/bcm/bcm63xx-i2s.h
@@ -74,7 +74,6 @@
struct bcm_i2s_priv {
struct device *dev;
- struct resource *r_irq;
struct regmap *regmap_i2s;
struct clk *i2s_clk;
struct snd_pcm_substream *play_substream;
diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c
index b5096f64c576..2c600b017524 100644
--- a/sound/soc/bcm/bcm63xx-pcm-whistler.c
+++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c
@@ -6,6 +6,7 @@
#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
@@ -387,14 +388,12 @@ int bcm63xx_soc_platform_probe(struct platform_device *pdev,
{
int ret;
- i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!i2s_priv->r_irq) {
- dev_err(&pdev->dev, "Unable to get register irq resource.\n");
- return -ENODEV;
- }
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
- ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr,
- i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv);
+ ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
+ irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
if (ret) {
dev_err(&pdev->dev,
"i2s_init: failed to request interrupt.ret=%d\n", ret);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 326f2d611ad4..d3e5ae8310ef 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ADS117X
imply SND_SOC_AK4104
imply SND_SOC_AK4118
+ imply SND_SOC_AK4375
imply SND_SOC_AK4458
imply SND_SOC_AK4535
imply SND_SOC_AK4554
@@ -220,6 +221,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TDA7419
imply SND_SOC_TFA9879
imply SND_SOC_TFA989X
+ imply SND_SOC_TLV320ADC3XXX
imply SND_SOC_TLV320ADCX140
imply SND_SOC_TLV320AIC23_I2C
imply SND_SOC_TLV320AIC23_SPI
@@ -241,8 +243,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_UDA1380
imply SND_SOC_WCD9335
imply SND_SOC_WCD934X
- imply SND_SOC_WCD937X
- imply SND_SOC_WCD938X
+ imply SND_SOC_WCD938X_SDW
imply SND_SOC_LPASS_RX_MACRO
imply SND_SOC_LPASS_TX_MACRO
imply SND_SOC_WL1273
@@ -343,11 +344,15 @@ config SND_SOC_WM_ADSP
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM2200=y
+ default y if SND_SOC_CS35L41_SPI=y
+ default y if SND_SOC_CS35L41_I2C=y
default m if SND_SOC_MADERA=m
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM2200=m
+ default m if SND_SOC_CS35L41_SPI=m
+ default m if SND_SOC_CS35L41_I2C=m
config SND_SOC_AB8500_CODEC
tristate
@@ -519,6 +524,16 @@ config SND_SOC_AK4118
depends on I2C
select REGMAP_I2C
+config SND_SOC_AK4375
+ tristate "AKM AK4375 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the Asahi-Kasei AK4375 codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-ak4375.
+
config SND_SOC_AK4458
tristate "AKM AK4458 CODEC"
depends on I2C
@@ -609,14 +624,24 @@ config SND_SOC_CS35L36
tristate "Cirrus Logic CS35L36 CODEC"
depends on I2C
+config SND_SOC_CS35L41_LIB
+ tristate
+
+config SND_SOC_CS35L41
+ tristate
+
config SND_SOC_CS35L41_SPI
tristate "Cirrus Logic CS35L41 CODEC (SPI)"
depends on SPI_MASTER
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
select REGMAP_SPI
config SND_SOC_CS35L41_I2C
tristate "Cirrus Logic CS35L41 CODEC (I2C)"
depends on I2C
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
select REGMAP_I2C
config SND_SOC_CS42L42
@@ -1486,6 +1511,13 @@ config SND_SOC_TFA989X
Note that the driver currently bypasses the built-in "CoolFlux DSP"
and does not support (hardware) volume control.
+config SND_SOC_TLV320ADC3XXX
+ tristate "Texas Instruments TLV320ADC3001/3101 audio ADC"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101
+ ADCs.
+
config SND_SOC_TLV320AIC23
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 9acfbcbfc46d..ac7f20972470 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -34,6 +34,7 @@ snd-soc-adav803-objs := adav803.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4118-objs := ak4118.o
+snd-soc-ak4375-objs := ak4375.o
snd-soc-ak4458-objs := ak4458.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4554-objs := ak4554.o
@@ -54,8 +55,10 @@ 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-cs35l41-lib-objs := cs35l41-lib.o
+snd-soc-cs35l41-objs := cs35l41.o
+snd-soc-cs35l41-spi-objs := cs35l41-spi.o
+snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -238,6 +241,7 @@ snd-soc-tda7419-objs := tda7419.o
snd-soc-tas2770-objs := tas2770.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tfa989x-objs := tfa989x.o
+snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
@@ -369,6 +373,7 @@ obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o
+obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o
obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
@@ -391,6 +396,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) += snd-soc-cs35l41.o
+obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.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
@@ -572,6 +579,7 @@ obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o
+obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index c5bf461c0b7e..dba9af753188 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -13,8 +13,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -106,8 +106,8 @@ static const char * const supply_names[] = {
};
struct adau1701 {
- int gpio_nreset;
- int gpio_pll_mode[2];
+ struct gpio_desc *gpio_nreset;
+ struct gpio_descs *gpio_pll_mode;
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
@@ -303,39 +303,41 @@ static int adau1701_reset(struct snd_soc_component *component, unsigned int clkd
struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
int ret;
+ DECLARE_BITMAP(values, 2);
sigmadsp_reset(adau1701->sigmadsp);
- if (clkdiv != ADAU1707_CLKDIV_UNSET &&
- gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
- gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) {
switch (clkdiv) {
case 64:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 0);
break;
case 256:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 1);
break;
case 384:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 0);
break;
- case 0: /* fallback */
+ case 0: /* fallback */
case 512:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 1);
break;
}
+ gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs,
+ adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info,
+ values);
}
adau1701->pll_clkdiv = clkdiv;
- if (gpio_is_valid(adau1701->gpio_nreset)) {
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset) {
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
- gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
@@ -719,8 +721,8 @@ static void adau1701_remove(struct snd_soc_component *component)
{
struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(adau1701->gpio_nreset))
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset)
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
}
@@ -788,8 +790,6 @@ static int adau1701_i2c_probe(struct i2c_client *client,
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
- int gpio_nreset = -EINVAL;
- int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret, i;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
@@ -823,26 +823,6 @@ static int adau1701_i2c_probe(struct i2c_client *client,
if (dev->of_node) {
- gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
- if (gpio_nreset < 0 && gpio_nreset != -ENOENT) {
- ret = gpio_nreset;
- goto exit_regulators_disable;
- }
-
- gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 0);
- if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) {
- ret = gpio_pll_mode[0];
- goto exit_regulators_disable;
- }
-
- gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 1);
- if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) {
- ret = gpio_pll_mode[1];
- goto exit_regulators_disable;
- }
-
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
@@ -851,32 +831,20 @@ static int adau1701_i2c_probe(struct i2c_client *client,
ARRAY_SIZE(adau1701->pin_config));
}
- if (gpio_is_valid(gpio_nreset)) {
- ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
- "ADAU1701 Reset");
- if (ret < 0)
- goto exit_regulators_disable;
+ adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN);
+
+ if (IS_ERR(adau1701->gpio_nreset)) {
+ ret = PTR_ERR(adau1701->gpio_nreset);
+ goto exit_regulators_disable;
}
- if (gpio_is_valid(gpio_pll_mode[0]) &&
- gpio_is_valid(gpio_pll_mode[1])) {
- ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 0");
- if (ret < 0)
- goto exit_regulators_disable;
+ adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW);
- ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 1");
- if (ret < 0)
- goto exit_regulators_disable;
+ if (IS_ERR(adau1701->gpio_pll_mode)) {
+ ret = PTR_ERR(adau1701->gpio_pll_mode);
+ goto exit_regulators_disable;
}
- adau1701->gpio_nreset = gpio_nreset;
- adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
- adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
-
i2c_set_clientdata(client, adau1701);
adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
index e0a6451851e8..2e6bafd2a821 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -374,20 +374,14 @@ static int ak4118_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4118);
ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(ak4118->reset)) {
- ret = PTR_ERR(ak4118->reset);
- if (ret != -EPROBE_DEFER)
- dev_err(&i2c->dev, "Failed to get reset: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(ak4118->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset),
+ "Failed to get reset\n");
ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN);
- if (IS_ERR(ak4118->irq)) {
- ret = PTR_ERR(ak4118->irq);
- if (ret != -EPROBE_DEFER)
- dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(ak4118->irq))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq),
+ "Failed to get IRQ\n");
ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq),
NULL, ak4118_irq_handler,
diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c
new file mode 100644
index 000000000000..9a7b662016b9
--- /dev/null
+++ b/sound/soc/codecs/ak4375.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Based on code by Hu Jin
+ * Copyright (C) 2014 Asahi Kasei Microdevices Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Registers and fields */
+#define AK4375_00_POWER_MANAGEMENT1 0x00
+#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */
+#define AK4375_01_POWER_MANAGEMENT2 0x01
+#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */
+#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */
+#define PMLDO1P BIT(4)
+#define PMLDO1N BIT(5)
+#define PMLDO (PMLDO1P | PMLDO1N)
+#define AK4375_02_POWER_MANAGEMENT3 0x02
+#define AK4375_03_POWER_MANAGEMENT4 0x03
+#define AK4375_04_OUTPUT_MODE_SETTING 0x04
+#define AK4375_05_CLOCK_MODE_SELECT 0x05
+#define FS_MASK GENMASK(4, 0)
+#define FS_8KHZ 0x00
+#define FS_11_025KHZ 0x01
+#define FS_16KHZ 0x04
+#define FS_22_05KHZ 0x05
+#define FS_32KHZ 0x08
+#define FS_44_1KHZ 0x09
+#define FS_48KHZ 0x0a
+#define FS_88_2KHZ 0x0d
+#define FS_96KHZ 0x0e
+#define FS_176_4KHZ 0x11
+#define FS_192KHZ 0x12
+#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */
+#define CM_0 (0x0 << 5)
+#define CM_1 (0x1 << 5)
+#define CM_2 (0x2 << 5)
+#define CM_3 (0x3 << 5)
+#define AK4375_06_DIGITAL_FILTER_SELECT 0x06
+#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */
+#define DASL BIT(6)
+#define DASD BIT(7)
+#define AK4375_07_DAC_MONO_MIXING 0x07
+#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */
+#define AK4375_08_JITTER_CLEANER_SETTING1 0x08
+#define AK4375_09_JITTER_CLEANER_SETTING2 0x09
+#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a
+#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */
+#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */
+#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */
+#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b
+#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c
+#define AK4375_0D_HP_VOLUME_CONTROL 0x0d
+#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e
+#define PLS BIT(0) /* 0: MCKI, 1: BCLK */
+#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */
+#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */
+#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */
+#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */
+#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */
+#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */
+#define DIV BIT(4)
+#define AK4375_14_DAC_CLK_DIVIDER 0x14
+#define AK4375_15_AUDIO_IF_FORMAT 0x15
+#define DEVICEID_MASK GENMASK(7, 5)
+#define AK4375_24_MODE_CONTROL 0x24
+
+#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */
+#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */
+
+#define DEVICEID_AK4375 0x00
+#define DEVICEID_AK4375A 0x01
+#define DEVICEID_AK4376A 0x02
+#define DEVICEID_AK4377 0x03
+#define DEVICEID_AK4331 0x07
+
+static const char * const supply_names[] = {
+ "avdd", "tvdd"
+};
+
+struct ak4375_drvdata {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct snd_soc_component_driver *comp_drv;
+};
+
+struct ak4375_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pdn_gpiod;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ unsigned int rate;
+ unsigned int pld;
+ u8 mute_save;
+};
+
+static const struct reg_default ak4375_reg_defaults[] = {
+ { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 },
+ { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 },
+ { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 },
+ { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 },
+ { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 },
+ { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 },
+ { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 },
+ { 0x15, 0x00 }, { 0x24, 0x00 },
+};
+
+/*
+ * Output Digital volume control:
+ * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0);
+
+/*
+ * HP-Amp Analog volume control:
+ * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB)
+ */
+static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0);
+
+static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" };
+static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" };
+static const char * const ak4375_cpmode_select_texts[] = {
+ "Automatic Switching",
+ "+-VDD Operation",
+ "+-1/2VDD Operation"
+};
+
+/*
+ * DASD, DASL bits Digital Filter Setting
+ * 0, 0 : Sharp Roll-Off Filter
+ * 0, 1 : Slow Roll-Off Filter
+ * 1, 0 : Short delay Sharp Roll-Off Filter
+ * 1, 1 : Short delay Slow Roll-Off Filter
+ */
+static const char * const ak4375_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+};
+
+static const struct soc_enum ak4375_ovolcn_enum =
+ SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7,
+ ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts);
+static const struct soc_enum ak4375_mdacl_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_mdacr_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_cpmode_enum =
+ SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2,
+ ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts);
+static const struct soc_enum ak4375_digfil_enum =
+ SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6,
+ ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts);
+
+static const struct snd_kcontrol_new ak4375_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME,
+ AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv),
+ SOC_SINGLE_TLV("HP-Amp Analog Volume",
+ AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv),
+
+ SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0),
+
+ SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum),
+ SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum),
+ SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum),
+ SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum),
+ SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum),
+};
+
+static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0),
+ SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0),
+ SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0),
+};
+
+static int ak4375_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1);
+ usleep_range(6500, 7000);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO);
+ usleep_range(1000, 2000);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2);
+ usleep_range(4500, 5000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0);
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0,
+ &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0,
+ &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route ak4375_intercon[] = {
+ { "DAC", NULL, "SDTI" },
+
+ { "HPL Mixer", "LDACL Switch", "DAC" },
+ { "HPL Mixer", "RDACL Switch", "DAC" },
+ { "HPR Mixer", "LDACR Switch", "DAC" },
+ { "HPR Mixer", "RDACR Switch", "DAC" },
+
+ { "HPL", NULL, "HPL Mixer" },
+ { "HPR", NULL, "HPR Mixer" },
+};
+
+static int ak4375_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 ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int freq_in, freq_out;
+
+ ak4375->rate = params_rate(params);
+
+ if (ak4375->rate <= 96000)
+ ak4375->pld = 0;
+ else
+ ak4375->pld = 1;
+
+ freq_in = 32 * ak4375->rate / (ak4375->pld + 1);
+
+ if ((ak4375->rate % 8000) == 0)
+ freq_out = AK4375_PLL_FREQ_OUT_122880000;
+ else
+ freq_out = AK4375_PLL_FREQ_OUT_112896000;
+
+ return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out);
+}
+
+static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int mclk, plm, mdiv, div;
+ u8 cms, fs, cm;
+
+ cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT);
+ fs = cms & ~FS_MASK;
+ cm = cms & ~CM_MASK;
+
+ switch (ak4375->rate) {
+ case 8000:
+ fs |= FS_8KHZ;
+ break;
+ case 11025:
+ fs |= FS_11_025KHZ;
+ break;
+ case 16000:
+ fs |= FS_16KHZ;
+ break;
+ case 22050:
+ fs |= FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= FS_32KHZ;
+ break;
+ case 44100:
+ fs |= FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= FS_48KHZ;
+ break;
+ case 88200:
+ fs |= FS_88_2KHZ;
+ break;
+ case 96000:
+ fs |= FS_96KHZ;
+ break;
+ case 176400:
+ fs |= FS_176_4KHZ;
+ break;
+ case 192000:
+ fs |= FS_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4375->rate <= 24000) {
+ cm |= CM_1;
+ mclk = 512 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else if (ak4375->rate <= 96000) {
+ cm |= CM_0;
+ mclk = 256 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else {
+ cm |= CM_3;
+ mclk = 128 * ak4375->rate;
+ mdiv = 4;
+ div = 1;
+ }
+
+ /* Writing both fields in one go seems to make playback choppy on start */
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs);
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm);
+
+ snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1,
+ (ak4375->pld & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2,
+ ak4375->pld & 0x00ff);
+
+ plm = freq_out / freq_in - 1;
+ snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff);
+
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div);
+
+ /* SRCCKS bit: force to 1 for SRC PLL source clock */
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS);
+
+ snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv);
+
+ dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n",
+ ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div);
+
+ return 0;
+}
+
+static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING);
+
+ dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val);
+
+ if (mute) {
+ ak4375->mute_save = val & DACMUTE_MASK;
+ val &= ~DACMUTE_MASK;
+ } else {
+ val |= ak4375->mute_save;
+ }
+
+ snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val);
+
+ return 0;
+}
+
+#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops ak4375_dai_ops = {
+ .hw_params = ak4375_hw_params,
+ .mute_stream = ak4375_mute,
+ .set_pll = ak4375_dai_set_pll,
+};
+
+static struct snd_soc_dai_driver ak4375_dai = {
+ .name = "ak4375-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4375_RATES,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .formats = AK4375_FORMATS,
+ },
+ .ops = &ak4375_dai_ops,
+};
+
+static void ak4375_power_off(struct ak4375_priv *ak4375)
+{
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0);
+ usleep_range(1000, 2000);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+}
+
+static int ak4375_power_on(struct ak4375_priv *ak4375)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(3000, 4000);
+
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1);
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int __maybe_unused ak4375_runtime_suspend(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4375->regmap, true);
+ ak4375_power_off(ak4375);
+
+ return 0;
+}
+
+static int __maybe_unused ak4375_runtime_resume(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(ak4375->regmap, false);
+ regcache_mark_dirty(ak4375->regmap);
+
+ return regcache_sync(ak4375->regmap);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4375 = {
+ .controls = ak4375_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4375_snd_controls),
+ .dapm_widgets = ak4375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets),
+ .dapm_routes = ak4375_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4375_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct regmap_config ak4375_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AK4375_24_MODE_CONTROL,
+ .reg_defaults = ak4375_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4375_drvdata ak4375_drvdata = {
+ .dai_drv = &ak4375_dai,
+ .comp_drv = &soc_codec_dev_ak4375,
+};
+
+static const struct dev_pm_ops ak4375_pm = {
+ SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static int ak4375_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4375_priv *ak4375;
+ const struct ak4375_drvdata *drvdata;
+ unsigned int deviceid;
+ int ret, i;
+
+ ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL);
+ if (!ak4375)
+ return -ENOMEM;
+
+ ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap);
+ if (IS_ERR(ak4375->regmap))
+ return PTR_ERR(ak4375->regmap);
+
+ i2c_set_clientdata(i2c, ak4375);
+ ak4375->dev = &i2c->dev;
+
+ drvdata = of_device_get_match_data(&i2c->dev);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ ak4375->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW);
+ if (IS_ERR(ak4375->pdn_gpiod))
+ return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod),
+ "failed to get pdn\n");
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ /* Don't read deviceid from cache */
+ regcache_cache_bypass(ak4375->regmap, true);
+
+ ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "unable to read DEVICEID!\n");
+ return ret;
+ }
+
+ regcache_cache_bypass(ak4375->regmap, false);
+
+ deviceid = (deviceid & DEVICEID_MASK) >> 5;
+
+ switch (deviceid) {
+ case DEVICEID_AK4331:
+ dev_err(ak4375->dev, "found untested AK4331\n");
+ return -EINVAL;
+ case DEVICEID_AK4375:
+ dev_dbg(ak4375->dev, "found AK4375\n");
+ break;
+ case DEVICEID_AK4375A:
+ dev_dbg(ak4375->dev, "found AK4375A\n");
+ break;
+ case DEVICEID_AK4376A:
+ dev_err(ak4375->dev, "found unsupported AK4376/A!\n");
+ return -EINVAL;
+ case DEVICEID_AK4377:
+ dev_err(ak4375->dev, "found unsupported AK4377!\n");
+ return -EINVAL;
+ default:
+ dev_err(ak4375->dev, "unrecognized DEVICEID!\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_set_active(ak4375->dev);
+ pm_runtime_enable(ak4375->dev);
+
+ ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv,
+ drvdata->dai_drv, 1);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ak4375_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+
+ return 0;
+}
+
+static const struct of_device_id ak4375_of_match[] = {
+ { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ak4375_of_match);
+
+static struct i2c_driver ak4375_i2c_driver = {
+ .driver = {
+ .name = "ak4375",
+ .pm = &ak4375_pm,
+ .of_match_table = ak4375_of_match,
+ },
+ .probe_new = ak4375_i2c_probe,
+ .remove = ak4375_i2c_remove,
+};
+module_i2c_driver(ak4375_i2c_driver);
+
+MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
+MODULE_DESCRIPTION("ASoC AK4375 DAC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 7a5588f1df01..961a3e07e70f 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -1311,7 +1311,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
- classh_config->classh_algo_enable = classh ? true : false;
+ classh_config->classh_algo_enable = (classh != NULL);
if (classh_config->classh_algo_enable) {
classh_config->classh_bst_override =
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
index d5fa8d2c4a70..faad5c638cb8 100644
--- a/sound/soc/codecs/cs35l41-i2c.c
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -17,27 +17,13 @@
#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 },
+ { "cs35l51", 0 },
+ { "cs35l53", 0 },
{}
};
@@ -100,6 +86,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
static struct i2c_driver cs35l41_i2c_driver = {
.driver = {
.name = "cs35l41",
+ .pm = &cs35l41_pm_ops,
.of_match_table = of_match_ptr(cs35l41_of_match),
.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
},
diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-lib.c
index 964e530afa27..e5a56bcbb223 100644
--- a/sound/soc/codecs/cs35l41-tables.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -1,17 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
//
-// cs35l41-tables.c -- CS35L41 ALSA SoC audio driver
+// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers
//
// Copyright 2017-2021 Cirrus Logic, Inc.
//
// Author: David Rhodes <david.rhodes@cirrus.com>
+// Author: Lucas Tanure <lucas.tanure@cirrus.com>
-#include "cs35l41.h"
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
-const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = {
+#include <sound/cs35l41.h>
+
+static const struct reg_default cs35l41_reg[] = {
{ CS35L41_PWR_CTRL1, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
{ CS35L41_PWR_CTRL3, 0x01000010 },
{ CS35L41_GPIO_PAD_CONTROL, 0x00000000 },
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 },
+ { CS35L41_TST_FS_MON0, 0x00020016 },
+ { CS35L41_BSTCVRT_COEFF, 0x00002424 },
+ { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 },
+ { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A },
{ CS35L41_SP_ENABLES, 0x00000000 },
{ CS35L41_SP_RATE_CTRL, 0x00000028 },
{ CS35L41_SP_FORMAT, 0x18180200 },
@@ -39,15 +52,20 @@ const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = {
{ CS35L41_CLASSH_CFG, 0x000B0405 },
{ CS35L41_WKFET_CFG, 0x00000111 },
{ CS35L41_NG_CFG, 0x00000033 },
- { CS35L41_AMP_GAIN_CTRL, 0x00000273 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_IRQ1_MASK1, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK2, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK3, 0xFFFF87FF },
+ { CS35L41_IRQ1_MASK4, 0xFEFFFFFF },
{ 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 },
+ { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 },
};
-bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
+static bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L41_DEVID:
@@ -72,10 +90,14 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
case CS35L41_PROTECT_REL_ERR_IGN:
case CS35L41_GPIO_PAD_CONTROL:
case CS35L41_JTAG_CONTROL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
case CS35L41_PLL_CLK_CTRL:
case CS35L41_DSP_CLK_CTRL:
case CS35L41_GLOBAL_CLK_CTRL:
case CS35L41_DATA_FS_SEL:
+ case CS35L41_TST_FS_MON0:
case CS35L41_MDSYNC_EN:
case CS35L41_MDSYNC_TX_ID:
case CS35L41_MDSYNC_PWR_CTRL:
@@ -200,6 +222,83 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
case CS35L41_DIE_STS2:
case CS35L41_TEMP_CAL1:
case CS35L41_TEMP_CAL2:
+ case CS35L41_DSP1_TIMESTAMP_COUNT:
+ case CS35L41_DSP1_SYS_ID:
+ case CS35L41_DSP1_SYS_VERSION:
+ case CS35L41_DSP1_SYS_CORE_ID:
+ case CS35L41_DSP1_SYS_AHB_ADDR:
+ case CS35L41_DSP1_SYS_XSRAM_SIZE:
+ case CS35L41_DSP1_SYS_YSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PM_BOOT_SIZE:
+ case CS35L41_DSP1_SYS_FEATURES:
+ case CS35L41_DSP1_SYS_FIR_FILTERS:
+ case CS35L41_DSP1_SYS_LMS_FILTERS:
+ case CS35L41_DSP1_SYS_XM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_YM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_PM_BANK_SIZE:
+ case CS35L41_DSP1_RX1_RATE:
+ case CS35L41_DSP1_RX2_RATE:
+ case CS35L41_DSP1_RX3_RATE:
+ case CS35L41_DSP1_RX4_RATE:
+ case CS35L41_DSP1_RX5_RATE:
+ case CS35L41_DSP1_RX6_RATE:
+ case CS35L41_DSP1_RX7_RATE:
+ case CS35L41_DSP1_RX8_RATE:
+ case CS35L41_DSP1_TX1_RATE:
+ case CS35L41_DSP1_TX2_RATE:
+ case CS35L41_DSP1_TX3_RATE:
+ case CS35L41_DSP1_TX4_RATE:
+ case CS35L41_DSP1_TX5_RATE:
+ case CS35L41_DSP1_TX6_RATE:
+ case CS35L41_DSP1_TX7_RATE:
+ case CS35L41_DSP1_TX8_RATE:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CORE_CTRL:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE:
+ case CS35L41_DSP1_XM_MSTR_EN:
+ case CS35L41_DSP1_XM_CORE_PRI:
+ case CS35L41_DSP1_XM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_XM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_XM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_XM_NPL0_PRI:
+ case CS35L41_DSP1_YM_MSTR_EN:
+ case CS35L41_DSP1_YM_CORE_PRI:
+ case CS35L41_DSP1_YM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_YM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_YM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_YM_NPL0_PRI:
+ case CS35L41_DSP1_MPU_XM_ACCESS0:
+ case CS35L41_DSP1_MPU_YM_ACCESS0:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS0:
+ case CS35L41_DSP1_MPU_XREG_ACCESS0:
+ case CS35L41_DSP1_MPU_YREG_ACCESS0:
+ case CS35L41_DSP1_MPU_XM_ACCESS1:
+ case CS35L41_DSP1_MPU_YM_ACCESS1:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS1:
+ case CS35L41_DSP1_MPU_XREG_ACCESS1:
+ case CS35L41_DSP1_MPU_YREG_ACCESS1:
+ case CS35L41_DSP1_MPU_XM_ACCESS2:
+ case CS35L41_DSP1_MPU_YM_ACCESS2:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS2:
+ case CS35L41_DSP1_MPU_XREG_ACCESS2:
+ case CS35L41_DSP1_MPU_YREG_ACCESS2:
+ case CS35L41_DSP1_MPU_XM_ACCESS3:
+ case CS35L41_DSP1_MPU_YM_ACCESS3:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS3:
+ case CS35L41_DSP1_MPU_XREG_ACCESS3:
+ case CS35L41_DSP1_MPU_YREG_ACCESS3:
+ case CS35L41_DSP1_MPU_XM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_XM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_YM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_YM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_PM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_PM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_LOCK_CONFIG:
+ case CS35L41_DSP1_MPU_WDT_RST_CTRL:
case CS35L41_OTP_TRIM_1:
case CS35L41_OTP_TRIM_2:
case CS35L41_OTP_TRIM_3:
@@ -237,6 +336,13 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
case CS35L41_OTP_TRIM_35:
case CS35L41_OTP_TRIM_36:
case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
/*test regs*/
case CS35L41_PLL_OVR:
case CS35L41_BST_TEST_DUTY:
@@ -247,23 +353,35 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
}
}
-bool cs35l41_precious_reg(struct device *dev, unsigned int reg)
+static bool cs35l41_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_TST_FS_MON0:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
return true;
default:
return false;
}
}
-bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
+static 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_OTPID:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
case CS35L41_DTEMP_EN:
case CS35L41_IRQ1_STATUS:
case CS35L41_IRQ1_STATUS1:
@@ -274,17 +392,6 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
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:
@@ -294,54 +401,20 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
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_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS:
case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
return true;
default:
@@ -555,7 +628,46 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM]
{ 0x00017044, 0, 24 }, /*LOT_NUMBER*/
};
-const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = {
+static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
+ { 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 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb0_errata_patch[] = {
+ { 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 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
+ { 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 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+};
+
+static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
{
.id = 0x01,
.map = otp_map_1,
@@ -592,3 +704,337 @@ const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]
.word_offset = 2,
},
};
+
+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,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c);
+
+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,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_spi);
+
+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;
+}
+
+int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to unlock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock);
+
+int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to lock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_lock);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
+{
+ const struct cs35l41_otp_map_element_t *otp_map_match;
+ const struct cs35l41_otp_packed_element_t *otp_map;
+ int bit_offset, word_offset, ret, i;
+ 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(regmap, CS35L41_OTPID, &otp_id_reg);
+ if (ret) {
+ dev_err(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(dev, "OTP Map matching ID %d not found\n", otp_id_reg);
+ ret = -EINVAL;
+ goto err_otp_unpack;
+ }
+
+ ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS);
+ if (ret) {
+ dev_err(dev, "Read OTP Mem failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map = otp_map_match->map;
+
+ bit_offset = otp_map_match->bit_offset;
+ word_offset = otp_map_match->word_offset;
+
+ for (i = 0; i < otp_map_match->num_elements; i++) {
+ dev_dbg(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(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(dev, "Write OTP val failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ }
+ }
+
+ ret = 0;
+
+err_otp_unpack:
+ kfree(otp_mem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_otp_unpack);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid)
+{
+ char *rev;
+ int ret;
+
+ switch (reg_revid) {
+ case CS35L41_REVID_A0:
+ ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch,
+ ARRAY_SIZE(cs35l41_reva0_errata_patch));
+ rev = "A0";
+ break;
+ case CS35L41_REVID_B0:
+ ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch,
+ ARRAY_SIZE(cs35l41_revb0_errata_patch));
+ rev = "B0";
+ break;
+ case CS35L41_REVID_B2:
+ ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch,
+ ARRAY_SIZE(cs35l41_revb2_errata_patch));
+ rev = "B2";
+ break;
+ default:
+ ret = -EINVAL;
+ rev = "XX";
+ break;
+ }
+
+ if (ret)
+ dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret);
+
+ ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0);
+ if (ret < 0)
+ dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch);
+
+int cs35l41_set_channels(struct device *dev, struct regmap *reg,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ 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(dev, "rx slot %d position = %d\n", i, rx_slot[i]);
+ val |= rx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val);
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < tx_num; i++) {
+ dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]);
+ val |= tx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_channels);
+
+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
+};
+
+
+int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap,
+ int boost_ipk)
+{
+ unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
+ 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;
+}
+EXPORT_SYMBOL_GPL(cs35l41_boost_config);
+
+MODULE_DESCRIPTION("CS35L41 library");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
index 3fa99741779a..6dfd5459aa20 100644
--- a/sound/soc/codecs/cs35l41-spi.c
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -15,28 +15,13 @@
#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 },
+ { "cs35l51", 0 },
+ { "cs35l53", 0 },
{}
};
@@ -99,6 +84,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
static struct spi_driver cs35l41_spi_driver = {
.driver = {
.name = "cs35l41",
+ .pm = &cs35l41_pm_ops,
.of_match_table = of_match_ptr(cs35l41_of_match),
.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
},
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 9c4d481f7614..77a017694645 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -13,8 +13,8 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
-#include <linux/slab.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -151,24 +151,6 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
{ 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;
@@ -197,6 +179,134 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
CS35L41_AMP_DIG_VOL_CTRL, 0,
cs35l41_pcm_sftramp_text);
+static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l41->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l41->dsp.preloaded)
+ return 0;
+
+ if (cs35l41->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
+ enum cs35l41_cspl_mbox_status sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ default:
+ return false;
+ }
+}
+
+static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41,
+ enum cs35l41_cspl_mbox_cmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ // Set mailbox cmd
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) {
+ dev_dbg(cs35l41->dev,
+ "[%u] cmd %u returned invalid sts %u",
+ i, cmd, sts);
+ } else {
+ return 0;
+ }
+ }
+
+ dev_err(cs35l41->dev,
+ "Failed to set mailbox cmd %u (status %u)\n",
+ cmd, sts);
+
+ return -ENOMSG;
+}
+
+static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ unsigned int fw_status;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!cs35l41->dsp.cs_dsp.running)
+ return wm_adsp_event(w, kcontrol, event);
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to read firmware status: %d\n", ret);
+ return ret;
+ }
+
+ switch (fw_status) {
+ case CSPL_MBOX_STS_RUNNING:
+ case CSPL_MBOX_STS_PAUSED:
+ break;
+ default:
+ dev_err(cs35l41->dev, "Firmware status is invalid: %u\n",
+ fw_status);
+ return -EINVAL;
+ }
+
+ return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+}
+
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,
@@ -255,6 +365,24 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum,
static const struct snd_kcontrol_new asp_tx4_mux =
SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum);
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum,
+ CS35L41_DSP1_RX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx1_mux =
+ SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum,
+ CS35L41_DSP1_RX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx2_mux =
+ SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_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),
@@ -282,130 +410,10 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
CS35L41_AMP_INV_PCM_SHIFT, 1, 0),
SOC_SINGLE("Amp Gain ZC", CS35L41_AMP_GAIN_CTRL,
CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 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 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;
- }
-
- 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;
- }
-
- 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;
@@ -414,6 +422,8 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
int ret = IRQ_NONE;
unsigned int i;
+ pm_runtime_get_sync(cs35l41->dev);
+
for (i = 0; i < ARRAY_SIZE(status); i++) {
regmap_read(cs35l41->regmap,
CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE),
@@ -426,7 +436,7 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
/* 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;
+ goto done;
if (status[3] & CS35L41_OTP_BOOT_DONE) {
regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
@@ -531,23 +541,27 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
ret = IRQ_HANDLED;
}
+done:
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
return ret;
}
static const struct reg_sequence cs35l41_pup_patch[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
{ 0x00002084, 0x002F1AA0 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
};
static const struct reg_sequence cs35l41_pdn_patch[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
{ 0x00002084, 0x002F1AA3 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
};
static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
@@ -596,6 +610,14 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
}
static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l41_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l41_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
SND_SOC_DAPM_OUTPUT("SPK"),
SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0),
@@ -611,11 +633,18 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
SND_SOC_DAPM_SIGGEN("VBST"),
SND_SOC_DAPM_SIGGEN("TEMP"),
- 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_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 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,
@@ -626,33 +655,51 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
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("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux),
+ SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_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[] = {
+ {"DSP RX1 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX1 Source", "ASPRX2", "ASPRX2"},
+ {"DSP RX2 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX2 Source", "ASPRX2", "ASPRX2"},
+
+ {"DSP1", NULL, "DSP RX1 Source"},
+ {"DSP1", NULL, "DSP RX2 Source"},
+
{"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", "DSPTX1", "DSP1"},
+ {"ASP TX1 Source", "DSPTX2", "DSP1"},
{"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", "DSPTX1", "DSP1"},
+ {"ASP TX2 Source", "DSPTX2", "DSP1"},
{"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", "DSPTX1", "DSP1"},
+ {"ASP TX3 Source", "DSPTX2", "DSP1"},
{"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", "DSPTX1", "DSP1"},
+ {"ASP TX4 Source", "DSPTX2", "DSP1"},
{"ASP TX4 Source", "ASPRX1", "ASPRX1" },
{"ASP TX4 Source", "ASPRX2", "ASPRX2" },
{"ASPTX1", NULL, "ASP TX1 Source"},
@@ -664,12 +711,27 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
{"AMP Capture", NULL, "ASPTX3"},
{"AMP Capture", NULL, "ASPTX4"},
+ {"DSP1", NULL, "VMON"},
+ {"DSP1", NULL, "IMON"},
+ {"DSP1", NULL, "VPMON"},
+ {"DSP1", NULL, "VBSTMON"},
+ {"DSP1", NULL, "TEMPMON"},
+
+ {"VMON ADC", NULL, "VMON"},
+ {"IMON ADC", NULL, "IMON"},
+ {"VPMON ADC", NULL, "VPMON"},
+ {"VBSTMON ADC", NULL, "VBSTMON"},
+ {"TEMPMON ADC", NULL, "TEMPMON"},
+
{"VMON ADC", NULL, "VSENSE"},
{"IMON ADC", NULL, "ISENSE"},
{"VPMON ADC", NULL, "VP"},
{"VBSTMON ADC", NULL, "VBST"},
{"TEMPMON ADC", NULL, "TEMP"},
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
{"ASPRX1", NULL, "AMP Playback"},
{"ASPRX2", NULL, "AMP Playback"},
{"DRE", "Switch", "CLASS H"},
@@ -678,39 +740,24 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
{"SPK", NULL, "Main AMP"},
{"PCM Source", "ASP", "ASPRX1"},
+ {"PCM Source", "DSP", "DSP1"},
{"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)
+static const struct cs_dsp_region cs35l41_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n,
+ unsigned int *tx_slot, unsigned int rx_n, 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;
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot);
}
static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -946,88 +993,6 @@ static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
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;
@@ -1036,9 +1001,8 @@ static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
/* 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);
+ ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, 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;
@@ -1091,6 +1055,20 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
return irq_pol;
}
+static int cs35l41_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+
+ return wm_adsp2_component_probe(&cs35l41->dsp, component);
+}
+
+static void cs35l41_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l41->dsp, component);
+}
+
static const struct snd_soc_dai_ops cs35l41_ops = {
.startup = cs35l41_pcm_startup,
.set_fmt = cs35l41_set_dai_fmt,
@@ -1124,6 +1102,8 @@ static struct snd_soc_dai_driver cs35l41_dai[] = {
static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
.name = "cs35l41-codec",
+ .probe = cs35l41_component_probe,
+ .remove = cs35l41_component_remove,
.dapm_widgets = cs35l41_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets),
@@ -1185,50 +1165,90 @@ static int cs35l41_handle_pdata(struct device *dev,
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_fs_errata_patch[] = {
+ { CS35L41_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX8_RATE, 0x00000001 },
};
-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 int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
+{
+ struct wm_adsp *dsp;
+ int ret;
-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 },
-};
+ dsp = &cs35l41->dsp;
+ dsp->part = "cs35l41";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+ dsp->cs_dsp.dev = cs35l41->dev;
+ dsp->cs_dsp.regmap = cs35l41->regmap;
+ dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE;
+ dsp->cs_dsp.base_sysinfo = CS35L41_DSP1_SYS_ID;
+ dsp->cs_dsp.mem = cs35l41_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
+ dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+ ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_fs_errata_patch,
+ ARRAY_SIZE(cs35l41_fs_errata_patch));
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Failed to write fs errata: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm_halo_init(dsp);
+ if (ret) {
+ dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC,
+ CS35L41_INPUT_SRC_VPMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC,
+ CS35L41_INPUT_SRC_CLASSH);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC,
+ CS35L41_INPUT_SRC_TEMPMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC,
+ CS35L41_INPUT_SRC_RSVD);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret);
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(dsp);
+
+ return ret;
+}
int cs35l41_probe(struct cs35l41_private *cs35l41,
struct cs35l41_platform_data *pdata)
@@ -1325,39 +1345,20 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
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;
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+ goto err;
}
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+
irq_pol = cs35l41_irq_gpio_config(cs35l41);
/* Set interrupt masks for critical errors */
@@ -1367,71 +1368,229 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
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);
+ ret = cs35l41_set_pdata(cs35l41);
if (ret < 0) {
- dev_err(cs35l41->dev, "Write CS35L41_PWR_CTRL2 failed: %d\n", ret);
+ dev_err(cs35l41->dev, "Set pdata 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);
+ ret = cs35l41_dsp_init(cs35l41);
+ if (ret < 0)
goto err;
- }
- ret = cs35l41_set_pdata(cs35l41);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret);
- goto err;
- }
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l41->dev);
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_set_active(cs35l41->dev);
+ pm_runtime_get_noresume(cs35l41->dev);
+ pm_runtime_enable(cs35l41->dev);
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;
+ goto err_pm;
}
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
regid, reg_revid);
return 0;
+err_pm:
+ pm_runtime_disable(cs35l41->dev);
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ wm_adsp2_remove(&cs35l41->dsp);
err:
regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
return ret;
}
+EXPORT_SYMBOL_GPL(cs35l41_probe);
void cs35l41_remove(struct cs35l41_private *cs35l41)
{
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ wm_adsp2_remove(&cs35l41->dsp);
+
+ pm_runtime_put_noidle(cs35l41->dev);
+
regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
}
+EXPORT_SYMBOL_GPL(cs35l41_remove);
+
+static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Runtime suspend\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ dev_dbg(cs35l41->dev, "Enter hibernate\n");
+
+ regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
+ regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1,
+ CSPL_MBOX_CMD_HIBERNATE);
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41)
+{
+ const int pwrmgt_retries = 10;
+ unsigned int sts;
+ int i, ret;
+
+ for (i = 0; i < pwrmgt_retries; i++) {
+ ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts);
+ if (ret)
+ dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret);
+ else if (!(sts & CS35L41_WR_PEND_STS_MASK))
+ return;
+
+ udelay(20);
+ }
+
+ dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n");
+}
+
+static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(cs35l41->dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l41_set_cspl_mbox_cmd(cs35l41,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret)
+ break;
+
+ usleep_range(100, 200);
+ }
+
+ if (j < wake_retries) {
+ dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+
+ dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l41_wait_for_pwrmgt_sts(cs35l41);
+ regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
+
+ cs35l41_wait_for_pwrmgt_sts(cs35l41);
+ regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ cs35l41_wait_for_pwrmgt_sts(cs35l41);
+ regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3);
+ }
+
+ dev_err(cs35l41->dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+
+static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "Runtime resume\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ ret = cs35l41_exit_hibernate(cs35l41);
+ if (ret)
+ return ret;
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+const struct dev_pm_ops cs35l41_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+ SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+EXPORT_SYMBOL_GPL(cs35l41_pm_ops);
MODULE_DESCRIPTION("ASoC CS35L41 driver");
MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
index 48485b08a6f1..88a3d6e3434f 100644
--- a/sound/soc/codecs/cs35l41.h
+++ b/sound/soc/codecs/cs35l41.h
@@ -11,751 +11,38 @@
#define __CS35L41_H__
#include <linux/gpio/consumer.h>
-#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/firmware.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 4000000
+#include "wm_adsp.h"
#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);
+extern const struct dev_pm_ops cs35l41_pm_ops;
-struct cs35l41_otp_packed_element_t {
- u32 reg;
- u8 shift;
- u8 size;
+enum cs35l41_cspl_mbox_status {
+ CSPL_MBOX_STS_RUNNING = 0,
+ CSPL_MBOX_STS_PAUSED = 1,
+ CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
};
-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;
+enum cs35l41_cspl_mbox_cmd {
+ CSPL_MBOX_CMD_NONE = 0,
+ CSPL_MBOX_CMD_PAUSE = 1,
+ CSPL_MBOX_CMD_RESUME = 2,
+ CSPL_MBOX_CMD_REINIT = 3,
+ CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+ CSPL_MBOX_CMD_HIBERNATE = 5,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+ CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
+ CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
};
-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 wm_adsp dsp; /* needs to be first member */
struct snd_soc_codec *codec;
struct cs35l41_platform_data pdata;
struct device *dev;
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index cffd6111afac..4aaee1873a11 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -611,8 +611,8 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
if (devid != CS4265_CHIP_ID_VAL) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
- "CS4265 Device ID (%X). Expected %X\n",
- devid, CS4265_CHIP_ID);
+ "CS4265 Part Number ID: 0x%x Expected: 0x%x\n",
+ devid >> 4, CS4265_CHIP_ID_VAL >> 4);
return ret;
}
dev_info(&i2c_client->dev,
@@ -626,6 +626,16 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
ARRAY_SIZE(cs4265_dai));
}
+static int cs4265_i2c_remove(struct i2c_client *i2c)
+{
+ struct cs4265_private *cs4265 = i2c_get_clientdata(i2c);
+
+ if (cs4265->reset_gpio)
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 0);
+
+ return 0;
+}
+
static const struct of_device_id cs4265_of_match[] = {
{ .compatible = "cirrus,cs4265", },
{ }
@@ -645,6 +655,7 @@ static struct i2c_driver cs4265_i2c_driver = {
},
.id_table = cs4265_id,
.probe = cs4265_i2c_probe,
+ .remove = cs4265_i2c_remove,
};
module_i2c_driver(cs4265_i2c_driver);
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 27a1c4c73074..43d98bdb5b5b 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -42,6 +42,7 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_SRC_CTL, 0x10 },
{ CS42L42_MCLK_CTL, 0x02 },
{ CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_SLOW_START_ENABLE, 0x70 },
{ CS42L42_I2C_DEBOUNCE, 0x88 },
{ CS42L42_I2C_STRETCH, 0x03 },
{ CS42L42_I2C_TIMEOUT, 0xB7 },
@@ -177,6 +178,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
case CS42L42_MCLK_STATUS:
case CS42L42_MCLK_CTL:
case CS42L42_SFTRAMP_RATE:
+ case CS42L42_SLOW_START_ENABLE:
case CS42L42_I2C_DEBOUNCE:
case CS42L42_I2C_STRETCH:
case CS42L42_I2C_TIMEOUT:
@@ -387,6 +389,28 @@ static const struct regmap_config cs42l42_regmap = {
static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true);
static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true);
+static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ u8 val;
+
+ /* all bits of SLOW_START_EN much change together */
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ val = 0;
+ break;
+ case 1:
+ val = CS42L42_SLOW_START_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_MASK, val);
+}
+
static const char * const cs42l42_hpf_freq_text[] = {
"1.86Hz", "120Hz", "235Hz", "466Hz"
};
@@ -431,7 +455,11 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
CS42L42_DAC_HPF_EN_SHIFT, true, false),
SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL,
CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT,
- 0x3f, 1, mixer_tlv)
+ 0x3f, 1, mixer_tlv),
+
+ SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_SHIFT, true, false,
+ snd_soc_get_volsw, cs42l42_slow_start_put),
};
static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w,
@@ -521,8 +549,25 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_
{
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l42->jack_detect_mutex);
cs42l42->jack = jk;
+ if (jk) {
+ switch (cs42l42->hs_type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET);
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE);
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock(&cs42l42->jack_detect_mutex);
+
return 0;
}
@@ -706,10 +751,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
CS42L42_PLL_DIVOUT_MASK,
(pll_ratio_table[i].pll_divout * pll_ratio_table[i].n)
<< CS42L42_PLL_DIVOUT_SHIFT);
- if (pll_ratio_table[i].n != 1)
- cs42l42->pll_divout = pll_ratio_table[i].pll_divout;
- else
- cs42l42->pll_divout = 0;
snd_soc_component_update_bits(component,
CS42L42_PLL_CAL_RATIO,
CS42L42_PLL_CAL_RATIO_MASK,
@@ -976,12 +1017,13 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
CS42L42_PLL_START_MASK, 1);
- if (cs42l42->pll_divout) {
+ if (pll_ratio_table[cs42l42->pll_config].n > 1) {
usleep_range(CS42L42_PLL_DIVOUT_TIME_US,
CS42L42_PLL_DIVOUT_TIME_US * 2);
+ regval = pll_ratio_table[cs42l42->pll_config].pll_divout;
snd_soc_component_update_bits(component, CS42L42_PLL_CTL3,
CS42L42_PLL_DIVOUT_MASK,
- cs42l42->pll_divout <<
+ regval <<
CS42L42_PLL_DIVOUT_SHIFT);
}
@@ -1242,10 +1284,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Turn on level detect circuitry */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(3 << CS42L42_HSBIAS_CTL_SHIFT) |
(0 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1272,10 +1312,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Make sure button detect and HS bias circuits are off */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
}
@@ -1296,12 +1334,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
/* Unmask tip sense interrupts */
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));
}
@@ -1311,22 +1345,16 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
/* Mask tip sense interrupts */
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) |
(1 << CS42L42_TS_PLUG_SHIFT) |
(1 << CS42L42_TS_UNPLUG_SHIFT));
/* Make sure button detect and HS bias circuits are off */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1370,10 +1398,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
/* Power up HS bias to 2.7V */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(3 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1420,10 +1446,8 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
/* Ground HS bias */
regmap_update_bits(cs42l42->regmap,
CS42L42_MISC_DET_CTL,
- CS42L42_DETECT_MODE_MASK |
CS42L42_HSBIAS_CTL_MASK |
CS42L42_PDN_MIC_LVL_DET_MASK,
- (0 << CS42L42_DETECT_MODE_SHIFT) |
(1 << CS42L42_HSBIAS_CTL_SHIFT) |
(1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
@@ -1611,6 +1635,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
CS42L42_M_DETECT_FT_MASK |
CS42L42_M_HSBIAS_HIZ_MASK);
+ mutex_lock(&cs42l42->jack_detect_mutex);
+
/* Check auto-detect status */
if ((~masks[5]) & irq_params_table[5].mask) {
if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
@@ -1647,18 +1673,8 @@ 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) {
- case CS42L42_PLUG_CTIA:
- case CS42L42_PLUG_OMTP:
- snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET);
- break;
- case CS42L42_PLUG_HEADPHONE:
- snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADPHONE);
- break;
- default:
- break;
- }
snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3);
@@ -1689,6 +1705,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
}
}
+ mutex_unlock(&cs42l42->jack_detect_mutex);
+
return IRQ_HANDLED;
}
@@ -1801,6 +1819,9 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
cs42l42->hs_type = CS42L42_PLUG_INVALID;
+ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK, 0);
+
/* Latch analog controls to VP power domain */
regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
CS42L42_LATCH_TO_VP_MASK |
@@ -2033,6 +2054,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
cs42l42->dev = &i2c_client->dev;
i2c_set_clientdata(i2c_client, cs42l42);
+ mutex_init(&cs42l42->jack_detect_mutex);
cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
if (IS_ERR(cs42l42->regmap)) {
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index f45bcc9a3a62..9fff183dce8e 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -12,6 +12,7 @@
#ifndef __CS42L42_H__
#define __CS42L42_H__
+#include <linux/mutex.h>
#include <sound/jack.h>
#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */
@@ -62,6 +63,9 @@
#define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT)
#define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A)
+#define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B)
+#define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4)
+#define CS42L42_SLOW_START_EN_SHIFT 4
#define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E)
#define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F)
#define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10)
@@ -838,11 +842,11 @@ struct cs42l42_private {
struct gpio_desc *reset_gpio;
struct completion pdn_done;
struct snd_soc_jack *jack;
+ struct mutex jack_detect_mutex;
int pll_config;
int bclk;
u32 sclk;
u32 srate;
- u8 pll_divout;
u8 plug_state;
u8 hs_type;
u8 ts_inv;
diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c
index 2344a0b03518..9f20bfb855b3 100644
--- a/sound/soc/codecs/es7241.c
+++ b/sound/soc/codecs/es7241.c
@@ -255,7 +255,6 @@ static int es7241_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct es7241_data *priv;
- int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -271,28 +270,19 @@ static int es7241_probe(struct platform_device *pdev)
es7241_parse_fmt(dev, priv);
priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(priv->reset)) {
- err = PTR_ERR(priv->reset);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'reset' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(dev, PTR_ERR(priv->reset),
+ "Failed to get 'reset' gpio");
priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW);
- if (IS_ERR(priv->m0)) {
- err = PTR_ERR(priv->m0);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'm0' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->m0))
+ return dev_err_probe(dev, PTR_ERR(priv->m0),
+ "Failed to get 'm0' gpio");
priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW);
- if (IS_ERR(priv->m1)) {
- err = PTR_ERR(priv->m1);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'm1' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->m1))
+ return dev_err_probe(dev, PTR_ERR(priv->m1),
+ "Failed to get 'm1' gpio");
return devm_snd_soc_register_component(&pdev->dev,
&es7241_component_driver,
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 390dd6c7f6a5..de5955db0a5f 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -46,9 +46,8 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
-static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width);
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream,
+ int direction);
static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
struct snd_soc_dai *dai);
@@ -58,7 +57,7 @@ static const struct snd_soc_dai_ops hdac_hda_dai_ops = {
.prepare = hdac_hda_dai_prepare,
.hw_params = hdac_hda_dai_hw_params,
.hw_free = hdac_hda_dai_hw_free,
- .set_tdm_slot = hdac_hda_dai_set_tdm_slot,
+ .set_stream = hdac_hda_dai_set_stream,
};
static struct snd_soc_dai_driver hdac_hda_dais[] = {
@@ -180,21 +179,22 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
};
-static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width)
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
{
struct snd_soc_component *component = dai->component;
struct hdac_hda_priv *hda_pvt;
struct hdac_hda_pcm *pcm;
+ struct hdac_stream *hstream;
+
+ if (!stream)
+ return -EINVAL;
hda_pvt = snd_soc_component_get_drvdata(component);
pcm = &hda_pvt->pcm[dai->id];
+ hstream = (struct hdac_stream *)stream;
- if (tx_mask)
- pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
- else
- pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+ pcm->stream_tag[direction] = hstream->stream_tag;
return 0;
}
diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c
index 6b60120f59a6..1d0c467ab57b 100644
--- a/sound/soc/codecs/jz4770.c
+++ b/sound/soc/codecs/jz4770.c
@@ -307,6 +307,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
/* Unconditional controls. */
static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
@@ -319,6 +320,14 @@ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4770_CODEC_REG_GCR_MIXADC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4770_CODEC_REG_GCR_MIXDAC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
};
static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c
index 00e9d4fd1651..d75fd61b9032 100644
--- a/sound/soc/codecs/max9759.c
+++ b/sound/soc/codecs/max9759.c
@@ -140,7 +140,6 @@ static int max9759_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct max9759 *priv;
- int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -149,29 +148,20 @@ static int max9759_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_shutdown)) {
- err = PTR_ERR(priv->gpiod_shutdown);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'shutdown' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio");
priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_mute)) {
- err = PTR_ERR(priv->gpiod_mute);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'mute' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_mute))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+ "Failed to get 'mute' gpio");
priv->is_mute = true;
priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_gain)) {
- err = PTR_ERR(priv->gpiod_gain);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'gain' gpios: %d", err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_gain))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain),
+ "Failed to get 'gain' gpios");
priv->gain = 0;
if (priv->gpiod_gain->ndescs != 2) {
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index dc520effc61c..f47e956d4f55 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -741,7 +741,7 @@ static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai,
static const struct snd_soc_dai_ops max98373_dai_sdw_ops = {
.hw_params = max98373_sdw_dai_hw_params,
.hw_free = max98373_pcm_hw_free,
- .set_sdw_stream = max98373_set_sdw_stream,
+ .set_stream = max98373_set_sdw_stream,
.shutdown = max98373_shutdown,
.set_tdm_slot = max98373_sdw_set_tdm_slot,
};
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
index dd29b183ecd6..7c9686be59d9 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -606,12 +606,9 @@ static int max9860_probe(struct i2c_client *i2c)
return -ENOMEM;
max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
- if (IS_ERR(max9860->dvddio)) {
- ret = PTR_ERR(max9860->dvddio);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(max9860->dvddio))
+ return dev_err_probe(dev, PTR_ERR(max9860->dvddio),
+ "Failed to get DVDDIO supply\n");
max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
@@ -643,8 +640,7 @@ static int max9860_probe(struct i2c_client *i2c)
if (IS_ERR(mclk)) {
ret = PTR_ERR(mclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get MCLK: %d\n", ret);
+ dev_err_probe(dev, ret, "Failed to get MCLK\n");
goto err_regulator;
}
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 3ddd822240e3..485cda46dbb9 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -822,8 +822,8 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
{"EAR PA", NULL, "EAR CP"},
/* Headset (RX MIX1 and RX MIX2) */
- {"HEADPHONE", NULL, "HPHL PA"},
- {"HEADPHONE", NULL, "HPHR PA"},
+ {"HPH_L", NULL, "HPHL PA"},
+ {"HPH_R", NULL, "HPHR PA"},
{"HPHL DAC", NULL, "EAR_HPHL_CLK"},
{"HPHR DAC", NULL, "EAR_HPHR_CLK"},
@@ -870,7 +870,8 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AMIC3"),
SND_SOC_DAPM_INPUT("AMIC2"),
SND_SOC_DAPM_OUTPUT("EAR"),
- SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+ SND_SOC_DAPM_OUTPUT("HPH_L"),
+ SND_SOC_DAPM_OUTPUT("HPH_R"),
/* RX stuff */
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
index 358c500377df..3a881523c30f 100644
--- a/sound/soc/codecs/mt6660.c
+++ b/sound/soc/codecs/mt6660.c
@@ -47,13 +47,12 @@ static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val)
struct mt6660_chip *chip = context;
int size = mt6660_get_reg_size(reg);
u8 reg_data[4];
- int i, ret;
+ int i;
for (i = 0; i < size; i++)
reg_data[size - i - 1] = (val >> (8 * i)) & 0xff;
- ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
- return ret;
+ return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
}
static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val)
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index b6fd412441a1..fdf92c8b28e1 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -751,21 +751,14 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW |
GPIOD_FLAGS_BIT_NONEXCLUSIVE);
- if (IS_ERR(pcm3168a->gpio_rst)) {
- ret = PTR_ERR(pcm3168a->gpio_rst);
- if (ret != -EPROBE_DEFER )
- dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
-
- return ret;
- }
+ if (IS_ERR(pcm3168a->gpio_rst))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst),
+ "failed to acquire RST gpio\n");
pcm3168a->scki = devm_clk_get(dev, "scki");
- if (IS_ERR(pcm3168a->scki)) {
- ret = PTR_ERR(pcm3168a->scki);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
- return ret;
- }
+ if (IS_ERR(pcm3168a->scki))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->scki),
+ "failed to acquire clock 'scki'\n");
ret = clk_prepare_enable(pcm3168a->scki);
if (ret) {
@@ -781,8 +774,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
ret = devm_regulator_bulk_get(dev,
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to request supplies: %d\n", ret);
+ dev_err_probe(dev, ret, "failed to request supplies\n");
goto err_clk;
}
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index f716668de640..149a76075c76 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -613,7 +613,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
.hw_params = rt1308_sdw_hw_params,
.hw_free = rt1308_sdw_pcm_hw_free,
- .set_sdw_stream = rt1308_set_sdw_stream,
+ .set_stream = rt1308_set_sdw_stream,
.shutdown = rt1308_sdw_shutdown,
.set_tdm_slot = rt1308_sdw_set_tdm_slot,
};
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 09b4914bba1b..c66d7b20cb4d 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -602,7 +602,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
.hw_params = rt1316_sdw_hw_params,
.hw_free = rt1316_sdw_pcm_hw_free,
- .set_sdw_stream = rt1316_set_sdw_stream,
+ .set_stream = rt1316_set_sdw_stream,
.shutdown = rt1316_sdw_shutdown,
};
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index d01fe73ab9c8..e7a82565b905 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -195,6 +195,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
+ case RT5640_DUMMY2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -1972,7 +1973,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
snd_soc_component_update_bits(component, RT5640_DUMMY1,
- 0x0301, 0x0301);
+ 0x1, 0x1);
snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
}
@@ -2159,7 +2160,11 @@ static bool rt5640_jack_inserted(struct snd_soc_component *component)
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
int val;
- val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+ if (rt5640->jd_gpio)
+ val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0;
+ else
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+
dev_dbg(component->dev, "irq status %#04x\n", val);
if (rt5640->jd_inverted)
@@ -2297,10 +2302,42 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset);
static void rt5640_jack_work(struct work_struct *work)
{
struct rt5640_priv *rt5640 =
- container_of(work, struct rt5640_priv, jack_work);
+ container_of(work, struct rt5640_priv, jack_work.work);
struct snd_soc_component *component = rt5640->component;
int status;
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged;
+
+ /* mic jack */
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+ hda_mic_plugged = !(val & RT5640_JD_STATUS);
+ dev_dbg(component->dev, "mic jack status %d\n",
+ hda_mic_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1,
+ RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT);
+
+ if (hda_mic_plugged)
+ jack_type |= SND_JACK_MICROPHONE;
+
+ /* headphone jack */
+ val = snd_soc_component_read(component, RT5640_DUMMY2);
+ hda_hp_plugged = !(val & (0x1 << 11));
+ dev_dbg(component->dev, "headphone jack status %d\n",
+ hda_hp_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ (0x1 << 10), !hda_hp_plugged << 10);
+
+ if (hda_hp_plugged)
+ jack_type |= SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET);
+
+ return;
+ }
+
if (!rt5640_jack_inserted(component)) {
/* Jack removed, or spurious IRQ? */
if (rt5640->jack->status & SND_JACK_HEADPHONE) {
@@ -2348,7 +2385,7 @@ static void rt5640_jack_work(struct work_struct *work)
* disabled the OVCD IRQ, the IRQ pin will stay high and as
* we react to edges, we miss the unplug event -> recheck.
*/
- queue_work(system_long_wq, &rt5640->jack_work);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
}
}
@@ -2357,7 +2394,17 @@ static irqreturn_t rt5640_irq(int irq, void *data)
struct rt5640_priv *rt5640 = data;
if (rt5640->jack)
- queue_work(system_long_wq, &rt5640->jack_work);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work,
+ msecs_to_jiffies(JACK_SETTLE_TIME));
return IRQ_HANDLED;
}
@@ -2366,7 +2413,7 @@ static void rt5640_cancel_work(void *data)
{
struct rt5640_priv *rt5640 = data;
- cancel_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->jack_work);
cancel_delayed_work_sync(&rt5640->bp_work);
}
@@ -2406,7 +2453,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component)
if (!rt5640->jack)
return;
- free_irq(rt5640->irq, rt5640);
+ if (rt5640->jd_gpio_irq_requested)
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+
+ if (rt5640->irq_requested)
+ free_irq(rt5640->irq, rt5640);
+
rt5640_cancel_work(rt5640);
if (rt5640->jack->status & SND_JACK_MICROPHONE) {
@@ -2415,11 +2467,15 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component)
snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
}
+ rt5640->jd_gpio_irq_requested = false;
+ rt5640->irq_requested = false;
+ rt5640->jd_gpio = NULL;
rt5640->jack = NULL;
}
static void rt5640_enable_jack_detect(struct snd_soc_component *component,
- struct snd_soc_jack *jack)
+ struct snd_soc_jack *jack,
+ struct rt5640_set_jack_data *jack_data)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
int ret;
@@ -2463,28 +2519,90 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640_enable_micbias1_ovcd_irq(component);
}
+ if (jack_data && jack_data->codec_irq_override)
+ rt5640->irq = jack_data->codec_irq_override;
+
+ if (jack_data && jack_data->jd_gpio) {
+ rt5640->jd_gpio = jack_data->jd_gpio;
+ rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio);
+
+ ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5640-jd-gpio", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n",
+ rt5640->jd_gpio_irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->jd_gpio_irq_requested = true;
+ }
+
ret = request_irq(rt5640->irq, rt5640_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
- rt5640->irq = -ENXIO;
- /* Undo above settings */
rt5640_disable_jack_detect(component);
return;
}
+ rt5640->irq_requested = true;
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+}
+
+static void rt5640_enable_hda_jack_detect(
+ struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Select JD1 for Mic */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, RT5640_JD_JD1_IN4P);
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
+
+ /* Select JD2 for Headphone */
+ snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100);
+
+ /* Selecting GPIO01 as an interrupt */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
+ RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ);
+
+ /* Set GPIO1 output */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
+
+ rt5640->jack = jack;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640->irq = -ENXIO;
+ return;
+ }
/* sync initial jack state */
- queue_work(system_long_wq, &rt5640->jack_work);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
}
static int rt5640_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
- if (jack)
- rt5640_enable_jack_detect(component, jack);
- else
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ rt5640_enable_hda_jack_detect(component, jack);
+ else
+ rt5640_enable_jack_detect(component, jack, data);
+ } else {
rt5640_disable_jack_detect(component);
+ }
return 0;
}
@@ -2574,11 +2692,16 @@ static int rt5640_probe(struct snd_soc_component *component)
if (device_property_read_u32(component->dev,
"realtek,jack-detect-source", &val) == 0) {
- if (val <= RT5640_JD_SRC_GPIO4)
+ if (val <= RT5640_JD_SRC_GPIO4) {
rt5640->jd_src = val << RT5640_JD_SFT;
- else
+ } else if (val == RT5640_JD_SRC_HDA_HEADER) {
+ rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER;
+ snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ 0x0300, 0x0);
+ } else {
dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n",
val);
+ }
}
if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
@@ -2632,6 +2755,7 @@ static int rt5640_suspend(struct snd_soc_component *component)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ rt5640_cancel_work(rt5640);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
rt5640_reset(component);
regcache_cache_only(rt5640->regmap, true);
@@ -2654,6 +2778,17 @@ static int rt5640_resume(struct snd_soc_component *component)
regcache_cache_only(rt5640->regmap, false);
regcache_sync(rt5640->regmap);
+ if (rt5640->jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ snd_soc_component_update_bits(component,
+ RT5640_DUMMY2, 0x1100, 0x1100);
+ else
+ snd_soc_component_write(component, RT5640_DUMMY2,
+ 0x4001);
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+
return 0;
}
#else
@@ -2856,7 +2991,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
rt5640->hp_mute = true;
rt5640->irq = i2c->irq;
INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work);
- INIT_WORK(&rt5640->jack_work, rt5640_jack_work);
+ INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work);
/* Make sure work is stopped on probe-error / remove */
ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 2c28f83e338a..9e49b9a0ccaa 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -2124,6 +2124,7 @@ struct rt5640_priv {
int ldo1_en; /* GPIO for LDO1_EN */
int irq;
+ int jd_gpio_irq;
int sysclk;
int sysclk_src;
int lrck[RT5640_AIFS];
@@ -2136,6 +2137,8 @@ struct rt5640_priv {
bool hp_mute;
bool asrc_en;
+ bool irq_requested;
+ bool jd_gpio_irq_requested;
/* Jack and button detect data */
bool ovcd_irq_enabled;
@@ -2145,14 +2148,20 @@ struct rt5640_priv {
int release_count;
int poll_count;
struct delayed_work bp_work;
- struct work_struct jack_work;
+ struct delayed_work jack_work;
struct snd_soc_jack *jack;
+ struct gpio_desc *jd_gpio;
unsigned int jd_src;
bool jd_inverted;
unsigned int ovcd_th;
unsigned int ovcd_sf;
};
+struct rt5640_set_jack_data {
+ int codec_irq_override;
+ struct gpio_desc *jd_gpio;
+};
+
int rt5640_dmic_enable(struct snd_soc_component *component,
bool dmic1_data_pin, bool dmic2_data_pin);
int rt5640_sel_asrc_clk_src(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 0389b2bb360e..2138f62e6af5 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3461,6 +3461,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663)
static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
{
int table_size;
+ int ret;
device_property_read_u32(dev, "realtek,dc_offset_l_manual",
&rt5663->pdata.dc_offset_l_manual);
@@ -3477,9 +3478,11 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
table_size = sizeof(struct impedance_mapping_table) *
rt5663->pdata.impedance_sensing_num;
rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL);
- device_property_read_u32_array(dev,
+ ret = device_property_read_u32_array(dev,
"realtek,impedance_sensing_table",
(u32 *)rt5663->imp_table, table_size);
+ if (ret)
+ return ret;
}
return 0;
@@ -3504,8 +3507,11 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5663->pdata = *pdata;
- else
- rt5663_parse_dp(rt5663, &i2c->dev);
+ else {
+ ret = rt5663_parse_dp(rt5663, &i2c->dev);
+ if (ret)
+ return ret;
+ }
for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++)
rt5663->supplies[i].supply = rt5663_supply_names[i];
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index 31a4f286043e..248257a2e4e0 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -272,7 +272,7 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt5682_sdw_ops = {
.hw_params = rt5682_sdw_hw_params,
.hw_free = rt5682_sdw_hw_free,
- .set_sdw_stream = rt5682_set_sdw_stream,
+ .set_stream = rt5682_set_sdw_stream,
.shutdown = rt5682_sdw_shutdown,
};
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index b34a8542077d..415ec564c82e 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -2862,7 +2862,6 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682)
for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) {
struct clk_init_data init = { };
- struct clk_parent_data parent_data;
const struct clk_hw *parent;
dai_clk_hw = &rt5682->dai_clks_hw[i];
@@ -2871,10 +2870,8 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682)
case RT5682_DAI_WCLK_IDX:
/* Make MCLK the parent of WCLK */
if (rt5682->mclk) {
- parent_data = (struct clk_parent_data){
- .fw_name = "mclk",
- };
- init.parent_data = &parent_data;
+ parent = __clk_get_hw(rt5682->mclk);
+ init.parent_hws = &parent;
init.num_parents = 1;
}
break;
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index d49a4f68566d..efa1016831dd 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -1367,6 +1367,31 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5682s_stereo1_adc_mixl_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);
+ unsigned int delay = 0;
+
+ if (rt5682s->pdata.amic_delay)
+ delay = rt5682s->pdata.amic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(delay);
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, RT5682S_L_MUTE);
+ break;
+ }
+
+ return 0;
+}
+
static int sar_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1680,9 +1705,10 @@ static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = {
/* 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_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix),
+ rt5682s_stereo1_adc_mixl_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
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)),
@@ -2885,6 +2911,8 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
&rt5682s->pdata.dmic_clk_rate);
device_property_read_u32(dev, "realtek,dmic-delay-ms",
&rt5682s->pdata.dmic_delay);
+ device_property_read_u32(dev, "realtek,amic-delay-ms",
+ &rt5682s->pdata.amic_delay);
rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
"realtek,ldo1-en-gpios", 0);
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index 921382724f9c..e61a8257bf64 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -1005,7 +1005,7 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt700_ops = {
.hw_params = rt700_pcm_hw_params,
.hw_free = rt700_pcm_hw_free,
- .set_sdw_stream = rt700_set_sdw_stream,
+ .set_stream = rt700_set_sdw_stream,
.shutdown = rt700_shutdown,
};
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index 2e992589f1e4..bdb1375f0338 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -1358,7 +1358,7 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt711_sdca_ops = {
.hw_params = rt711_sdca_pcm_hw_params,
.hw_free = rt711_sdca_pcm_hw_free,
- .set_sdw_stream = rt711_sdca_set_sdw_stream,
+ .set_stream = rt711_sdca_set_sdw_stream,
.shutdown = rt711_sdca_shutdown,
};
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index a7c5608a0ef8..6770825d037a 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -1089,7 +1089,7 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt711_ops = {
.hw_params = rt711_pcm_hw_params,
.hw_free = rt711_pcm_hw_free,
- .set_sdw_stream = rt711_set_sdw_stream,
+ .set_stream = rt711_set_sdw_stream,
.shutdown = rt711_shutdown,
};
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index 66e166568c50..bfa536bd7196 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -938,7 +938,7 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt715_sdca_ops = {
.hw_params = rt715_sdca_pcm_hw_params,
.hw_free = rt715_sdca_pcm_hw_free,
- .set_sdw_stream = rt715_sdca_set_sdw_stream,
+ .set_stream = rt715_sdca_set_sdw_stream,
.shutdown = rt715_sdca_shutdown,
};
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index 1352869cc086..a64d11a74751 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -909,7 +909,7 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops rt715_ops = {
.hw_params = rt715_pcm_hw_params,
.hw_free = rt715_pcm_hw_free,
- .set_sdw_stream = rt715_set_sdw_stream,
+ .set_stream = rt715_set_sdw_stream,
.shutdown = rt715_shutdown,
};
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index 8ea13cfa9f8e..7c612aaf31c7 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -138,7 +138,7 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops sdw_mockup_ops = {
.hw_params = sdw_mockup_pcm_hw_params,
.hw_free = sdw_mockup_pcm_hw_free,
- .set_sdw_stream = sdw_mockup_set_sdw_stream,
+ .set_stream = sdw_mockup_set_sdw_stream,
.shutdown = sdw_mockup_shutdown,
};
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 97bf1f222805..8eebf27d0ea2 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1612,9 +1612,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
if (ret == -ENOENT)
ret = -EPROBE_DEFER;
- if (ret != -EPROBE_DEFER)
- dev_err(&client->dev, "Failed to get mclock: %d\n",
- ret);
+ dev_err_probe(&client->dev, ret, "Failed to get mclock\n");
+
goto disable_regs;
}
diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c
index b30fc1f894e1..d306c585b52b 100644
--- a/sound/soc/codecs/simple-amplifier.c
+++ b/sound/soc/codecs/simple-amplifier.c
@@ -69,7 +69,6 @@ static int simple_amp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct simple_amp *priv;
- int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
@@ -78,12 +77,9 @@ static int simple_amp_probe(struct platform_device *pdev)
priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
- if (IS_ERR(priv->gpiod_enable)) {
- err = PTR_ERR(priv->gpiod_enable);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'enable' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_enable))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable),
+ "Failed to get 'enable' gpio");
return devm_snd_soc_register_component(dev,
&simple_amp_component_driver,
diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c
index e0a09dadfa7c..d30c0d24d90a 100644
--- a/sound/soc/codecs/simple-mux.c
+++ b/sound/soc/codecs/simple-mux.c
@@ -82,7 +82,6 @@ static int simple_mux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct simple_mux *priv;
- int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -91,12 +90,9 @@ static int simple_mux_probe(struct platform_device *pdev)
dev_set_drvdata(dev, priv);
priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW);
- if (IS_ERR(priv->gpiod_mux)) {
- err = PTR_ERR(priv->gpiod_mux);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'mux' gpio: %d", err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_mux))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux),
+ "Failed to get 'mux' gpio");
return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0);
}
diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c
index 2968959c4b75..1d022643c307 100644
--- a/sound/soc/codecs/ssm2305.c
+++ b/sound/soc/codecs/ssm2305.c
@@ -57,7 +57,6 @@ static int ssm2305_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ssm2305 *priv;
- int err;
/* Allocate the private data */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -69,13 +68,9 @@ static int ssm2305_probe(struct platform_device *pdev)
/* Get shutdown gpio */
priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown",
GPIOD_OUT_LOW);
- if (IS_ERR(priv->gpiod_shutdown)) {
- err = PTR_ERR(priv->gpiod_shutdown);
- if (err != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'shutdown' gpio: %d\n",
- err);
- return err;
- }
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio\n");
return devm_snd_soc_register_component(dev, &ssm2305_component_driver,
NULL, 0);
diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h
index f16900e00afa..80bf56093d94 100644
--- a/sound/soc/codecs/sta350.h
+++ b/sound/soc/codecs/sta350.h
@@ -14,7 +14,7 @@
#ifndef _ASOC_STA_350_H
#define _ASOC_STA_350_H
-/* STA50 register addresses */
+/* STA350 register addresses */
#define STA350_REGISTER_COUNT 0x4D
#define STA350_COEF_COUNT 62
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c
index eb2a7870148d..dc86852752c5 100644
--- a/sound/soc/codecs/tfa989x.c
+++ b/sound/soc/codecs/tfa989x.c
@@ -7,6 +7,7 @@
* Copyright (C) 2013 Sony Mobile Communications Inc.
*/
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -56,6 +57,7 @@ struct tfa989x_rev {
struct tfa989x {
const struct tfa989x_rev *rev;
struct regulator *vddd_supply;
+ struct gpio_desc *rcv_gpiod;
};
static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg)
@@ -99,10 +101,20 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = {
{"Amp Input", "Right", "AIFINR"},
};
+static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component);
+
+ gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]);
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
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),
+ SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode),
};
static int tfa989x_probe(struct snd_soc_component *component)
@@ -301,6 +313,12 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply),
"Failed to get vddd regulator\n");
+ if (tfa989x->rev->rev == TFA9897_REVISION) {
+ tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW);
+ if (IS_ERR(tfa989x->rcv_gpiod))
+ return PTR_ERR(tfa989x->rcv_gpiod);
+ }
+
regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
new file mode 100644
index 000000000000..4baf3d881633
--- /dev/null
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Based on sound/soc/codecs/tlv320aic3x.c by Vladimir Barinov
+//
+// Copyright (C) 2010 Mistral Solutions Pvt Ltd.
+// Author: Shahina Shaik <shahina.s@mistralsolutions.com>
+//
+// Copyright (C) 2014-2018, Ambarella, Inc.
+// Author: Dongge wu <dgwu@ambarella.com>
+//
+// Copyright (C) 2021 Axis Communications AB
+// Author: Ricard Wanderlof <ricardw@axis.com>
+//
+
+#include <dt-bindings/sound/tlv320adc3xxx.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/driver.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+
+/*
+ * General definitions defining exported functionality.
+ */
+
+#define ADC3XXX_MICBIAS_PINS 2
+
+/* Number of GPIO pins exposed via the gpiolib interface */
+#define ADC3XXX_GPIOS_MAX 2
+
+#define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000
+#define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * PLL modes, to be used for clk_id for set_sysclk callback.
+ *
+ * The default behavior (AUTO) is to take the first matching entry in the clock
+ * table, which is intended to be the PLL based one if there is more than one.
+ *
+ * Setting the clock source using simple-card (clocks or
+ * system-clock-frequency property) sets clk_id = 0 = ADC3XXX_PLL_AUTO.
+ */
+#define ADC3XXX_PLL_AUTO 0 /* Use first available mode */
+#define ADC3XXX_PLL_ENABLE 1 /* Use PLL for clock generation */
+#define ADC3XXX_PLL_BYPASS 2 /* Don't use PLL for clock generation */
+
+/* Register definitions. */
+
+#define ADC3XXX_PAGE_SIZE 128
+#define ADC3XXX_REG(page, reg) ((page * ADC3XXX_PAGE_SIZE) + reg)
+
+/*
+ * Page 0 registers.
+ */
+
+#define ADC3XXX_PAGE_SELECT ADC3XXX_REG(0, 0)
+#define ADC3XXX_RESET ADC3XXX_REG(0, 1)
+
+/* 2-3 Reserved */
+
+#define ADC3XXX_CLKGEN_MUX ADC3XXX_REG(0, 4)
+#define ADC3XXX_PLL_PROG_PR ADC3XXX_REG(0, 5)
+#define ADC3XXX_PLL_PROG_J ADC3XXX_REG(0, 6)
+#define ADC3XXX_PLL_PROG_D_MSB ADC3XXX_REG(0, 7)
+#define ADC3XXX_PLL_PROG_D_LSB ADC3XXX_REG(0, 8)
+
+/* 9-17 Reserved */
+
+#define ADC3XXX_ADC_NADC ADC3XXX_REG(0, 18)
+#define ADC3XXX_ADC_MADC ADC3XXX_REG(0, 19)
+#define ADC3XXX_ADC_AOSR ADC3XXX_REG(0, 20)
+#define ADC3XXX_ADC_IADC ADC3XXX_REG(0, 21)
+
+/* 23-24 Reserved */
+
+#define ADC3XXX_CLKOUT_MUX ADC3XXX_REG(0, 25)
+#define ADC3XXX_CLKOUT_M_DIV ADC3XXX_REG(0, 26)
+#define ADC3XXX_INTERFACE_CTRL_1 ADC3XXX_REG(0, 27)
+#define ADC3XXX_CH_OFFSET_1 ADC3XXX_REG(0, 28)
+#define ADC3XXX_INTERFACE_CTRL_2 ADC3XXX_REG(0, 29)
+#define ADC3XXX_BCLK_N_DIV ADC3XXX_REG(0, 30)
+#define ADC3XXX_INTERFACE_CTRL_3 ADC3XXX_REG(0, 31)
+#define ADC3XXX_INTERFACE_CTRL_4 ADC3XXX_REG(0, 32)
+#define ADC3XXX_INTERFACE_CTRL_5 ADC3XXX_REG(0, 33)
+#define ADC3XXX_I2S_SYNC ADC3XXX_REG(0, 34)
+/* 35 Reserved */
+#define ADC3XXX_ADC_FLAG ADC3XXX_REG(0, 36)
+#define ADC3XXX_CH_OFFSET_2 ADC3XXX_REG(0, 37)
+#define ADC3XXX_I2S_TDM_CTRL ADC3XXX_REG(0, 38)
+/* 39-41 Reserved */
+#define ADC3XXX_INTR_FLAG_1 ADC3XXX_REG(0, 42)
+#define ADC3XXX_INTR_FLAG_2 ADC3XXX_REG(0, 43)
+/* 44 Reserved */
+#define ADC3XXX_INTR_FLAG_ADC1 ADC3XXX_REG(0, 45)
+/* 46 Reserved */
+#define ADC3XXX_INTR_FLAG_ADC2 ADC3XXX_REG(0, 47)
+#define ADC3XXX_INT1_CTRL ADC3XXX_REG(0, 48)
+#define ADC3XXX_INT2_CTRL ADC3XXX_REG(0, 49)
+/* 50 Reserved */
+#define ADC3XXX_GPIO2_CTRL ADC3XXX_REG(0, 51)
+#define ADC3XXX_GPIO1_CTRL ADC3XXX_REG(0, 52)
+#define ADC3XXX_DOUT_CTRL ADC3XXX_REG(0, 53)
+/* 54-56 Reserved */
+#define ADC3XXX_SYNC_CTRL_1 ADC3XXX_REG(0, 57)
+#define ADC3XXX_SYNC_CTRL_2 ADC3XXX_REG(0, 58)
+#define ADC3XXX_CIC_GAIN_CTRL ADC3XXX_REG(0, 59)
+/* 60 Reserved */
+#define ADC3XXX_PRB_SELECT ADC3XXX_REG(0, 61)
+#define ADC3XXX_INST_MODE_CTRL ADC3XXX_REG(0, 62)
+/* 63-79 Reserved */
+#define ADC3XXX_MIC_POLARITY_CTRL ADC3XXX_REG(0, 80)
+#define ADC3XXX_ADC_DIGITAL ADC3XXX_REG(0, 81)
+#define ADC3XXX_ADC_FGA ADC3XXX_REG(0, 82)
+#define ADC3XXX_LADC_VOL ADC3XXX_REG(0, 83)
+#define ADC3XXX_RADC_VOL ADC3XXX_REG(0, 84)
+#define ADC3XXX_ADC_PHASE_COMP ADC3XXX_REG(0, 85)
+#define ADC3XXX_LEFT_CHN_AGC_1 ADC3XXX_REG(0, 86)
+#define ADC3XXX_LEFT_CHN_AGC_2 ADC3XXX_REG(0, 87)
+#define ADC3XXX_LEFT_CHN_AGC_3 ADC3XXX_REG(0, 88)
+#define ADC3XXX_LEFT_CHN_AGC_4 ADC3XXX_REG(0, 89)
+#define ADC3XXX_LEFT_CHN_AGC_5 ADC3XXX_REG(0, 90)
+#define ADC3XXX_LEFT_CHN_AGC_6 ADC3XXX_REG(0, 91)
+#define ADC3XXX_LEFT_CHN_AGC_7 ADC3XXX_REG(0, 92)
+#define ADC3XXX_LEFT_AGC_GAIN ADC3XXX_REG(0, 93)
+#define ADC3XXX_RIGHT_CHN_AGC_1 ADC3XXX_REG(0, 94)
+#define ADC3XXX_RIGHT_CHN_AGC_2 ADC3XXX_REG(0, 95)
+#define ADC3XXX_RIGHT_CHN_AGC_3 ADC3XXX_REG(0, 96)
+#define ADC3XXX_RIGHT_CHN_AGC_4 ADC3XXX_REG(0, 97)
+#define ADC3XXX_RIGHT_CHN_AGC_5 ADC3XXX_REG(0, 98)
+#define ADC3XXX_RIGHT_CHN_AGC_6 ADC3XXX_REG(0, 99)
+#define ADC3XXX_RIGHT_CHN_AGC_7 ADC3XXX_REG(0, 100)
+#define ADC3XXX_RIGHT_AGC_GAIN ADC3XXX_REG(0, 101)
+/* 102-127 Reserved */
+
+/*
+ * Page 1 registers.
+ */
+
+/* 1-25 Reserved */
+#define ADC3XXX_DITHER_CTRL ADC3XXX_REG(1, 26)
+/* 27-50 Reserved */
+#define ADC3XXX_MICBIAS_CTRL ADC3XXX_REG(1, 51)
+#define ADC3XXX_LEFT_PGA_SEL_1 ADC3XXX_REG(1, 52)
+/* 53 Reserved */
+#define ADC3XXX_LEFT_PGA_SEL_2 ADC3XXX_REG(1, 54)
+#define ADC3XXX_RIGHT_PGA_SEL_1 ADC3XXX_REG(1, 55)
+#define ADC3XXX_RIGHT_PGA_SEL_2 ADC3XXX_REG(1, 57)
+#define ADC3XXX_LEFT_APGA_CTRL ADC3XXX_REG(1, 59)
+#define ADC3XXX_RIGHT_APGA_CTRL ADC3XXX_REG(1, 60)
+#define ADC3XXX_LOW_CURRENT_MODES ADC3XXX_REG(1, 61)
+#define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62)
+/* 63-127 Reserved */
+
+/*
+ * Register bits.
+ */
+
+/* PLL Enable bits */
+#define ADC3XXX_ENABLE_PLL_SHIFT 7
+#define ADC3XXX_ENABLE_PLL (1 << ADC3XXX_ENABLE_PLL_SHIFT)
+#define ADC3XXX_ENABLE_NADC_SHIFT 7
+#define ADC3XXX_ENABLE_NADC (1 << ADC3XXX_ENABLE_NADC_SHIFT)
+#define ADC3XXX_ENABLE_MADC_SHIFT 7
+#define ADC3XXX_ENABLE_MADC (1 << ADC3XXX_ENABLE_MADC_SHIFT)
+#define ADC3XXX_ENABLE_BCLK_SHIFT 7
+#define ADC3XXX_ENABLE_BCLK (1 << ADC3XXX_ENABLE_BCLK_SHIFT)
+
+/* Power bits */
+#define ADC3XXX_LADC_PWR_ON 0x80
+#define ADC3XXX_RADC_PWR_ON 0x40
+
+#define ADC3XXX_SOFT_RESET 0x01
+#define ADC3XXX_BCLK_MASTER 0x08
+#define ADC3XXX_WCLK_MASTER 0x04
+
+/* Interface register masks */
+#define ADC3XXX_FORMAT_MASK 0xc0
+#define ADC3XXX_FORMAT_SHIFT 6
+#define ADC3XXX_WLENGTH_MASK 0x30
+#define ADC3XXX_WLENGTH_SHIFT 4
+#define ADC3XXX_CLKDIR_MASK 0x0c
+#define ADC3XXX_CLKDIR_SHIFT 2
+
+/* Interface register bit patterns */
+#define ADC3XXX_FORMAT_I2S (0 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_DSP (1 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_RJF (2 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_LJF (3 << ADC3XXX_FORMAT_SHIFT)
+
+#define ADC3XXX_IFACE_16BITS (0 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_20BITS (1 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_24BITS (2 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_32BITS (3 << ADC3XXX_WLENGTH_SHIFT)
+
+/* PLL P/R bit offsets */
+#define ADC3XXX_PLLP_SHIFT 4
+#define ADC3XXX_PLLR_SHIFT 0
+#define ADC3XXX_PLL_PR_MASK 0x7f
+#define ADC3XXX_PLLJ_MASK 0x3f
+#define ADC3XXX_PLLD_MSB_MASK 0x3f
+#define ADC3XXX_PLLD_LSB_MASK 0xff
+#define ADC3XXX_NADC_MASK 0x7f
+#define ADC3XXX_MADC_MASK 0x7f
+#define ADC3XXX_AOSR_MASK 0xff
+#define ADC3XXX_IADC_MASK 0xff
+#define ADC3XXX_BDIV_MASK 0x7f
+
+/* PLL_CLKIN bits */
+#define ADC3XXX_PLL_CLKIN_SHIFT 2
+#define ADC3XXX_PLL_CLKIN_MCLK 0x0
+#define ADC3XXX_PLL_CLKIN_BCLK 0x1
+#define ADC3XXX_PLL_CLKIN_ZERO 0x3
+
+/* CODEC_CLKIN bits */
+#define ADC3XXX_CODEC_CLKIN_SHIFT 0
+#define ADC3XXX_CODEC_CLKIN_MCLK 0x0
+#define ADC3XXX_CODEC_CLKIN_BCLK 0x1
+#define ADC3XXX_CODEC_CLKIN_PLL_CLK 0x3
+
+#define ADC3XXX_USE_PLL ((ADC3XXX_PLL_CLKIN_MCLK << ADC3XXX_PLL_CLKIN_SHIFT) | \
+ (ADC3XXX_CODEC_CLKIN_PLL_CLK << ADC3XXX_CODEC_CLKIN_SHIFT))
+#define ADC3XXX_NO_PLL ((ADC3XXX_PLL_CLKIN_ZERO << ADC3XXX_PLL_CLKIN_SHIFT) | \
+ (ADC3XXX_CODEC_CLKIN_MCLK << ADC3XXX_CODEC_CLKIN_SHIFT))
+
+/* Analog PGA control bits */
+#define ADC3XXX_LPGA_MUTE 0x80
+#define ADC3XXX_RPGA_MUTE 0x80
+
+#define ADC3XXX_LPGA_GAIN_MASK 0x7f
+#define ADC3XXX_RPGA_GAIN_MASK 0x7f
+
+/* ADC current modes */
+#define ADC3XXX_ADC_LOW_CURR_MODE 0x01
+
+/* Left ADC Input selection bits */
+#define ADC3XXX_LCH_SEL1_SHIFT 0
+#define ADC3XXX_LCH_SEL2_SHIFT 2
+#define ADC3XXX_LCH_SEL3_SHIFT 4
+#define ADC3XXX_LCH_SEL4_SHIFT 6
+
+#define ADC3XXX_LCH_SEL1X_SHIFT 0
+#define ADC3XXX_LCH_SEL2X_SHIFT 2
+#define ADC3XXX_LCH_SEL3X_SHIFT 4
+#define ADC3XXX_LCH_COMMON_MODE 0x40
+#define ADC3XXX_BYPASS_LPGA 0x80
+
+/* Right ADC Input selection bits */
+#define ADC3XXX_RCH_SEL1_SHIFT 0
+#define ADC3XXX_RCH_SEL2_SHIFT 2
+#define ADC3XXX_RCH_SEL3_SHIFT 4
+#define ADC3XXX_RCH_SEL4_SHIFT 6
+
+#define ADC3XXX_RCH_SEL1X_SHIFT 0
+#define ADC3XXX_RCH_SEL2X_SHIFT 2
+#define ADC3XXX_RCH_SEL3X_SHIFT 4
+#define ADC3XXX_RCH_COMMON_MODE 0x40
+#define ADC3XXX_BYPASS_RPGA 0x80
+
+/* MICBIAS control bits */
+#define ADC3XXX_MICBIAS_MASK 0x2
+#define ADC3XXX_MICBIAS1_SHIFT 5
+#define ADC3XXX_MICBIAS2_SHIFT 3
+
+#define ADC3XXX_ADC_MAX_VOLUME 64
+#define ADC3XXX_ADC_POS_VOL 24
+
+/* GPIO control bits (GPIO1_CTRL and GPIO2_CTRL) */
+#define ADC3XXX_GPIO_CTRL_CFG_MASK 0x3c
+#define ADC3XXX_GPIO_CTRL_CFG_SHIFT 2
+#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK 0x01
+#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT 0
+#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_MASK 0x02
+#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_SHIFT 1
+
+enum adc3xxx_type {
+ ADC3001 = 0,
+ ADC3101
+};
+
+struct adc3xxx {
+ struct device *dev;
+ enum adc3xxx_type type;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct gpio_desc *rst_pin;
+ unsigned int pll_mode;
+ unsigned int sysclk;
+ unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */
+ unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS];
+ int master;
+ u8 page_no;
+ int use_pll;
+ struct gpio_chip gpio_chip;
+};
+
+static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = {
+ ADC3XXX_GPIO1_CTRL,
+ ADC3XXX_GPIO2_CTRL
+};
+
+static const unsigned int adc3xxx_micbias_shift[ADC3XXX_MICBIAS_PINS] = {
+ ADC3XXX_MICBIAS1_SHIFT,
+ ADC3XXX_MICBIAS2_SHIFT
+};
+
+static const struct reg_default adc3xxx_defaults[] = {
+ /* Page 0 */
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x00 },
+ { 4, 0x00 }, { 5, 0x11 }, { 6, 0x04 }, { 7, 0x00 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x00 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x00 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x01 }, { 19, 0x01 },
+ { 20, 0x80 }, { 21, 0x80 }, { 22, 0x04 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x01 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x02 }, { 30, 0x01 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x10 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x00 }, { 38, 0x02 }, { 39, 0x00 },
+ { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x00 },
+ { 44, 0x00 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 },
+ { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x00 },
+ { 52, 0x00 }, { 53, 0x12 }, { 54, 0x00 }, { 55, 0x00 },
+ { 56, 0x00 }, { 57, 0x00 }, { 58, 0x00 }, { 59, 0x44 },
+ { 60, 0x00 }, { 61, 0x01 }, { 62, 0x00 }, { 63, 0x00 },
+ { 64, 0x00 }, { 65, 0x00 }, { 66, 0x00 }, { 67, 0x00 },
+ { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 },
+ { 72, 0x00 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 },
+ { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 },
+ { 80, 0x00 }, { 81, 0x00 }, { 82, 0x88 }, { 83, 0x00 },
+ { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 },
+ { 88, 0x7f }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 },
+ { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 },
+ { 96, 0x7f }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 },
+ { 100, 0x00 }, { 101, 0x00 }, { 102, 0x00 }, { 103, 0x00 },
+ { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 },
+ { 108, 0x00 }, { 109, 0x00 }, { 110, 0x00 }, { 111, 0x00 },
+ { 112, 0x00 }, { 113, 0x00 }, { 114, 0x00 }, { 115, 0x00 },
+ { 116, 0x00 }, { 117, 0x00 }, { 118, 0x00 }, { 119, 0x00 },
+ { 120, 0x00 }, { 121, 0x00 }, { 122, 0x00 }, { 123, 0x00 },
+ { 124, 0x00 }, { 125, 0x00 }, { 126, 0x00 }, { 127, 0x00 },
+
+ /* Page 1 */
+ { 128, 0x00 }, { 129, 0x00 }, { 130, 0x00 }, { 131, 0x00 },
+ { 132, 0x00 }, { 133, 0x00 }, { 134, 0x00 }, { 135, 0x00 },
+ { 136, 0x00 }, { 137, 0x00 }, { 138, 0x00 }, { 139, 0x00 },
+ { 140, 0x00 }, { 141, 0x00 }, { 142, 0x00 }, { 143, 0x00 },
+ { 144, 0x00 }, { 145, 0x00 }, { 146, 0x00 }, { 147, 0x00 },
+ { 148, 0x00 }, { 149, 0x00 }, { 150, 0x00 }, { 151, 0x00 },
+ { 152, 0x00 }, { 153, 0x00 }, { 154, 0x00 }, { 155, 0x00 },
+ { 156, 0x00 }, { 157, 0x00 }, { 158, 0x00 }, { 159, 0x00 },
+ { 160, 0x00 }, { 161, 0x00 }, { 162, 0x00 }, { 163, 0x00 },
+ { 164, 0x00 }, { 165, 0x00 }, { 166, 0x00 }, { 167, 0x00 },
+ { 168, 0x00 }, { 169, 0x00 }, { 170, 0x00 }, { 171, 0x00 },
+ { 172, 0x00 }, { 173, 0x00 }, { 174, 0x00 }, { 175, 0x00 },
+ { 176, 0x00 }, { 177, 0x00 }, { 178, 0x00 }, { 179, 0x00 },
+ { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff },
+ { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 },
+ { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 },
+};
+
+static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADC3XXX_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg adc3xxx_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 2 * ADC3XXX_PAGE_SIZE,
+ .selector_reg = ADC3XXX_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = ADC3XXX_PAGE_SIZE,
+ }
+};
+
+static const struct regmap_config adc3xxx_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = adc3xxx_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adc3xxx_defaults),
+
+ .volatile_reg = adc3xxx_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .ranges = adc3xxx_ranges,
+ .num_ranges = ARRAY_SIZE(adc3xxx_ranges),
+ .max_register = 2 * ADC3XXX_PAGE_SIZE,
+};
+
+struct adc3xxx_rate_divs {
+ u32 mclk;
+ u32 rate;
+ u8 pll_p;
+ u8 pll_r;
+ u8 pll_j;
+ u16 pll_d;
+ u8 nadc;
+ u8 madc;
+ u8 aosr;
+};
+
+/*
+ * PLL and Clock settings.
+ * If p member is 0, PLL is not used.
+ * The order of the entries in this table have the PLL entries before
+ * the non-PLL entries, so that the PLL modes are preferred unless
+ * the PLL mode setting says otherwise.
+ */
+static const struct adc3xxx_rate_divs adc3xxx_divs[] = {
+ /* mclk, rate, p, r, j, d, nadc, madc, aosr */
+ /* 8k rate */
+ { 12000000, 8000, 1, 1, 7, 1680, 42, 2, 128 },
+ { 12288000, 8000, 1, 1, 7, 0000, 42, 2, 128 },
+ /* 11.025k rate */
+ { 12000000, 11025, 1, 1, 6, 8208, 29, 2, 128 },
+ /* 16k rate */
+ { 12000000, 16000, 1, 1, 7, 1680, 21, 2, 128 },
+ { 12288000, 16000, 1, 1, 7, 0000, 21, 2, 128 },
+ /* 22.05k rate */
+ { 12000000, 22050, 1, 1, 7, 560, 15, 2, 128 },
+ /* 32k rate */
+ { 12000000, 32000, 1, 1, 8, 1920, 12, 2, 128 },
+ { 12288000, 32000, 1, 1, 8, 0000, 12, 2, 128 },
+ /* 44.1k rate */
+ { 12000000, 44100, 1, 1, 7, 5264, 8, 2, 128 },
+ /* 48k rate */
+ { 12000000, 48000, 1, 1, 7, 1680, 7, 2, 128 },
+ { 12288000, 48000, 1, 1, 7, 0000, 7, 2, 128 },
+ { 24576000, 48000, 1, 1, 3, 5000, 7, 2, 128 }, /* With PLL */
+ { 24576000, 48000, 0, 0, 0, 0000, 2, 2, 128 }, /* Without PLL */
+ /* 88.2k rate */
+ { 12000000, 88200, 1, 1, 7, 5264, 4, 4, 64 },
+ /* 96k rate */
+ { 12000000, 96000, 1, 1, 8, 1920, 4, 4, 64 },
+};
+
+static int adc3xxx_get_divs(struct device *dev, int mclk, int rate, int pll_mode)
+{
+ int i;
+
+ dev_dbg(dev, "mclk = %d, rate = %d, clock mode %u\n",
+ mclk, rate, pll_mode);
+ for (i = 0; i < ARRAY_SIZE(adc3xxx_divs); i++) {
+ const struct adc3xxx_rate_divs *mode = &adc3xxx_divs[i];
+
+ /* Skip this entry if it doesn't fulfill the intended clock
+ * mode requirement. We consider anything besides the two
+ * modes below to be the same as ADC3XXX_PLL_AUTO.
+ */
+ if ((pll_mode == ADC3XXX_PLL_BYPASS && mode->pll_p) ||
+ (pll_mode == ADC3XXX_PLL_ENABLE && !mode->pll_p))
+ continue;
+
+ if (mode->rate == rate && mode->mclk == mclk)
+ return i;
+ }
+
+ dev_info(dev, "Master clock rate %d and sample rate %d is not supported\n",
+ mclk, rate);
+ return -EINVAL;
+}
+
+static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /* 10msec delay needed after PLL power-up to allow
+ * PLL and dividers to stabilize (datasheet p13).
+ */
+ usleep_range(10000, 20000);
+
+ return 0;
+}
+
+static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" };
+static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0,
+ adc_softstepping_text);
+
+static const char * const multiplier_text[] = { "1", "2", "4", "8", "16", "32", "64", "128" };
+static SOC_ENUM_SINGLE_DECL(left_agc_attack_mult_enum,
+ ADC3XXX_LEFT_CHN_AGC_4, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(right_agc_attack_mult_enum,
+ ADC3XXX_RIGHT_CHN_AGC_4, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(left_agc_decay_mult_enum,
+ ADC3XXX_LEFT_CHN_AGC_5, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(right_agc_decay_mult_enum,
+ ADC3XXX_RIGHT_CHN_AGC_5, 0, multiplier_text);
+
+static const char * const dither_dc_offset_text[] = {
+ "0mV", "15mV", "30mV", "45mV", "60mV", "75mV", "90mV", "105mV",
+ "-15mV", "-30mV", "-45mV", "-60mV", "-75mV", "-90mV", "-105mV"
+};
+static const unsigned int dither_dc_offset_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15
+};
+static SOC_VALUE_ENUM_DOUBLE_DECL(dither_dc_offset_enum,
+ ADC3XXX_DITHER_CTRL,
+ 4, 0, 0xf, dither_dc_offset_text,
+ dither_dc_offset_values);
+
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_fine_tlv, -40, 10, 0);
+/* AGC target: 8 values: -5.5, -8, -10, -12, -14, -17, -20, -24 dB */
+/* It would be nice to declare these in the order above, but empirically
+ * TLV_DB_SCALE_ITEM doesn't take lightly to the increment (second) parameter
+ * being negative, despite there being examples to the contrary in other
+ * drivers. So declare these in the order from lowest to highest, and
+ * set the invert flag in the SOC_DOUBLE_R_TLV declaration instead.
+ */
+static const DECLARE_TLV_DB_RANGE(agc_target_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-2400, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-2000, 300, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(-550, 0, 0));
+/* Since the 'disabled' value (mute) is at the highest value in the dB
+ * range (i.e. just before -32 dB) rather than the lowest, we need to resort
+ * to using a TLV_DB_RANGE in order to get the mute value in the right place.
+ */
+static const DECLARE_TLV_DB_RANGE(agc_thresh_tlv,
+ 0, 30, TLV_DB_SCALE_ITEM(-9000, 200, 0),
+ 31, 31, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */
+/* AGC hysteresis: 4 values: 1, 2, 4 dB, disabled (= mute) */
+static const DECLARE_TLV_DB_RANGE(agc_hysteresis_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(400, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */
+static const DECLARE_TLV_DB_SCALE(agc_max_tlv, 0, 50, 0);
+/* Input attenuation: -6 dB or 0 dB */
+static const DECLARE_TLV_DB_SCALE(input_attenuation_tlv, -600, 600, 0);
+
+static const struct snd_kcontrol_new adc3xxx_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADC3XXX_LEFT_APGA_CTRL,
+ ADC3XXX_RIGHT_APGA_CTRL, 0, 80, 0, pga_tlv),
+ SOC_DOUBLE("PGA Capture Switch", ADC3XXX_ADC_FGA, 7, 3, 1, 1),
+ SOC_DOUBLE_R("AGC Capture Switch", ADC3XXX_LEFT_CHN_AGC_1,
+ ADC3XXX_RIGHT_CHN_AGC_1, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("AGC Target Level Capture Volume", ADC3XXX_LEFT_CHN_AGC_1,
+ ADC3XXX_RIGHT_CHN_AGC_2, 4, 0x07, 1, agc_target_tlv),
+ SOC_DOUBLE_R_TLV("AGC Noise Threshold Capture Volume", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 1, 0x1f, 1, agc_thresh_tlv),
+ SOC_DOUBLE_R_TLV("AGC Hysteresis Capture Volume", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 6, 3, 0, agc_hysteresis_tlv),
+ SOC_DOUBLE_R("AGC Clip Stepping Capture Switch", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 0, 1, 0),
+ /*
+ * Oddly enough, the data sheet says the default value
+ * for the left/right AGC maximum gain register field
+ * (ADC3XXX_LEFT/RIGHT_CHN_AGC_3 bits 0..6) is 0x7f = 127
+ * (verified empirically) even though this value (indeed, above
+ * 0x50) is specified as 'Reserved. Do not use.' in the accompanying
+ * table in the data sheet.
+ */
+ SOC_DOUBLE_R_TLV("AGC Maximum Capture Volume", ADC3XXX_LEFT_CHN_AGC_3,
+ ADC3XXX_RIGHT_CHN_AGC_3, 0, 0x50, 0, agc_max_tlv),
+ SOC_DOUBLE_R("AGC Attack Time", ADC3XXX_LEFT_CHN_AGC_4,
+ ADC3XXX_RIGHT_CHN_AGC_4, 3, 0x1f, 0),
+ /* Would like to have the multipliers as LR pairs, but there is
+ * no SOC_ENUM_foo which accepts two values in separate registers.
+ */
+ SOC_ENUM("AGC Left Attack Time Multiplier", left_agc_attack_mult_enum),
+ SOC_ENUM("AGC Right Attack Time Multiplier", right_agc_attack_mult_enum),
+ SOC_DOUBLE_R("AGC Decay Time", ADC3XXX_LEFT_CHN_AGC_5,
+ ADC3XXX_RIGHT_CHN_AGC_5, 3, 0x1f, 0),
+ SOC_ENUM("AGC Left Decay Time Multiplier", left_agc_decay_mult_enum),
+ SOC_ENUM("AGC Right Decay Time Multiplier", right_agc_decay_mult_enum),
+ SOC_DOUBLE_R("AGC Noise Debounce", ADC3XXX_LEFT_CHN_AGC_6,
+ ADC3XXX_RIGHT_CHN_AGC_6, 0, 0x1f, 0),
+ SOC_DOUBLE_R("AGC Signal Debounce", ADC3XXX_LEFT_CHN_AGC_7,
+ ADC3XXX_RIGHT_CHN_AGC_7, 0, 0x0f, 0),
+ /* Read only register */
+ SOC_DOUBLE_R_S_TLV("AGC Applied Capture Volume", ADC3XXX_LEFT_AGC_GAIN,
+ ADC3XXX_RIGHT_AGC_GAIN, 0, -24, 40, 6, 0, adc_tlv),
+ /* ADC soft stepping */
+ SOC_ENUM("ADC Soft Stepping", adc_softstepping_enum),
+ /* Left/Right Input attenuation */
+ SOC_SINGLE_TLV("Left Input IN_1L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_2L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_3L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_1R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_2L_3L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_1L_1R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_2R_3R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_1R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_2R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_3R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_1L Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_2R_3R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_1L_1R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_2L_3L Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC Volume Control Capture Volume", ADC3XXX_LADC_VOL,
+ ADC3XXX_RADC_VOL, 0, -24, 40, 6, 0, adc_tlv),
+ /* Empirically, the following doesn't work the way it's supposed
+ * to. Values 0, -0.1, -0.2 and -0.3 dB result in the same level, and
+ * -0.4 dB drops about 0.12 dB on a specific chip.
+ */
+ SOC_DOUBLE_TLV("ADC Fine Volume Control Capture Volume", ADC3XXX_ADC_FGA,
+ 4, 0, 4, 1, adc_fine_tlv),
+ SOC_SINGLE("Left ADC Unselected CM Bias Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 6, 1, 0),
+ SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0),
+ SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum),
+};
+
+/* Left input selection, Single Ended inputs and Differential inputs */
+static const struct snd_kcontrol_new left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN_1L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 1, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_2L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_3L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 7, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_1R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 1, 0x1, 1),
+};
+
+/* Right input selection, Single Ended inputs and Differential inputs */
+static const struct snd_kcontrol_new right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN_1R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 1, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_2R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_3R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 7, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_1L Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 1, 0x1, 1),
+};
+
+/* Left Digital Mic input for left ADC */
+static const struct snd_kcontrol_new left_input_dmic_controls[] = {
+ SOC_DAPM_SINGLE("Left ADC Capture Switch",
+ ADC3XXX_ADC_DIGITAL, 3, 0x1, 0),
+};
+
+/* Right Digital Mic input for Right ADC */
+static const struct snd_kcontrol_new right_input_dmic_controls[] = {
+ SOC_DAPM_SINGLE("Right ADC Capture Switch",
+ ADC3XXX_ADC_DIGITAL, 2, 0x1, 0),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget adc3xxx_dapm_widgets[] = {
+
+ /* Left Input Selection */
+ SND_SOC_DAPM_MIXER("Left Input", SND_SOC_NOPM, 0, 0,
+ &left_input_mixer_controls[0],
+ ARRAY_SIZE(left_input_mixer_controls)),
+ /* Right Input Selection */
+ SND_SOC_DAPM_MIXER("Right Input", SND_SOC_NOPM, 0, 0,
+ &right_input_mixer_controls[0],
+ ARRAY_SIZE(right_input_mixer_controls)),
+ /* PGA selection */
+ SND_SOC_DAPM_PGA("Left PGA", ADC3XXX_LEFT_APGA_CTRL, 7, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", ADC3XXX_RIGHT_APGA_CTRL, 7, 1, NULL, 0),
+
+ /* Digital Microphone Input Control for Left/Right ADC */
+ SND_SOC_DAPM_MIXER("Left DMic Input", SND_SOC_NOPM, 0, 0,
+ &left_input_dmic_controls[0],
+ ARRAY_SIZE(left_input_dmic_controls)),
+ SND_SOC_DAPM_MIXER("Right DMic Input", SND_SOC_NOPM, 0, 0,
+ &right_input_dmic_controls[0],
+ ARRAY_SIZE(right_input_dmic_controls)),
+
+ /* Left/Right ADC */
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3XXX_ADC_DIGITAL, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3XXX_ADC_DIGITAL, 6, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("IN_1L"),
+ SND_SOC_DAPM_INPUT("IN_1R"),
+ SND_SOC_DAPM_INPUT("IN_2L"),
+ SND_SOC_DAPM_INPUT("IN_2R"),
+ SND_SOC_DAPM_INPUT("IN_3L"),
+ SND_SOC_DAPM_INPUT("IN_3R"),
+ SND_SOC_DAPM_INPUT("DIFL_1L_1R"),
+ SND_SOC_DAPM_INPUT("DIFL_2L_3L"),
+ SND_SOC_DAPM_INPUT("DIFL_2R_3R"),
+ SND_SOC_DAPM_INPUT("DIFR_1L_1R"),
+ SND_SOC_DAPM_INPUT("DIFR_2L_3L"),
+ SND_SOC_DAPM_INPUT("DIFR_2R_3R"),
+ SND_SOC_DAPM_INPUT("DMic_L"),
+ SND_SOC_DAPM_INPUT("DMic_R"),
+
+ /* Digital audio interface output */
+ SND_SOC_DAPM_AIF_OUT("AIF_OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Clocks */
+ SND_SOC_DAPM_SUPPLY("PLL_CLK", ADC3XXX_PLL_PROG_PR, ADC3XXX_ENABLE_PLL_SHIFT,
+ 0, adc3xxx_pll_delay, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("ADC_CLK", ADC3XXX_ADC_NADC, ADC3XXX_ENABLE_NADC_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", ADC3XXX_ADC_MADC, ADC3XXX_ENABLE_MADC_SHIFT,
+ 0, NULL, 0),
+
+ /* This refers to the generated BCLK in master mode. */
+ SND_SOC_DAPM_SUPPLY("BCLK", ADC3XXX_BCLK_N_DIV, ADC3XXX_ENABLE_BCLK_SHIFT,
+ 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adc3xxx_intercon[] = {
+ /* Left input selection from switches */
+ { "Left Input", "IN_1L Capture Switch", "IN_1L" },
+ { "Left Input", "IN_2L Capture Switch", "IN_2L" },
+ { "Left Input", "IN_3L Capture Switch", "IN_3L" },
+ { "Left Input", "DIF_2L_3L Capture Switch", "DIFL_2L_3L" },
+ { "Left Input", "DIF_1L_1R Capture Switch", "DIFL_1L_1R" },
+ { "Left Input", "DIF_2R_3R Capture Switch", "DIFL_2R_3R" },
+ { "Left Input", "IN_1R Capture Switch", "IN_1R" },
+
+ /* Left input selection to left PGA */
+ { "Left PGA", NULL, "Left Input" },
+
+ /* Left PGA to left ADC */
+ { "Left ADC", NULL, "Left PGA" },
+
+ /* Right input selection from switches */
+ { "Right Input", "IN_1R Capture Switch", "IN_1R" },
+ { "Right Input", "IN_2R Capture Switch", "IN_2R" },
+ { "Right Input", "IN_3R Capture Switch", "IN_3R" },
+ { "Right Input", "DIF_2R_3R Capture Switch", "DIFR_2R_3R" },
+ { "Right Input", "DIF_1L_1R Capture Switch", "DIFR_1L_1R" },
+ { "Right Input", "DIF_2L_3L Capture Switch", "DIFR_2L_3L" },
+ { "Right Input", "IN_1L Capture Switch", "IN_1L" },
+
+ /* Right input selection to right PGA */
+ { "Right PGA", NULL, "Right Input" },
+
+ /* Right PGA to right ADC */
+ { "Right ADC", NULL, "Right PGA" },
+
+ /* Left DMic Input selection from switch */
+ { "Left DMic Input", "Left ADC Capture Switch", "DMic_L" },
+
+ /* Left DMic to left ADC */
+ { "Left ADC", NULL, "Left DMic Input" },
+
+ /* Right DMic Input selection from switch */
+ { "Right DMic Input", "Right ADC Capture Switch", "DMic_R" },
+
+ /* Right DMic to right ADC */
+ { "Right ADC", NULL, "Right DMic Input" },
+
+ /* ADC to AIF output */
+ { "AIF_OUT", NULL, "Left ADC" },
+ { "AIF_OUT", NULL, "Right ADC" },
+
+ /* Clocking */
+ { "ADC_MOD_CLK", NULL, "ADC_CLK" },
+ { "Left ADC", NULL, "ADC_MOD_CLK" },
+ { "Right ADC", NULL, "ADC_MOD_CLK" },
+
+ { "BCLK", NULL, "ADC_CLK" },
+};
+
+static const struct snd_soc_dapm_route adc3xxx_pll_intercon[] = {
+ { "ADC_CLK", NULL, "PLL_CLK" },
+};
+
+static const struct snd_soc_dapm_route adc3xxx_bclk_out_intercon[] = {
+ { "AIF_OUT", NULL, "BCLK" }
+};
+
+static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+
+ if (offset >= ADC3XXX_GPIOS_MAX)
+ return -EINVAL;
+
+ /* GPIO1 is offset 0, GPIO2 is offset 1 */
+ /* We check here that the GPIO pins are either not configured in the
+ * DT, or that they purposely are set as outputs.
+ * (Input mode not yet implemented).
+ */
+ if (adc3xxx->gpio_cfg[offset] != 0 &&
+ adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int adc3xxx_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+
+ /* Set GPIO output function. */
+ return regmap_update_bits(adc3xxx->regmap,
+ adc3xxx_gpio_ctrl_reg[offset],
+ ADC3XXX_GPIO_CTRL_CFG_MASK |
+ ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK,
+ ADC3XXX_GPIO_GPO << ADC3XXX_GPIO_CTRL_CFG_SHIFT |
+ !!value << ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT);
+}
+
+/* With only GPIO outputs configured, we never get the .direction_out call,
+ * so we set the output mode and output value in the same call. Hence
+ * .set in practice does the same thing as .direction_out .
+ */
+static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ (void) adc3xxx_gpio_direction_out(chip, offset, value);
+}
+
+/* Even though we only support GPIO output for now, some GPIO clients
+ * want to read the current pin state using the .get callback.
+ */
+static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+ unsigned int regval;
+ int ret;
+
+ /* We only allow output pins, so just read the value set in the output
+ * pin register field.
+ */
+ ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], &regval);
+ if (ret)
+ return ret;
+ return !!(regval & ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK);
+}
+
+static const struct gpio_chip adc3xxx_gpio_chip = {
+ .label = "adc3xxx",
+ .owner = THIS_MODULE,
+ .request = adc3xxx_gpio_request,
+ .direction_output = adc3xxx_gpio_direction_out,
+ .set = adc3xxx_gpio_set,
+ .get = adc3xxx_gpio_get,
+ .can_sleep = 1,
+};
+
+static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx)
+{
+ gpiochip_remove(&adc3xxx->gpio_chip);
+}
+
+static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx)
+{
+ int gpio, micbias;
+ int ret;
+
+ adc3xxx->gpio_chip = adc3xxx_gpio_chip;
+ adc3xxx->gpio_chip.ngpio = ADC3XXX_GPIOS_MAX;
+ adc3xxx->gpio_chip.parent = adc3xxx->dev;
+ adc3xxx->gpio_chip.base = -1;
+
+ ret = gpiochip_add_data(&adc3xxx->gpio_chip, adc3xxx);
+ if (ret)
+ dev_err(adc3xxx->dev, "Failed to add gpios: %d\n", ret);
+
+ /* Set up potential GPIO configuration from the devicetree.
+ * This allows us to set up things which are not software
+ * controllable GPIOs, such as PDM microphone I/O,
+ */
+ for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) {
+ unsigned int cfg = adc3xxx->gpio_cfg[gpio];
+
+ if (cfg) {
+ cfg--; /* actual value to use is stored +1 */
+ regmap_update_bits(adc3xxx->regmap,
+ adc3xxx_gpio_ctrl_reg[gpio],
+ ADC3XXX_GPIO_CTRL_CFG_MASK,
+ cfg << ADC3XXX_GPIO_CTRL_CFG_SHIFT);
+ }
+ }
+
+ /* Set up micbias voltage */
+ for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) {
+ unsigned int vg = adc3xxx->micbias_vg[micbias];
+
+ regmap_update_bits(adc3xxx->regmap,
+ ADC3XXX_MICBIAS_CTRL,
+ ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias],
+ vg << adc3xxx_micbias_shift[micbias]);
+ }
+}
+
+static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx,
+ const char *propname, unsigned int *cfg)
+{
+ struct device *dev = adc3xxx->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int val;
+
+ if (!of_property_read_u32(np, propname, &val)) {
+ if (val & ~15 || val == 7 || val >= 11) {
+ dev_err(dev, "Invalid property value for '%s'\n", propname);
+ return -EINVAL;
+ }
+ if (val == ADC3XXX_GPIO_GPI)
+ dev_warn(dev, "GPIO Input read not yet implemented\n");
+ *cfg = val + 1; /* 0 => not set up, all others shifted +1 */
+ }
+ return 0;
+}
+
+static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx,
+ const char *propname, unsigned int *vg)
+{
+ struct device *dev = adc3xxx->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int val;
+
+ if (!of_property_read_u32(np, propname, &val)) {
+ if (val >= ADC3XXX_MICBIAS_AVDD) {
+ dev_err(dev, "Invalid property value for '%s'\n", propname);
+ return -EINVAL;
+ }
+ *vg = val;
+ }
+ return 0;
+}
+
+static int adc3xxx_parse_pll_mode(uint32_t val, unsigned int *pll_mode)
+{
+ if (val != ADC3XXX_PLL_ENABLE && val != ADC3XXX_PLL_BYPASS &&
+ val != ADC3XXX_PLL_AUTO)
+ return -EINVAL;
+
+ *pll_mode = val;
+
+ return 0;
+}
+
+static void adc3xxx_setup_pll(struct snd_soc_component *component,
+ int div_entry)
+{
+ int i = div_entry;
+
+ /* P & R values */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_PR,
+ (adc3xxx_divs[i].pll_p << ADC3XXX_PLLP_SHIFT) |
+ (adc3xxx_divs[i].pll_r << ADC3XXX_PLLR_SHIFT));
+ /* J value */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_J,
+ adc3xxx_divs[i].pll_j & ADC3XXX_PLLJ_MASK);
+ /* D value */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_LSB,
+ adc3xxx_divs[i].pll_d & ADC3XXX_PLLD_LSB_MASK);
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_MSB,
+ (adc3xxx_divs[i].pll_d >> 8) & ADC3XXX_PLLD_MSB_MASK);
+}
+
+static int adc3xxx_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 snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component);
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ int i, width = 16;
+ u8 iface_len, bdiv;
+
+ i = adc3xxx_get_divs(component->dev, adc3xxx->sysclk,
+ params_rate(params), adc3xxx->pll_mode);
+
+ if (i < 0)
+ return i;
+
+ /* select data word length */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iface_len = ADC3XXX_IFACE_16BITS;
+ width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface_len = ADC3XXX_IFACE_20BITS;
+ width = 20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface_len = ADC3XXX_IFACE_24BITS;
+ width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface_len = ADC3XXX_IFACE_32BITS;
+ width = 32;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported serial data format\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, ADC3XXX_INTERFACE_CTRL_1,
+ ADC3XXX_WLENGTH_MASK, iface_len);
+ if (adc3xxx_divs[i].pll_p) { /* If PLL used for this mode */
+ adc3xxx_setup_pll(component, i);
+ snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_USE_PLL);
+ if (!adc3xxx->use_pll) {
+ snd_soc_dapm_add_routes(dapm, adc3xxx_pll_intercon,
+ ARRAY_SIZE(adc3xxx_pll_intercon));
+ adc3xxx->use_pll = 1;
+ }
+ } else {
+ snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_NO_PLL);
+ if (adc3xxx->use_pll) {
+ snd_soc_dapm_del_routes(dapm, adc3xxx_pll_intercon,
+ ARRAY_SIZE(adc3xxx_pll_intercon));
+ adc3xxx->use_pll = 0;
+ }
+ }
+
+ /* NADC */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_NADC,
+ ADC3XXX_NADC_MASK, adc3xxx_divs[i].nadc);
+ /* MADC */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_MADC,
+ ADC3XXX_MADC_MASK, adc3xxx_divs[i].madc);
+ /* AOSR */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_AOSR,
+ ADC3XXX_AOSR_MASK, adc3xxx_divs[i].aosr);
+ /* BDIV N Value */
+ /* BCLK is (by default) set up to be derived from ADC_CLK */
+ bdiv = (adc3xxx_divs[i].aosr * adc3xxx_divs[i].madc) / (2 * width);
+ snd_soc_component_update_bits(component, ADC3XXX_BCLK_N_DIV,
+ ADC3XXX_BDIV_MASK, bdiv);
+
+ return 0;
+}
+
+static const char *adc3xxx_pll_mode_text(int pll_mode)
+{
+ switch (pll_mode) {
+ case ADC3XXX_PLL_AUTO:
+ return "PLL auto";
+ case ADC3XXX_PLL_ENABLE:
+ return "PLL enable";
+ case ADC3XXX_PLL_BYPASS:
+ return "PLL bypass";
+ default:
+ break;
+ }
+
+ return "PLL unknown";
+}
+
+static int adc3xxx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = adc3xxx_parse_pll_mode(clk_id, &adc3xxx->pll_mode);
+ if (ret < 0)
+ return ret;
+
+ adc3xxx->sysclk = freq;
+ dev_dbg(component->dev, "Set sysclk to %u Hz, %s\n",
+ freq, adc3xxx_pll_mode_text(adc3xxx->pll_mode));
+ return 0;
+}
+
+static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ u8 clkdir = 0, format = 0;
+ int master = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ master = 1;
+ clkdir = ADC3XXX_BCLK_MASTER | ADC3XXX_WCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ master = 0;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock setup\n");
+ return -EINVAL;
+ }
+
+ /*
+ * match both interface format and signal polarities since they
+ * are fixed
+ */
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF:
+ format = ADC3XXX_FORMAT_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF:
+ format = ADC3XXX_FORMAT_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_RJF;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_LJF;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ /* Add/del route enabling BCLK output as applicable */
+ if (master && !adc3xxx->master)
+ snd_soc_dapm_add_routes(dapm, adc3xxx_bclk_out_intercon,
+ ARRAY_SIZE(adc3xxx_bclk_out_intercon));
+ else if (!master && adc3xxx->master)
+ snd_soc_dapm_del_routes(dapm, adc3xxx_bclk_out_intercon,
+ ARRAY_SIZE(adc3xxx_bclk_out_intercon));
+ adc3xxx->master = master;
+
+ /* set clock direction and format */
+ return snd_soc_component_update_bits(component,
+ ADC3XXX_INTERFACE_CTRL_1,
+ ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK,
+ clkdir | format);
+}
+
+static const struct snd_soc_dai_ops adc3xxx_dai_ops = {
+ .hw_params = adc3xxx_hw_params,
+ .set_sysclk = adc3xxx_set_dai_sysclk,
+ .set_fmt = adc3xxx_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver adc3xxx_dai = {
+ .name = "tlv320adc3xxx-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ADC3XXX_RATES,
+ .formats = ADC3XXX_FORMATS,
+ },
+ .ops = &adc3xxx_dai_ops,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_adc3xxx = {
+ .controls = adc3xxx_snd_controls,
+ .num_controls = ARRAY_SIZE(adc3xxx_snd_controls),
+ .dapm_widgets = adc3xxx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets),
+ .dapm_routes = adc3xxx_intercon,
+ .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon),
+};
+
+static int adc3xxx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct adc3xxx *adc3xxx = NULL;
+ int ret;
+
+ adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
+ if (!adc3xxx)
+ return -ENOMEM;
+ adc3xxx->dev = dev;
+
+ adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(adc3xxx->rst_pin)) {
+ return dev_err_probe(dev, PTR_ERR(adc3xxx->rst_pin),
+ "Failed to request rst_pin\n");
+ }
+
+ adc3xxx->mclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(adc3xxx->mclk)) {
+ /*
+ * The chip itself supports running off the BCLK either
+ * directly or via the PLL, but the driver does not (yet), so
+ * having a specified mclk is required. Otherwise, we could
+ * use the lack of a clocks property to indicate when BCLK is
+ * intended as the clock source.
+ */
+ return dev_err_probe(dev, PTR_ERR(adc3xxx->mclk),
+ "Failed to acquire MCLK\n");
+ } else if (adc3xxx->mclk) {
+ ret = clk_prepare_enable(adc3xxx->mclk);
+ if (ret < 0)
+ return ret;
+ dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk));
+ }
+
+ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+
+ adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap);
+ if (IS_ERR(adc3xxx->regmap)) {
+ ret = PTR_ERR(adc3xxx->regmap);
+ goto err_unprepare_mclk;
+ }
+
+ i2c_set_clientdata(i2c, adc3xxx);
+
+ adc3xxx->type = id->driver_data;
+
+ /* Reset codec chip */
+ gpiod_set_value_cansleep(adc3xxx->rst_pin, 1);
+ usleep_range(2000, 100000); /* Requirement: > 10 ns (datasheet p13) */
+ gpiod_set_value_cansleep(adc3xxx->rst_pin, 0);
+
+ /* Potentially set up pins used as GPIOs */
+ adc3xxx_init_gpio(adc3xxx);
+
+ ret = snd_soc_register_component(dev,
+ &soc_component_dev_adc3xxx, &adc3xxx_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ goto err_unprepare_mclk;
+ }
+
+ return 0;
+
+err_unprepare_mclk:
+ clk_disable_unprepare(adc3xxx->mclk);
+ return ret;
+}
+
+static int __exit adc3xxx_i2c_remove(struct i2c_client *client)
+{
+ struct adc3xxx *adc3xxx = i2c_get_clientdata(client);
+
+ if (adc3xxx->mclk)
+ clk_disable_unprepare(adc3xxx->mclk);
+ adc3xxx_free_gpio(adc3xxx);
+ snd_soc_unregister_component(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id tlv320adc3xxx_of_match[] = {
+ { .compatible = "ti,tlv320adc3001", },
+ { .compatible = "ti,tlv320adc3101", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match);
+
+static const struct i2c_device_id adc3xxx_i2c_id[] = {
+ { "tlv320adc3001", ADC3001 },
+ { "tlv320adc3101", ADC3101 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
+
+static struct i2c_driver adc3xxx_i2c_driver = {
+ .driver = {
+ .name = "tlv320adc3xxx-codec",
+ .of_match_table = tlv320adc3xxx_of_match,
+ },
+ .probe = adc3xxx_i2c_probe,
+ .remove = adc3xxx_i2c_remove,
+ .id_table = adc3xxx_i2c_id,
+};
+
+module_i2c_driver(adc3xxx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320ADC3xxx codec driver");
+MODULE_AUTHOR("shahina.s@mistralsolutions.com");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h
index 1f2879b7a080..c86569883e0c 100644
--- a/sound/soc/codecs/tlv320aic26.h
+++ b/sound/soc/codecs/tlv320aic26.h
@@ -6,8 +6,8 @@
* Copyright (C) 2008 Secret Lab Technologies Ltd.
*/
-#ifndef _TLV320AIC16_H_
-#define _TLV320AIC16_H_
+#ifndef _TLV320AIC26_H_
+#define _TLV320AIC26_H_
/* AIC26 Registers */
#define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5)
@@ -88,4 +88,4 @@ enum aic26_wlen {
AIC26_WLEN_32 = 3 << 10,
};
-#endif /* _TLV320AIC16_H_ */
+#endif /* _TLV320AIC26_H_ */
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 52d2c968b5c0..e77342aff46d 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
@@ -169,6 +170,7 @@ struct aic31xx_priv {
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
struct snd_soc_jack *jack;
+ u32 sysclk_id;
unsigned int sysclk;
u8 p_div;
int rate_div_line;
@@ -180,6 +182,7 @@ struct aic31xx_priv {
struct aic31xx_rate_divs {
u32 mclk_p;
u32 rate;
+ u8 pll_r;
u8 pll_j;
u16 pll_d;
u16 dosr;
@@ -192,51 +195,71 @@ struct aic31xx_rate_divs {
/* ADC dividers can be disabled by configuring them to 0 */
static const struct aic31xx_rate_divs aic31xx_divs[] = {
- /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
+ /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */
/* 8k rate */
- {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2},
- {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3},
- {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2},
+ { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2},
+ {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2},
+ {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3},
+ {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2},
/* 11.025k rate */
- {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2},
- {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3},
- {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2},
+ { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3},
+ {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2},
+ {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3},
+ {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2},
/* 16k rate */
- {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2},
- {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3},
- {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2},
+ { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3},
+ { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3},
+ {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2},
+ {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3},
+ {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2},
/* 22.05k rate */
- {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2},
- {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3},
- {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2},
+ { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3},
+ { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3},
+ {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2},
+ {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3},
+ {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2},
/* 32k rate */
- {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2},
- {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3},
- {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2},
+ { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2},
+ { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2},
+ {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2},
+ {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3},
+ {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2},
/* 44.1k rate */
- {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2},
- {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3},
- {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2},
+ { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2},
+ { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2},
+ {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2},
+ {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3},
+ {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2},
/* 48k rate */
- {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2},
- {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4},
- {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2},
+ { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2},
+ { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2},
+ {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2},
+ {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4},
+ {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2},
/* 88.2k rate */
- {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2},
- {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3},
- {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2},
+ { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2},
+ { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2},
+ {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2},
+ {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3},
+ {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2},
/* 96k rate */
- {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2},
- {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4},
- {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2},
+ { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2},
+ { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2},
+ {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2},
+ {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4},
+ {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2},
/* 176.4k rate */
- {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2},
- {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3},
- {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2},
+ { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2},
+ {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2},
+ {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3},
+ {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2},
/* 192k rate */
- {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2},
- {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4},
- {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2},
+ { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2},
+ {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4},
+ {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2},
};
static const char * const ldac_in_text[] = {
@@ -888,7 +911,7 @@ static int aic31xx_setup_pll(struct snd_soc_component *component,
/* PLL configuration */
snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
- (aic31xx->p_div << 4) | 0x01);
+ (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r);
snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
snd_soc_component_write(component, AIC31XX_PLLDMSB,
@@ -941,6 +964,7 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
u8 data = 0;
dev_dbg(component->dev, "## %s: width %d rate %d\n",
@@ -972,6 +996,16 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream,
AIC31XX_IFACE1_DATALEN_MASK,
data);
+ /*
+ * If BCLK is used as PLL input, the sysclk is determined by the hw
+ * params. So it must be updated here to match the input frequency.
+ */
+ if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) {
+ aic31xx->sysclk = params_rate(params) * params_width(params) *
+ params_channels(params);
+ aic31xx->p_div = 1;
+ }
+
return aic31xx_setup_pll(component, params);
}
@@ -1156,6 +1190,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
clk_id << AIC31XX_PLL_CLKIN_SHIFT);
+ aic31xx->sysclk_id = clk_id;
aic31xx->sysclk = freq;
return 0;
@@ -1645,11 +1680,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset",
GPIOD_OUT_LOW);
- if (IS_ERR(aic31xx->gpio_reset)) {
- if (PTR_ERR(aic31xx->gpio_reset) != -EPROBE_DEFER)
- dev_err(aic31xx->dev, "not able to acquire gpio\n");
- return PTR_ERR(aic31xx->gpio_reset);
- }
+ if (IS_ERR(aic31xx->gpio_reset))
+ return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset),
+ "not able to acquire gpio\n");
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
aic31xx->supplies[i].supply = aic31xx_supply_names[i];
@@ -1657,12 +1690,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
ret = devm_regulator_bulk_get(aic31xx->dev,
ARRAY_SIZE(aic31xx->supplies),
aic31xx->supplies);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(aic31xx->dev,
- "Failed to request supplies: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n");
aic31xx_configure_ocmv(aic31xx);
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index 2513922a0292..80d062578fb5 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -118,7 +118,7 @@ struct aic31xx_pdata {
#define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2)
#define AIC31XX_PLL_CLKIN_SHIFT (2)
#define AIC31XX_PLL_CLKIN_MCLK 0x00
-#define AIC31XX_PLL_CLKIN_BCKL 0x01
+#define AIC31XX_PLL_CLKIN_BCLK 0x01
#define AIC31XX_PLL_CLKIN_GPIO1 0x02
#define AIC31XX_PLL_CLKIN_DIN 0x03
#define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0)
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 405128ccb4b0..7488a150a138 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1022,6 +1022,52 @@ static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
return plug_type;
}
+static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
+{
+ int hs_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hs_threshold = mbhc->cfg->hs_thr;
+ else
+ hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
+ WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hs_threshold;
+}
+
+static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
+{
+ bool is_spl_hs = false;
+ int output_mv, hs_threshold, hph_threshold;
+
+ if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ return false;
+
+ /* Bump up MIC_BIAS2 to 2.7V */
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
+ usleep_range(10000, 10100);
+
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+ hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
+ hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
+
+ if (!(output_mv > hs_threshold || output_mv < hph_threshold))
+ is_spl_hs = true;
+
+ /* Back MIC_BIAS2 to 1.8v if the type is not special headset */
+ if (!is_spl_hs) {
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
+ /* Add 10ms delay for micbias to settle */
+ usleep_range(10000, 10100);
+ }
+
+ return is_spl_hs;
+}
+
static void wcd_correct_swch_plug(struct work_struct *work)
{
struct wcd_mbhc *mbhc;
@@ -1029,12 +1075,14 @@ static void wcd_correct_swch_plug(struct work_struct *work)
enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
unsigned long timeout;
int pt_gnd_mic_swap_cnt = 0;
- int output_mv, cross_conn, hs_threshold, try = 0;
+ int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
+ bool is_spl_hs = false;
bool is_pa_on;
mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
component = mbhc->component;
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
/* Mask ADC COMPLETE interrupt */
@@ -1097,6 +1145,16 @@ correct_plug_type:
plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
+ if (output_mv > hs_threshold && !is_spl_hs) {
+ is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+
+ if (is_spl_hs) {
+ hs_threshold *= wcd_mbhc_get_micbias(mbhc);
+ hs_threshold /= micbias_mv;
+ }
+ }
+
if ((output_mv <= hs_threshold) && !is_pa_on) {
/* Check for cross connection*/
cross_conn = wcd_check_cross_conn(mbhc);
@@ -1122,14 +1180,19 @@ correct_plug_type:
}
}
- if (output_mv > hs_threshold) /* cable is extension cable */
+ /* cable is extension cable */
+ if (output_mv > hs_threshold || mbhc->force_linein)
plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
}
wcd_mbhc_bcs_enable(mbhc, plug_type, true);
- if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH)
- wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
+ if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
+ if (is_spl_hs)
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
+ }
wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
@@ -1176,7 +1239,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
struct wcd_mbhc *mbhc = data;
unsigned long timeout;
int adc_threshold, output_mv, retry = 0;
- bool hphpa_on = false;
mutex_lock(&mbhc->lock);
timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
@@ -1210,10 +1272,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
wcd_mbhc_elec_hs_report_unplug(mbhc);
wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
- if (hphpa_on) {
- hphpa_on = false;
- wcd_mbhc_write_field(mbhc, WCD_MBHC_HPH_PA_EN, 3);
- }
exit:
mutex_unlock(&mbhc->lock);
return IRQ_HANDLED;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index bc5d68c53e5a..1e60db4056ad 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -341,7 +341,7 @@ struct wcd9335_codec {
int reset_gpio;
struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY];
- unsigned int rx_port_value;
+ unsigned int rx_port_value[WCD9335_RX_MAX];
unsigned int tx_port_value;
int hph_l_gain;
int hph_r_gain;
@@ -1269,10 +1269,11 @@ static const struct snd_kcontrol_new sb_tx8_mux =
static int slim_rx_mux_get(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc);
- struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev);
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc);
+ struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev);
+ u32 port_id = w->shift;
- ucontrol->value.enumerated.item[0] = wcd->rx_port_value;
+ ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id];
return 0;
}
@@ -1286,9 +1287,9 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc,
struct snd_soc_dapm_update *update = NULL;
u32 port_id = w->shift;
- wcd->rx_port_value = ucontrol->value.enumerated.item[0];
+ wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
- switch (wcd->rx_port_value) {
+ switch (wcd->rx_port_value[port_id]) {
case 0:
list_del_init(&wcd->rx_chs[port_id].list);
break;
@@ -1309,11 +1310,11 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc,
&wcd->dai[AIF4_PB].slim_ch_list);
break;
default:
- dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value);
+ dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]);
goto err;
}
- snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value,
+ snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id],
e, update);
return 0;
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index e63c6b723d76..6c468527fec6 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -3423,7 +3423,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc,
{
struct soc_enum *e = (struct soc_enum *)kc->private_value;
struct snd_soc_component *component;
- int reg, val, ret;
+ int reg, val;
component = snd_soc_dapm_kcontrol_component(kc);
val = ucontrol->value.enumerated.item[0];
@@ -3446,9 +3446,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc,
WCD934X_RX_DLY_ZN_EN_MASK,
WCD934X_RX_DLY_ZN_DISABLE);
- ret = snd_soc_dapm_put_enum_double(kc, ucontrol);
-
- return ret;
+ return snd_soc_dapm_put_enum_double(kc, ucontrol);
}
static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index 67151c7770c6..eff200a07d9f 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -3086,7 +3086,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
int micb_num, bool req_en)
{
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
- int rc, micb_mv;
+ int micb_mv;
if (micb_num != MIC_BIAS_2)
return -EINVAL;
@@ -3100,9 +3100,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv;
- rc = wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
-
- return rc;
+ return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}
static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x,
@@ -4287,7 +4285,7 @@ static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai,
static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = {
.hw_params = wcd938x_codec_hw_params,
.hw_free = wcd938x_codec_free,
- .set_sdw_stream = wcd938x_codec_set_sdw_stream,
+ .set_stream = wcd938x_codec_set_sdw_stream,
};
static struct snd_soc_dai_driver wcd938x_dais[] = {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 6cb01a8e08fb..f3672e3d1703 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -401,7 +401,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
int ret = 0;
mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_write_ctrl(cs_ctl, p, cs_ctl->len);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
@@ -421,7 +421,7 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
if (copy_from_user(cs_ctl->cache, bytes, size))
ret = -EFAULT;
else
- ret = cs_dsp_coeff_write_ctrl(cs_ctl, cs_ctl->cache, size);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size);
mutex_unlock(&cs_ctl->dsp->pwr_lock);
@@ -464,7 +464,7 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
int ret;
mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_read_ctrl(cs_ctl, p, cs_ctl->len);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
@@ -481,7 +481,7 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_read_ctrl(cs_ctl, cs_ctl->cache, size);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);
if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
ret = -EFAULT;
@@ -537,15 +537,20 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
return out;
}
-static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
+static void wm_adsp_ctl_work(struct work_struct *work)
{
+ struct wm_coeff_ctl *ctl = container_of(work,
+ struct wm_coeff_ctl,
+ work);
struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp,
+ struct wm_adsp,
+ cs_dsp);
struct snd_kcontrol_new *kcontrol;
- int ret;
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
if (!kcontrol)
- return -ENOMEM;
+ return;
kcontrol->name = ctl->name;
kcontrol->info = wm_coeff_info;
@@ -571,29 +576,9 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
break;
}
- ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1);
- if (ret < 0)
- goto err_kcontrol;
-
- kfree(kcontrol);
-
- return 0;
+ snd_soc_add_component_controls(dsp->component, kcontrol, 1);
-err_kcontrol:
kfree(kcontrol);
- return ret;
-}
-
-static void wm_adsp_ctl_work(struct work_struct *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);
-
- wmfw_add_ctl(dsp, ctl);
}
static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
@@ -700,7 +685,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
if (len > cs_ctl->len)
return -EINVAL;
- ret = cs_dsp_coeff_write_ctrl(cs_ctl, buf, len);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
if (ret)
return ret;
@@ -739,7 +724,7 @@ int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
if (len > cs_ctl->len)
return -EINVAL;
- return cs_dsp_coeff_read_ctrl(cs_ctl, buf, len);
+ return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len);
}
EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
@@ -911,11 +896,12 @@ 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->cs_dsp.name);
+ if (dsp->preloaded == ucontrol->value.integer.value[0])
+ return 0;
- dsp->preloaded = ucontrol->value.integer.value[0];
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
- if (ucontrol->value.integer.value[0])
+ if (ucontrol->value.integer.value[0] || dsp->toggle_preload)
snd_soc_component_force_enable_pin(component, preload);
else
snd_soc_component_disable_pin(component, preload);
@@ -924,6 +910,13 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
flush_work(&dsp->boot_work);
+ dsp->preloaded = ucontrol->value.integer.value[0];
+
+ if (dsp->toggle_preload) {
+ snd_soc_component_disable_pin(component, preload);
+ snd_soc_dapm_sync(dapm);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
@@ -1448,7 +1441,7 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
int ret, i;
for (i = 0; i < 5; ++i) {
- ret = cs_dsp_coeff_read_ctrl(cs_ctl, &coeff_v1, sizeof(coeff_v1));
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, sizeof(coeff_v1));
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 0e2f113bd342..7f4fabbc6ad3 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -41,6 +41,14 @@ struct wm_adsp {
struct list_head compr_list;
struct list_head buffer_list;
+
+ /*
+ * Flag indicating the preloader widget only needs power toggled
+ * on state change rather than held on for the duration of the
+ * preload, useful for devices that can retain firmware memory
+ * across power down.
+ */
+ bool toggle_preload;
};
#define WM_ADSP1(wname, num) \
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 564b78f3cdd0..0222370ff95d 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1026,7 +1026,7 @@ static const struct snd_soc_dai_ops wsa881x_dai_ops = {
.hw_params = wsa881x_hw_params,
.hw_free = wsa881x_hw_free,
.mute_stream = wsa881x_digital_mute,
- .set_sdw_stream = wsa881x_set_sdw_stream,
+ .set_stream = wsa881x_set_sdw_stream,
};
static struct snd_soc_dai_driver wsa881x_dais[] = {
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
index d20ec1571010..6cae0fb08093 100644
--- a/sound/soc/codecs/zl38060.c
+++ b/sound/soc/codecs/zl38060.c
@@ -589,9 +589,7 @@ static int zl38_spi_probe(struct spi_device *spi)
sizeof(template_chip), GFP_KERNEL);
if (!priv->gpio_chip)
return -ENOMEM;
-#ifdef CONFIG_OF_GPIO
- priv->gpio_chip->of_node = dev->of_node;
-#endif
+ priv->gpio_chip->parent = dev;
err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap);
if (err)
return err;
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 8e05d092790e..10fa38753453 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -311,6 +311,7 @@ config SND_SOC_FSL_ASOC_CARD
select SND_SOC_FSL_ESAI
select SND_SOC_FSL_SAI
select SND_SOC_FSL_SSI
+ select SND_SOC_TLV320AIC31XX
select SND_SOC_WM8994
select MFD_WM8994
help
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 6e6494f9f399..5ee945505281 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -26,6 +26,7 @@
#include "../codecs/wm8962.h"
#include "../codecs/wm8960.h"
#include "../codecs/wm8994.h"
+#include "../codecs/tlv320aic31xx.h"
#define CS427x_SYSCLK_MCLK 0
@@ -629,6 +630,16 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) {
codec_dai_name = "tlv320aic32x4-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) {
+ codec_dai_name = "tlv320dac31xx-hifi";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ priv->dai_link[1].dpcm_capture = 0;
+ priv->dai_link[2].dpcm_capture = 0;
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+ priv->codec_priv.mclk_id = AIC31XX_PLL_CLKIN_BCLK;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
codec_dai_name = "wm8962";
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
@@ -842,8 +853,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
goto asrc_fail;
}
@@ -888,6 +898,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-cs42888", },
{ .compatible = "fsl,imx-audio-cs427x", },
{ .compatible = "fsl,imx-audio-tlv320aic32x4", },
+ { .compatible = "fsl,imx-audio-tlv320aic31xx", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{ .compatible = "fsl,imx-audio-wm8960", },
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 24b41881a68f..d7d1536a4f37 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -19,6 +19,7 @@
#include "fsl_asrc.h"
#define IDEAL_RATIO_DECIMAL_DEPTH 26
+#define DIVIDER_NUM 64
#define pair_err(fmt, ...) \
dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
@@ -101,6 +102,55 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
},
};
+/*
+ * According to RM, the divider range is 1 ~ 8,
+ * prescaler is power of 2 from 1 ~ 128.
+ */
+static int asrc_clk_divider[DIVIDER_NUM] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */
+ 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */
+ 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */
+ 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */
+ 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */
+ 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */
+ 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */
+ 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */
+};
+
+/*
+ * Check if the divider is available for internal ratio mode
+ */
+static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
+{
+ u32 rem, i;
+ u64 n;
+
+ if (div)
+ *div = 0;
+
+ if (clk_rate == 0 || rate == 0)
+ return false;
+
+ n = clk_rate;
+ rem = do_div(n, rate);
+
+ if (div)
+ *div = n;
+
+ if (rem != 0)
+ return false;
+
+ for (i = 0; i < DIVIDER_NUM; i++) {
+ if (n == asrc_clk_divider[i])
+ break;
+ }
+
+ if (i == DIVIDER_NUM)
+ return false;
+
+ return true;
+}
+
/**
* fsl_asrc_sel_proc - Select the pre-processing and post-processing options
* @inrate: input sample rate
@@ -330,12 +380,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
- u32 clk_index[2], div[2], rem[2];
+ u32 clk_index[2], div[2];
u64 clk_rate;
int in, out, channels;
int pre_proc, post_proc;
struct clk *clk;
- bool ideal;
+ bool ideal, div_avail;
if (!config) {
pair_err("invalid pair config\n");
@@ -415,8 +465,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
clk_rate = clk_get_rate(clk);
- rem[IN] = do_div(clk_rate, inrate);
- div[IN] = (u32)clk_rate;
+ div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]);
/*
* The divider range is [1, 1024], defined by the hardware. For non-
@@ -425,7 +474,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
* only result in different converting speeds. So remainder does not
* matter, as long as we keep the divider within its valid range.
*/
- if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
+ if (div[IN] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
return -EINVAL;
@@ -436,13 +485,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
clk = asrc_priv->asrck_clk[clk_index[OUT]];
clk_rate = clk_get_rate(clk);
if (ideal && use_ideal_rate)
- rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
+ div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]);
else
- rem[OUT] = do_div(clk_rate, outrate);
- div[OUT] = clk_rate;
+ div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]);
/* Output divider has the same limitation as the input one */
- if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
+ if (div[OUT] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]);
return -EINVAL;
@@ -621,8 +669,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
clk_index = asrc_priv->clk_map[j][i];
clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
/* Only match a perfect clock source with no remainder */
- if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 &&
- (clk_rate % rate[j]) == 0)
+ if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL))
break;
}
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index 27b4536dce44..ceaecbe3a25e 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -337,4 +337,4 @@ module_platform_driver(fsl_mqs_driver);
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
MODULE_DESCRIPTION("MQS codec driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform: fsl-mqs");
+MODULE_ALIAS("platform:fsl-mqs");
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index 6f06afd23b16..6f8efd838fcc 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -120,7 +120,7 @@ struct imx_card_data {
static struct imx_akcodec_fs_mul ak4458_fs_mul[] = {
/* Normal, < 32kHz */
- { .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, },
+ { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, },
/* Normal, 32kHz */
{ .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, },
/* Normal */
@@ -151,8 +151,8 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = {
* Table 7 - mapping multiplier and speed mode
* Tables 8 & 9 - mapping speed mode and LRCK fs
*/
- { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */
- { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, /* Normal */
+ { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */
+ { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */
{ .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */
{ .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */
{ .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */
@@ -164,7 +164,7 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = {
* (Table 4 from datasheet)
*/
static struct imx_akcodec_fs_mul ak5558_fs_mul[] = {
- { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, },
+ { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, },
{ .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, },
{ .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, },
{ .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, },
@@ -247,13 +247,14 @@ static bool codec_is_akcodec(unsigned int type)
}
static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ int slots, int slot_width)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card);
const struct imx_card_plat_data *plat_data = data->plat_data;
struct dai_link_data *link_data = &data->link_data[rtd->num];
- unsigned int width = link_data->slots * link_data->slot_width;
+ unsigned int width = slots * slot_width;
unsigned int rate = params_rate(params);
int i;
@@ -349,7 +350,7 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream,
/* Set MCLK freq */
if (codec_is_akcodec(plat_data->type))
- mclk_freq = akcodec_get_mclk_rate(substream, params);
+ mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width);
else
mclk_freq = params_rate(params) * slots * slot_width;
/* Use the maximum freq from DSD512 (512*44100 = 22579200) */
@@ -553,8 +554,23 @@ static int imx_card_parse_of(struct imx_card_data *data)
link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1;
/* sai may support mclk/bclk = 1 */
- if (of_find_property(np, "fsl,mclk-equal-bclk", NULL))
+ if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) {
link_data->one2one_ratio = true;
+ } else {
+ int i;
+
+ /*
+ * i.MX8MQ don't support one2one ratio, then
+ * with ak4497 only 16bit case is supported.
+ */
+ for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) {
+ if (ak4497_fs_mul[i].rmin == 705600 &&
+ ak4497_fs_mul[i].rmax == 768000) {
+ ak4497_fs_mul[i].wmin = 32;
+ ak4497_fs_mul[i].wmax = 32;
+ }
+ }
+ }
}
link->cpus->of_node = args.np;
@@ -563,9 +579,8 @@ static int imx_card_parse_of(struct imx_card_data *data)
ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(card->dev, "%s: error getting cpu dai name: %d\n",
- link->name, ret);
+ dev_err_probe(card->dev, ret,
+ "%s: error getting cpu dai name\n", link->name);
goto err;
}
@@ -573,9 +588,8 @@ static int imx_card_parse_of(struct imx_card_data *data)
if (codec) {
ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "%s: codec dai not found: %d\n",
- link->name, ret);
+ dev_err_probe(dev, ret, "%s: codec dai not found\n",
+ link->name);
goto err;
}
@@ -814,11 +828,8 @@ static int imx_card_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
return 0;
}
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index f10359a28800..929f69b758af 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -145,6 +145,8 @@ static int imx_hdmi_probe(struct platform_device *pdev)
data->dai.capture_only = false;
data->dai.init = imx_hdmi_init;
+ put_device(&cpu_pdev->dev);
+
if (of_node_name_eq(cpu_np, "sai")) {
data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 2f1acd011042..8daced42d55e 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -173,9 +173,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
goto fail;
}
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index 6c4dadf60355..4446fba755b9 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -70,8 +70,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
goto end;
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
end:
of_node_put(spdif_np);
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 7eb027238327..2b598af8feef 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -593,10 +593,7 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev)
err:
asoc_simple_clean_reference(card);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
-
- return ret;
+ return dev_err_probe(dev, ret, "parse error\n");
}
EXPORT_SYMBOL_GPL(audio_graph_parse_of);
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index b6049bcfb771..c3947347dda3 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -1238,8 +1238,8 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
err:
devm_kfree(dev, li);
- if ((ret < 0) && (ret != -EPROBE_DEFER))
- dev_err(dev, "parse error %d\n", ret);
+ if (ret < 0)
+ dev_err_probe(dev, ret, "parse error\n");
return ret;
}
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 850e968677f1..a81323d1691d 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -499,57 +499,14 @@ EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
char *prefix)
{
- const unsigned int nb_controls_max = 16;
- const char **strings, *control_name;
- struct snd_kcontrol_new *controls;
- struct device *dev = card->dev;
- unsigned int i, nb_controls;
char prop[128];
- int ret;
if (!prefix)
prefix = "";
snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
- if (!of_property_read_bool(dev->of_node, prop))
- return 0;
-
- strings = devm_kcalloc(dev, nb_controls_max,
- sizeof(*strings), GFP_KERNEL);
- if (!strings)
- return -ENOMEM;
-
- ret = of_property_read_string_array(dev->of_node, prop,
- strings, nb_controls_max);
- if (ret < 0)
- return ret;
-
- nb_controls = (unsigned int)ret;
-
- controls = devm_kcalloc(dev, nb_controls,
- sizeof(*controls), GFP_KERNEL);
- if (!controls)
- return -ENOMEM;
-
- for (i = 0; i < nb_controls; i++) {
- control_name = devm_kasprintf(dev, GFP_KERNEL,
- "%s Switch", strings[i]);
- if (!control_name)
- return -ENOMEM;
-
- controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- controls[i].name = control_name;
- controls[i].info = snd_soc_dapm_info_pin_switch;
- controls[i].get = snd_soc_dapm_get_pin_switch;
- controls[i].put = snd_soc_dapm_put_pin_switch;
- controls[i].private_value = (unsigned long)strings[i];
- }
-
- card->controls = controls;
- card->num_controls = nb_controls;
-
- return 0;
+ return snd_soc_of_parse_pin_switches(card, prop);
}
EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index a3a7990b5cb6..a89d1cfdda32 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -642,8 +642,7 @@ static int asoc_simple_probe(struct platform_device *pdev)
ret = simple_parse_of(priv, li);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
+ dev_err_probe(dev, ret, "parse error\n");
goto err;
}
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
index 85385a771d80..5da4725d9e16 100644
--- a/sound/soc/generic/test-component.c
+++ b/sound/soc/generic/test-component.c
@@ -531,8 +531,7 @@ 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;
+ const struct test_adata *adata = of_device_get_match_data(&pdev->dev);
struct snd_soc_component_driver *cdriv;
struct snd_soc_dai_driver *ddriv;
struct test_dai_name *dname;
@@ -549,7 +548,7 @@ static int test_driver_probe(struct platform_device *pdev)
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)
+ if (!priv || !cdriv || !ddriv || !dname || !adata)
return -EINVAL;
priv->dev = dev;
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index 1bf5d6edbd32..f1f36f15a503 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -451,11 +451,9 @@ static int img_i2s_in_probe(struct platform_device *pdev)
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
i2s->clk_sys = devm_clk_get(dev, "sys");
- if (IS_ERR(i2s->clk_sys)) {
- if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(i2s->clk_sys);
- }
+ if (IS_ERR(i2s->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
+ "Failed to acquire clock 'sys'\n");
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c
index 4f90d36dc7df..28f48ca1508a 100644
--- a/sound/soc/img/img-i2s-out.c
+++ b/sound/soc/img/img-i2s-out.c
@@ -457,25 +457,19 @@ static int img_i2s_out_probe(struct platform_device *pdev)
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
- if (IS_ERR(i2s->rst)) {
- if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(i2s->rst);
- }
+ if (IS_ERR(i2s->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst),
+ "No top level reset found\n");
i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(i2s->clk_sys)) {
- if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(i2s->clk_sys);
- }
+ if (IS_ERR(i2s->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
+ "Failed to acquire clock 'sys'\n");
i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(i2s->clk_ref)) {
- if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(i2s->clk_ref);
- }
+ if (IS_ERR(i2s->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_ref),
+ "Failed to acquire clock 'ref'\n");
i2s->suspend_ch_ctl = devm_kcalloc(dev,
i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c
index ce0f08d3777c..800f247283cd 100644
--- a/sound/soc/img/img-parallel-out.c
+++ b/sound/soc/img/img-parallel-out.c
@@ -229,25 +229,19 @@ static int img_prl_out_probe(struct platform_device *pdev)
prl->base = base;
prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
- if (IS_ERR(prl->rst)) {
- if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(prl->rst);
- }
+ if (IS_ERR(prl->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst),
+ "No top level reset found\n");
prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(prl->clk_sys)) {
- if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(prl->clk_sys);
- }
+ if (IS_ERR(prl->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(prl->clk_sys),
+ "Failed to acquire clock 'sys'\n");
prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(prl->clk_ref)) {
- if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(prl->clk_ref);
- }
+ if (IS_ERR(prl->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(prl->clk_ref),
+ "Failed to acquire clock 'ref'\n");
ret = clk_prepare_enable(prl->clk_sys);
if (ret)
diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c
index 6364eb742f6d..95914d0612fe 100644
--- a/sound/soc/img/img-spdif-in.c
+++ b/sound/soc/img/img-spdif-in.c
@@ -739,11 +739,9 @@ static int img_spdif_in_probe(struct platform_device *pdev)
spdif->base = base;
spdif->clk_sys = devm_clk_get(dev, "sys");
- if (IS_ERR(spdif->clk_sys)) {
- if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(spdif->clk_sys);
- }
+ if (IS_ERR(spdif->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
+ "Failed to acquire clock 'sys'\n");
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c
index 858e1b853820..c3189d9ff72f 100644
--- a/sound/soc/img/img-spdif-out.c
+++ b/sound/soc/img/img-spdif-out.c
@@ -342,25 +342,19 @@ static int img_spdif_out_probe(struct platform_device *pdev)
spdif->base = base;
spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
- if (IS_ERR(spdif->rst)) {
- if (PTR_ERR(spdif->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(spdif->rst);
- }
+ if (IS_ERR(spdif->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst),
+ "No top level reset found\n");
spdif->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(spdif->clk_sys)) {
- if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(spdif->clk_sys);
- }
+ if (IS_ERR(spdif->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
+ "Failed to acquire clock 'sys'\n");
spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(spdif->clk_ref)) {
- if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(spdif->clk_ref);
- }
+ if (IS_ERR(spdif->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_ref),
+ "Failed to acquire clock 'ref'\n");
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c
index fe181c2e51d6..802c0ee63aa2 100644
--- a/sound/soc/img/pistachio-internal-dac.c
+++ b/sound/soc/img/pistachio-internal-dac.c
@@ -161,12 +161,9 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev)
return PTR_ERR(dac->regmap);
dac->supply = devm_regulator_get(dev, "VDD");
- if (IS_ERR(dac->supply)) {
- ret = PTR_ERR(dac->supply);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
- return ret;
- }
+ if (IS_ERR(dac->supply))
+ return dev_err_probe(dev, PTR_ERR(dac->supply),
+ "failed to acquire supply 'VDD-supply'\n");
ret = regulator_enable(dac->supply);
if (ret) {
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 5db2f4865bbb..a56dd48c045f 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -653,10 +653,21 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,
dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
return ret_val;
}
- substream->runtime->delay = str_info->pcm_delay;
return str_info->buffer_ptr;
}
+static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream = substream->runtime->private_data;
+ struct pcm_stream_info *str_info = &stream->stream_info;
+
+ if (sst_get_stream_status(stream) == SST_PLATFORM_INIT)
+ return 0;
+
+ return str_info->pcm_delay;
+}
+
static int sst_soc_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
@@ -695,6 +706,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = {
.open = sst_soc_open,
.trigger = sst_soc_trigger,
.pointer = sst_soc_pointer,
+ .delay = sst_soc_delay,
.compress_ops = &sst_platform_compress_ops,
.pcm_construct = sst_soc_pcm_new,
};
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 2dd5ff7e35ce..34ccefcc30c7 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -467,6 +467,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
(MFD_INTEL_LPSS || COMPILE_TEST)) ||\
(SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST))
select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98390
select SND_SOC_RT1011
select SND_SOC_RT1015
select SND_SOC_RT1015P
@@ -527,6 +528,25 @@ config SND_SOC_INTEL_SOF_ES8336_MACH
Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_INTEL_SOF_NAU8825_MACH
+ tristate "SOF with nau8825 codec in I2S Mode"
+ depends on I2C && ACPI && GPIOLIB
+ depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
+ (MFD_INTEL_LPSS || COMPILE_TEST))
+ select SND_SOC_NAU8825
+ select SND_SOC_RT1015P
+ select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with nau8825 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 9ee8ed864f5d..3ea273d27168 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -22,6 +22,7 @@ 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-sof_nau8825-objs := sof_nau8825.o sof_realtek_common.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
@@ -44,6 +45,7 @@ snd-soc-sof-sdw-objs += sof_sdw.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_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.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/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 0a736308052a..ffd497a5b5a5 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -147,7 +147,7 @@ static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_RATE, 48000);
}
-static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = {
+static const struct snd_soc_ops byt_cht_cx2072x_aif1_ops = {
.startup = byt_cht_cx2072x_aif1_startup,
};
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
index 67b3c4e97864..115c2bcaabd4 100644
--- a/sound/soc/intel/boards/bytcht_nocodec.c
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -93,7 +93,7 @@ static int aif1_startup(struct snd_pcm_substream *substream)
&constraints_48000);
}
-static struct snd_soc_ops aif1_ops = {
+static const struct snd_soc_ops aif1_ops = {
.startup = aif1_startup,
};
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index a0c5f0e9c22a..2ace32c03ec9 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -40,6 +40,8 @@ enum {
BYT_RT5640_NO_INTERNAL_MIC_MAP,
};
+#define RT5640_JD_SRC_EXT_GPIO 0x0f
+
enum {
BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4),
BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4),
@@ -47,6 +49,7 @@ enum {
BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4),
BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4),
BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4),
+ BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4)
};
enum {
@@ -79,6 +82,7 @@ enum {
#define BYT_RT5640_LINEOUT_AS_HP2 BIT(26)
#define BYT_RT5640_HSMIC2_ON_IN1 BIT(27)
#define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28)
+#define BYT_RT5640_USE_AMCR0F28 BIT(29)
#define BYTCR_INPUT_DEFAULTS \
(BYT_RT5640_IN3_MAP | \
@@ -93,6 +97,7 @@ enum {
struct byt_rt5640_private {
struct snd_soc_jack jack;
struct snd_soc_jack jack2;
+ struct rt5640_set_jack_data jack_data;
struct gpio_desc *hsmic_detect;
struct clk *mclk;
struct device *codec_dev;
@@ -597,7 +602,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_OVCD_TH_2000UA |
BYT_RT5640_OVCD_SF_0P75 |
BYT_RT5640_SSP0_AIF1 |
- BYT_RT5640_MCLK_EN),
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_USE_AMCR0F28),
},
{
.matches = {
@@ -624,6 +630,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF2 |
BYT_RT5640_MCLK_EN),
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_EXT_GPIO |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_USE_AMCR0F28),
+ },
{ /* Chuwi Vi8 (CWI506) */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
@@ -1080,9 +1099,11 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev,
}
if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
- props[cnt++] = PROPERTY_ENTRY_U32(
- "realtek,jack-detect-source",
- BYT_RT5640_JDSRC(byt_rt5640_quirk));
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) {
+ props[cnt++] = PROPERTY_ENTRY_U32(
+ "realtek,jack-detect-source",
+ BYT_RT5640_JDSRC(byt_rt5640_quirk));
+ }
props[cnt++] = PROPERTY_ENTRY_U32(
"realtek,over-current-threshold-microamp",
@@ -1109,6 +1130,51 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev,
return ret;
}
+/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */
+static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false };
+
+static const struct acpi_gpio_mapping amcr0f28_gpios[] = {
+ { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 },
+ { }
+};
+
+static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card)
+{
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ struct rt5640_set_jack_data *data = &priv->jack_data;
+ struct acpi_device *adev;
+ int ret = 0;
+
+ adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1);
+ if (!adev) {
+ dev_err(card->dev, "error cannot find AMCR0F28 adev\n");
+ return -ENOENT;
+ }
+
+ data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0);
+ if (data->codec_irq_override < 0) {
+ ret = data->codec_irq_override;
+ dev_err(card->dev, "error %d getting codec IRQ\n", ret);
+ goto put_adev;
+ }
+
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) {
+ acpi_dev_add_driver_gpios(adev, amcr0f28_gpios);
+ data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev),
+ "rt5640-jd", GPIOD_IN, "rt5640-jd");
+ acpi_dev_remove_driver_gpios(adev);
+
+ if (IS_ERR(data->jd_gpio)) {
+ ret = PTR_ERR(data->jd_gpio);
+ dev_err(card->dev, "error %d getting jd GPIO\n", ret);
+ }
+ }
+
+put_adev:
+ acpi_dev_put(adev);
+ return ret;
+}
+
static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
@@ -1244,7 +1310,14 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
}
snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0,
KEY_PLAYPAUSE);
- snd_soc_component_set_jack(component, &priv->jack, NULL);
+
+ if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) {
+ ret = byt_rt5640_get_amcr0f28_settings(card);
+ if (ret)
+ return ret;
+ }
+
+ snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data);
}
if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
@@ -1448,7 +1521,8 @@ static int byt_rt5640_resume(struct snd_soc_card *card)
for_each_card_components(card, component) {
if (!strcmp(component->name, byt_rt5640_codec_name)) {
dev_dbg(component->dev, "re-enabling jack detect after resume\n");
- snd_soc_component_set_jack(component, &priv->jack, NULL);
+ snd_soc_component_set_jack(component, &priv->jack,
+ &priv->jack_data);
break;
}
}
diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c
index efdc4bc4bb1f..5c31ddc0884a 100644
--- a/sound/soc/intel/boards/hda_dsp_common.c
+++ b/sound/soc/intel/boards/hda_dsp_common.c
@@ -68,7 +68,7 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
hpcm->pcm = NULL;
hpcm->device = SNDRV_PCM_INVALID_DEVICE;
dev_warn(card->dev,
- "%s: no PCM in topology for HDMI converter %d\n\n",
+ "%s: no PCM in topology for HDMI converter %d\n",
__func__, i);
}
i++;
diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c
index e66dfe666915..112e89951da0 100644
--- a/sound/soc/intel/boards/sof_maxim_common.c
+++ b/sound/soc/intel/boards/sof_maxim_common.c
@@ -5,6 +5,7 @@
#include <linux/string.h>
#include <sound/pcm.h>
#include <sound/soc.h>
+#include <sound/soc-acpi.h>
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
#include <uapi/sound/asound.h>
@@ -134,6 +135,185 @@ void max_98373_set_codec_conf(struct snd_soc_card *card)
EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
/*
+ * Maxim MAX98390
+ */
+static const struct snd_soc_dapm_route max_98390_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("TL Spk"),
+ SOC_DAPM_PIN_SWITCH("TR Spk"),
+};
+
+static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("TL Spk", NULL),
+ SND_SOC_DAPM_SPK("TR Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = {
+ /* Tweeter speaker */
+ { "TL Spk", NULL, "Tweeter Left BE_OUT" },
+ { "TR Spk", NULL, "Tweeter Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max_98390_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME),
+ .name_prefix = "Tweeter Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME),
+ .name_prefix = "Tweeter Left",
+ },
+};
+
+struct snd_soc_dai_link_component max_98390_components[] = {
+ {
+ .name = MAX_98390_DEV0_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV1_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+};
+EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+struct snd_soc_dai_link_component max_98390_4spk_components[] = {
+ {
+ .name = MAX_98390_DEV0_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV1_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV2_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV3_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+};
+EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+static int max_98390_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;
+ int i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (i >= ARRAY_SIZE(max_98390_4spk_components)) {
+ dev_err(codec_dai->dev, "invalid codec index %d\n", i);
+ return -ENODEV;
+ }
+
+ if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) {
+ /* DEV0 tdm slot configuration Right */
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32);
+ }
+ if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) {
+ /* DEV1 tdm slot configuration Left */
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32);
+ }
+
+ if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) {
+ /* DEVi2 tdm slot configuration Tweeter Right */
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32);
+ }
+ if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) {
+ /* DEV3 tdm slot configuration Tweeter Left */
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32);
+ }
+ }
+ return 0;
+}
+
+int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ /* add regular speakers dapm route */
+ ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes,
+ ARRAY_SIZE(max_98390_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret);
+ return ret;
+ }
+
+ /* add widgets/controls/dapm for tweeter speakers */
+ if (acpi_dev_present("MX98390", "3", -1)) {
+ ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets,
+ ARRAY_SIZE(max_98390_tt_dapm_widgets));
+
+ if (ret) {
+ dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols,
+ ARRAY_SIZE(max_98390_tt_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes,
+ ARRAY_SIZE(max_98390_tt_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev,
+ "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+const struct snd_soc_ops max_98390_ops = {
+ .hw_params = max_98390_hw_params,
+};
+EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+void max_98390_set_codec_conf(struct snd_soc_card *card, int ch)
+{
+ if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) {
+ card->codec_conf = max_98390_4spk_codec_conf;
+ card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf);
+ } else {
+ card->codec_conf = max_98390_codec_conf;
+ card->num_configs = ARRAY_SIZE(max_98390_codec_conf);
+ }
+}
+EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+/*
* Maxim MAX98357A/MAX98360A
*/
static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h
index 3ff5e8fec4de..7a8c53049e4d 100644
--- a/sound/soc/intel/boards/sof_maxim_common.h
+++ b/sound/soc/intel/boards/sof_maxim_common.h
@@ -25,6 +25,22 @@ void max_98373_set_codec_conf(struct snd_soc_card *card);
int max_98373_trigger(struct snd_pcm_substream *substream, int cmd);
/*
+ * Maxim MAX98390
+ */
+#define MAX_98390_CODEC_DAI "max98390-aif1"
+#define MAX_98390_DEV0_NAME "i2c-MX98390:00"
+#define MAX_98390_DEV1_NAME "i2c-MX98390:01"
+#define MAX_98390_DEV2_NAME "i2c-MX98390:02"
+#define MAX_98390_DEV3_NAME "i2c-MX98390:03"
+
+extern struct snd_soc_dai_link_component max_98390_components[2];
+extern struct snd_soc_dai_link_component max_98390_4spk_components[4];
+extern const struct snd_soc_ops max_98390_ops;
+
+void max_98390_set_codec_conf(struct snd_soc_card *card, int ch);
+int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd);
+
+/*
* Maxim MAX98357A/MAX98360A
*/
#define MAX_98357A_CODEC_DAI "HiFi"
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
new file mode 100644
index 000000000000..33de043b66c6
--- /dev/null
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+// Copyright(c) 2021 Nuvoton Corporation.
+
+/*
+ * Intel SOF Machine Driver with Nuvoton headphone codec NAU8825
+ * and speaker codec RT1019P MAX98360a or MAX98373
+ */
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/sof.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/nau8825.h"
+#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
+#include "sof_realtek_common.h"
+#include "sof_maxim_common.h"
+
+#define NAME_SIZE 32
+
+#define SOF_NAU8825_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
+#define SOF_NAU8825_SSP_CODEC_MASK (GENMASK(2, 0))
+#define SOF_SPEAKER_AMP_PRESENT BIT(3)
+#define SOF_NAU8825_SSP_AMP_SHIFT 4
+#define SOF_NAU8825_SSP_AMP_MASK (GENMASK(6, 4))
+#define SOF_NAU8825_SSP_AMP(quirk) \
+ (((quirk) << SOF_NAU8825_SSP_AMP_SHIFT) & SOF_NAU8825_SSP_AMP_MASK)
+#define SOF_NAU8825_NUM_HDMIDEV_SHIFT 7
+#define SOF_NAU8825_NUM_HDMIDEV_MASK (GENMASK(9, 7))
+#define SOF_NAU8825_NUM_HDMIDEV(quirk) \
+ (((quirk) << SOF_NAU8825_NUM_HDMIDEV_SHIFT) & SOF_NAU8825_NUM_HDMIDEV_MASK)
+
+/* BT audio offload: reserve 3 bits for future */
+#define SOF_BT_OFFLOAD_SSP_SHIFT 10
+#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(12, 10))
+#define SOF_BT_OFFLOAD_SSP(quirk) \
+ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
+#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(13)
+#define SOF_RT1019P_SPEAKER_AMP_PRESENT BIT(14)
+#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(15)
+#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(16)
+
+static unsigned long sof_nau8825_quirk = SOF_NAU8825_SSP_CODEC(0);
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+struct sof_card_private {
+ struct clk *mclk;
+ struct snd_soc_jack sof_headset;
+ struct list_head hdmi_pcm_list;
+};
+
+static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = rtd->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+ struct snd_soc_jack *jack;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ 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,
+ &ctx->sof_headset, NULL, 0);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ jack = &ctx->sof_headset;
+
+ 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(component, jack, NULL);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+};
+
+static void sof_nau8825_codec_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_nau8825_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);
+ int clk_freq, ret;
+
+ clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
+
+ if (clk_freq <= 0) {
+ dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
+ return -EINVAL;
+ }
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops sof_nau8825_ops = {
+ .hw_params = sof_nau8825_hw_params,
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:00:1f.3"
+ }
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+ struct sof_hdmi_pcm *pcm;
+ int err;
+
+ if (list_empty(&ctx->hdmi_pcm_list))
+ return -EINVAL;
+
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
+
+ if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) {
+ /* Disable Left and Right Spk pin after boot */
+ snd_soc_dapm_disable_pin(dapm, "Left Spk");
+ snd_soc_dapm_disable_pin(dapm, "Right Spk");
+ err = snd_soc_dapm_sync(dapm);
+ if (err < 0)
+ return err;
+ }
+
+ return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_kcontrol_new speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* other jacks */
+ { "MIC", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route speaker_map[] = {
+ /* speaker */
+ { "Spk", NULL, "Speaker" },
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, speaker_widgets,
+ ARRAY_SIZE(speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, speaker_controls,
+ ARRAY_SIZE(speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map,
+ ARRAY_SIZE(speaker_map));
+
+ if (ret)
+ dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
+ return ret;
+}
+
+static int dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->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);
+ /* Don't need to add routes if widget addition failed */
+ 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;
+}
+
+/* sof audio machine driver for nau8825 codec */
+static struct snd_soc_card sof_audio_card_nau8825 = {
+ .name = "nau8825", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static struct snd_soc_dai_link_component nau8825_component[] = {
+ {
+ .name = "i2c-10508825:00",
+ .dai_name = "nau8825-hifi",
+ }
+};
+
+static struct snd_soc_dai_link_component dmic_component[] = {
+ {
+ .name = "dmic-codec",
+ .dai_name = "dmic-hifi",
+ }
+};
+
+static struct snd_soc_dai_link_component rt1019p_component[] = {
+ {
+ .name = "RTL1019:00",
+ .dai_name = "HiFi",
+ }
+};
+
+static struct snd_soc_dai_link_component dummy_component[] = {
+ {
+ .name = "snd-soc-dummy",
+ .dai_name = "snd-soc-dummy-dai",
+ }
+};
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+ int ssp_codec,
+ int ssp_amp,
+ int dmic_be_num,
+ int hdmi_num)
+{
+ struct snd_soc_dai_link_component *idisp_components;
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link *links;
+ int i, id = 0;
+
+ links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
+ sof_audio_card_nau8825.num_links, GFP_KERNEL);
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
+ sof_audio_card_nau8825.num_links, 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 = nau8825_component;
+ links[id].num_codecs = ARRAY_SIZE(nau8825_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_nau8825_codec_init;
+ links[id].exit = sof_nau8825_codec_exit;
+ links[id].ops = &sof_nau8825_ops;
+ 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;
+ }
+ }
+
+ 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;
+ 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++;
+ }
+
+ /* speaker amp */
+ if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d-Codec", ssp_amp);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ if (sof_nau8825_quirk & SOF_RT1019P_SPEAKER_AMP_PRESENT) {
+ links[id].codecs = rt1019p_component;
+ links[id].num_codecs = ARRAY_SIZE(rt1019p_component);
+ links[id].init = speaker_codec_init;
+ } else if (sof_nau8825_quirk &
+ SOF_MAX98373_SPEAKER_AMP_PRESENT) {
+ links[id].codecs = max_98373_components;
+ links[id].num_codecs = ARRAY_SIZE(max_98373_components);
+ links[id].init = max_98373_spk_codec_init;
+ links[id].ops = &max_98373_ops;
+ /* feedback stream */
+ links[id].dpcm_capture = 1;
+ } else if (sof_nau8825_quirk &
+ SOF_MAX98360A_SPEAKER_AMP_PRESENT) {
+ max_98360a_dai_link(&links[id]);
+ } else {
+ goto devm_err;
+ }
+
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].dpcm_playback = 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_amp);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+ id++;
+ }
+
+ /* BT audio offload */
+ if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
+ int port = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
+ SOF_BT_OFFLOAD_SSP_SHIFT;
+
+ links[id].id = id;
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin", port);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
+ if (!links[id].name)
+ goto devm_err;
+ links[id].codecs = dummy_component;
+ links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].dpcm_playback = 1;
+ links[id].dpcm_capture = 1;
+ links[id].no_pcm = 1;
+ links[id].num_cpus = 1;
+ }
+
+ return links;
+devm_err:
+ return NULL;
+}
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_links;
+ struct snd_soc_acpi_mach *mach;
+ struct sof_card_private *ctx;
+ int dmic_be_num, hdmi_num;
+ int ret, ssp_amp, ssp_codec;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ mach = pdev->dev.platform_data;
+
+ /* A speaker amp might not be present when the quirk claims one is.
+ * Detect this via whether the machine driver match includes quirk_data.
+ */
+ if ((sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data)
+ sof_nau8825_quirk &= ~SOF_SPEAKER_AMP_PRESENT;
+
+ dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk);
+
+ /* default number of DMIC DAI's */
+ dmic_be_num = 2;
+ hdmi_num = (sof_nau8825_quirk & SOF_NAU8825_NUM_HDMIDEV_MASK) >>
+ SOF_NAU8825_NUM_HDMIDEV_SHIFT;
+ /* default number of HDMI DAI's */
+ if (!hdmi_num)
+ hdmi_num = 3;
+
+ ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >>
+ SOF_NAU8825_SSP_AMP_SHIFT;
+
+ ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK;
+
+ /* compute number of dai links */
+ sof_audio_card_nau8825.num_links = 1 + dmic_be_num + hdmi_num;
+
+ if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT)
+ sof_audio_card_nau8825.num_links++;
+
+ if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT)
+ max_98373_set_codec_conf(&sof_audio_card_nau8825);
+
+ if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
+ sof_audio_card_nau8825.num_links++;
+
+ dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp,
+ dmic_be_num, hdmi_num);
+ if (!dai_links)
+ return -ENOMEM;
+
+ sof_audio_card_nau8825.dai_link = dai_links;
+
+ INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
+ sof_audio_card_nau8825.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_nau8825);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "sof_nau8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_NAU8825_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+
+ },
+ {
+ .name = "adl_rt1019p_nau8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT1019P_SPEAKER_AMP_PRESENT |
+ SOF_NAU8825_SSP_AMP(2) |
+ SOF_NAU8825_NUM_HDMIDEV(4)),
+ },
+ {
+ .name = "adl_max98373_nau8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98373_SPEAKER_AMP_PRESENT |
+ SOF_NAU8825_SSP_AMP(1) |
+ SOF_NAU8825_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ /* The limitation of length of char array, shorten the name */
+ .name = "adl_mx98360a_nau8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+ SOF_NAU8825_SSP_AMP(1) |
+ SOF_NAU8825_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .driver = {
+ .name = "sof_nau8825",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c
index 2ec34f8df9e1..4cf131310ad3 100644
--- a/sound/soc/intel/boards/sof_realtek_common.c
+++ b/sound/soc/intel/boards/sof_realtek_common.c
@@ -12,12 +12,13 @@
#include <sound/soc-dapm.h>
#include <uapi/sound/asound.h>
#include "../../codecs/rt1011.h"
+#include "../../codecs/rt1015.h"
#include "sof_realtek_common.h"
/*
* Current only 2-amp configuration is supported for rt1011
*/
-static const struct snd_soc_dapm_route rt1011_dapm_routes[] = {
+static const struct snd_soc_dapm_route speaker_map_lr[] = {
/* speaker */
{ "Left Spk", NULL, "Left SPO" },
{ "Right Spk", NULL, "Right SPO" },
@@ -117,8 +118,8 @@ static int rt1011_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = rtd->card;
int ret;
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1011_dapm_routes,
- ARRAY_SIZE(rt1011_dapm_routes));
+ ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map_lr,
+ ARRAY_SIZE(speaker_map_lr));
if (ret)
dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
return ret;
@@ -241,3 +242,115 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card)
card->codec_conf = rt1015p_codec_confs;
card->num_configs = ARRAY_SIZE(rt1015p_codec_confs);
}
+
+/*
+ * RT1015 audio amplifier
+ */
+
+static int rt1015_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;
+ int i, fs = 64, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ params_rate(params) * fs,
+ params_rate(params) * 256);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rt1015_hw_params_pll_and_tdm(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;
+ int i, fs = 100, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ params_rate(params) * fs,
+ params_rate(params) * 256);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ /* rx slot 1 for RT1015_DEV0_NAME */
+ ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0),
+ 0x0, 0x1, 4, 24);
+ if (ret)
+ return ret;
+
+ /* rx slot 2 for RT1015_DEV1_NAME */
+ ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 1),
+ 0x0, 0x2, 4, 24);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops rt1015_ops = {
+ .hw_params = rt1015_hw_params,
+};
+
+static struct snd_soc_codec_conf rt1015_amp_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_dai_link_component rt1015_components[] = {
+ {
+ .name = RT1015_DEV0_NAME,
+ .dai_name = RT1015_CODEC_DAI,
+ },
+ {
+ .name = RT1015_DEV1_NAME,
+ .dai_name = RT1015_CODEC_DAI,
+ },
+};
+
+static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd)
+{
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr,
+ ARRAY_SIZE(speaker_map_lr));
+}
+
+void sof_rt1015_codec_conf(struct snd_soc_card *card)
+{
+ card->codec_conf = rt1015_amp_conf;
+ card->num_configs = ARRAY_SIZE(rt1015_amp_conf);
+}
+
+void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs)
+{
+ link->codecs = rt1015_components;
+ link->num_codecs = ARRAY_SIZE(rt1015_components);
+ link->init = speaker_codec_init_lr;
+ link->ops = &rt1015_ops;
+
+ if (fs == 100)
+ rt1015_ops.hw_params = rt1015_hw_params_pll_and_tdm;
+}
diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h
index cb0b49b2855c..228ac9c08430 100644
--- a/sound/soc/intel/boards/sof_realtek_common.h
+++ b/sound/soc/intel/boards/sof_realtek_common.h
@@ -28,4 +28,11 @@ void sof_rt1011_codec_conf(struct snd_soc_card *card);
void sof_rt1015p_dai_link(struct snd_soc_dai_link *link);
void sof_rt1015p_codec_conf(struct snd_soc_card *card);
+#define RT1015_CODEC_DAI "rt1015-aif"
+#define RT1015_DEV0_NAME "i2c-10EC1015:00"
+#define RT1015_DEV1_NAME "i2c-10EC1015:01"
+
+void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs);
+void sof_rt1015_codec_conf(struct snd_soc_card *card);
+
#endif /* __SOF_REALTEK_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index c41f386b4138..bd6d2e7dea53 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -20,7 +20,6 @@
#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"
@@ -59,6 +58,9 @@
(((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)
+#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24)
+#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25)
+
/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
@@ -179,6 +181,36 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = {
SOF_RT5682_SSP_AMP(2) |
SOF_RT5682_NUM_HDMIDEV(4)),
},
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98390_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(2) |
+ SOF_RT5682_NUM_HDMIDEV(4)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98390_SPEAKER_AMP_PRESENT |
+ SOF_MAX98390_TWEETER_SPEAKER_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+
+ },
{}
};
@@ -367,67 +399,6 @@ static struct snd_soc_ops sof_rt5682_ops = {
.hw_params = sof_rt5682_hw_params,
};
-static int sof_rt1015_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai;
- int i, fs, ret;
-
- if (!snd_soc_card_get_codec_dai(card, "rt1015-aif"))
- return 0;
-
- if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS)
- fs = 100;
- else
- fs = 64;
-
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
- params_rate(params) * fs,
- params_rate(params) * 256);
- if (ret < 0) {
- dev_err(card->dev, "failed to set pll\n");
- return ret;
- }
- /* Configure sysclk for codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
- params_rate(params) * 256,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "failed to set sysclk\n");
- return ret;
- }
-
- if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) {
- if (!strcmp(codec_dai->component->name, "i2c-10EC1015:00")) {
- ret = snd_soc_dai_set_tdm_slot(codec_dai,
- 0x0, 0x1, 4, 24);
- if (ret < 0) {
- dev_err(card->dev, "failed to set tdm slot\n");
- return ret;
- }
- }
-
- if (!strcmp(codec_dai->component->name, "i2c-10EC1015:01")) {
- ret = snd_soc_dai_set_tdm_slot(codec_dai,
- 0x0, 0x2, 4, 24);
- if (ret < 0) {
- dev_err(card->dev, "failed to set tdm slot\n");
- return ret;
- }
- }
- }
- }
-
- return 0;
-}
-
-static struct snd_soc_ops sof_rt1015_ops = {
- .hw_params = sof_rt1015_hw_params,
-};
-
static struct snd_soc_dai_link_component platform_component[] = {
{
/* name might be overridden during probe */
@@ -486,6 +457,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
if (err < 0)
return err;
}
+
return hdac_hdmi_jack_port_init(component, &card->dapm);
}
@@ -517,22 +489,11 @@ static const struct snd_soc_dapm_route sof_map[] = {
{ "IN1P", NULL, "Headset Mic" },
};
-static const struct snd_soc_dapm_route speaker_map_lr[] = {
- { "Left Spk", NULL, "Left SPO" },
- { "Right Spk", NULL, "Right SPO" },
-};
-
static const struct snd_soc_dapm_route dmic_map[] = {
/* digital mics */
{"DMic", NULL, "SoC DMIC"},
};
-static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd)
-{
- return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr,
- ARRAY_SIZE(speaker_map_lr));
-}
-
static int dmic_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
@@ -555,17 +516,6 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
-static struct snd_soc_codec_conf rt1015_amp_conf[] = {
- {
- .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"),
- .name_prefix = "Left",
- },
- {
- .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"),
- .name_prefix = "Right",
- },
-};
-
/* sof audio machine driver for rt5682 codec */
static struct snd_soc_card sof_audio_card_rt5682 = {
.name = "rt5682", /* the sof- prefix is added by the core */
@@ -601,17 +551,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component rt1015_components[] = {
- {
- .name = "i2c-10EC1015:00",
- .dai_name = "rt1015-aif",
- },
- {
- .name = "i2c-10EC1015:01",
- .dai_name = "rt1015-aif",
- },
-};
-
static struct snd_soc_dai_link_component dummy_component[] = {
{
.name = "snd-soc-dummy",
@@ -764,10 +703,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].id = id;
if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) {
- links[id].codecs = rt1015_components;
- links[id].num_codecs = ARRAY_SIZE(rt1015_components);
- links[id].init = speaker_codec_init_lr;
- links[id].ops = &sof_rt1015_ops;
+ sof_rt1015_dai_link(&links[id], (sof_rt5682_quirk &
+ SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64);
} else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) {
sof_rt1015p_dai_link(&links[id]);
} else if (sof_rt5682_quirk &
@@ -784,6 +721,20 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
} else if (sof_rt5682_quirk &
SOF_RT1011_SPEAKER_AMP_PRESENT) {
sof_rt1011_dai_link(&links[id]);
+ } else if (sof_rt5682_quirk &
+ SOF_MAX98390_SPEAKER_AMP_PRESENT) {
+ if (sof_rt5682_quirk &
+ SOF_MAX98390_TWEETER_SPEAKER_PRESENT) {
+ links[id].codecs = max_98390_4spk_components;
+ links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components);
+ } else {
+ links[id].codecs = max_98390_components;
+ links[id].num_codecs = ARRAY_SIZE(max_98390_components);
+ }
+ links[id].init = max_98390_spk_codec_init;
+ links[id].ops = &max_98390_ops;
+ links[id].dpcm_capture = 1;
+
} else {
max_98357a_dai_link(&links[id]);
}
@@ -868,6 +819,10 @@ static int sof_audio_probe(struct platform_device *pdev)
if (acpi_dev_present("RTL5682", NULL, -1))
sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT;
+ /* Detect the headset codec variant to support machines in DMI quirk */
+ 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;
@@ -924,6 +879,14 @@ static int sof_audio_probe(struct platform_device *pdev)
sof_rt1011_codec_conf(&sof_audio_card_rt5682);
else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT)
sof_rt1015p_codec_conf(&sof_audio_card_rt5682);
+ else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) {
+ if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT)
+ max_98390_set_codec_conf(&sof_audio_card_rt5682,
+ ARRAY_SIZE(max_98390_4spk_components));
+ else
+ max_98390_set_codec_conf(&sof_audio_card_rt5682,
+ ARRAY_SIZE(max_98390_components));
+ }
if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
sof_audio_card_rt5682.num_links++;
@@ -935,10 +898,8 @@ static int sof_audio_probe(struct platform_device *pdev)
sof_audio_card_rt5682.dai_link = dai_links;
- if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) {
- sof_audio_card_rt5682.codec_conf = rt1015_amp_conf;
- sof_audio_card_rt5682.num_configs = ARRAY_SIZE(rt1015_amp_conf);
- }
+ if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT)
+ sof_rt1015_codec_conf(&sof_audio_card_rt5682);
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
@@ -1051,6 +1012,17 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_NUM_HDMIDEV(4)),
},
{
+ .name = "adl_max98390_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98390_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
.name = "adl_mx98360_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0) |
@@ -1080,6 +1052,7 @@ MODULE_DESCRIPTION("SOF Audio Machine driver");
MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>");
MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 77219c3f8766..da515eb1ddbe 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -36,8 +36,6 @@ static void log_quirks(struct device *dev)
if (SOF_SSP_GET_PORT(sof_sdw_quirk))
dev_dbg(dev, "SSP port %ld\n",
SOF_SSP_GET_PORT(sof_sdw_quirk));
- if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX)
- dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n");
if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION)
dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n");
}
@@ -64,8 +62,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
},
- .driver_data = (void *)(RT711_JD2 |
- SOF_RT715_DAI_ID_FIX),
+ .driver_data = (void *)RT711_JD2,
},
{
/* early version of SKU 09C6 */
@@ -74,8 +71,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
},
- .driver_data = (void *)(RT711_JD2 |
- SOF_RT715_DAI_ID_FIX),
+ .driver_data = (void *)RT711_JD2,
},
{
.callback = sof_sdw_quirk_cb,
@@ -84,7 +80,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
},
.driver_data = (void *)(RT711_JD2 |
- SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
{
@@ -94,7 +89,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
},
.driver_data = (void *)(RT711_JD2 |
- SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
/* IceLake devices */
@@ -126,8 +120,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
- RT711_JD2 |
- SOF_RT715_DAI_ID_FIX),
+ RT711_JD2),
+ },
+ {
+ /* another SKU of Dell Latitude 9520 */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
},
{
/* Dell XPS 9710 */
@@ -138,7 +141,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
- SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
{
@@ -149,7 +151,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
- SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
{
@@ -188,7 +189,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
SOF_SDW_PCH_DMIC |
- RT711_JD2),
+ RT711_JD1),
},
{
/* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */
@@ -210,7 +211,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
- SOF_RT715_DAI_ID_FIX |
SOF_SDW_FOUR_SPK),
},
{
@@ -220,8 +220,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
- RT711_JD2 |
- SOF_RT715_DAI_ID_FIX),
+ RT711_JD2),
},
/* AlderLake devices */
{
@@ -232,7 +231,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(RT711_JD2_100K |
SOF_SDW_TGL_HDMI |
- SOF_RT715_DAI_ID_FIX |
SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT),
},
@@ -349,7 +347,7 @@ int sdw_prepare(struct snd_pcm_substream *substream)
/* Find stream from first CPU DAI */
dai = asoc_rtd_to_cpu(rtd, 0);
- sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
@@ -369,7 +367,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd)
/* Find stream from first CPU DAI */
dai = asoc_rtd_to_cpu(rtd, 0);
- sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
@@ -408,7 +406,7 @@ int sdw_hw_free(struct snd_pcm_substream *substream)
/* Find stream from first CPU DAI */
dai = asoc_rtd_to_cpu(rtd, 0);
- sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
@@ -431,26 +429,13 @@ static const struct snd_soc_ops sdw_ops = {
.shutdown = sdw_shutdown,
};
-static int sof_sdw_mic_codec_mockup_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback)
-{
- /*
- * force DAI link to use same ID as RT715 and DMIC
- * to reuse topologies
- */
- dai_links->id = SDW_DMIC_DAI_ID;
- return 0;
-}
-
static struct sof_sdw_codec_info codec_info_list[] = {
{
.part_id = 0x700,
.direction = {true, true},
.dai_name = "rt700-aif1",
.init = sof_sdw_rt700_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0x711,
@@ -459,6 +444,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dai_name = "rt711-sdca-aif1",
.init = sof_sdw_rt711_sdca_init,
.exit = sof_sdw_rt711_sdca_exit,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0x711,
@@ -467,6 +453,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dai_name = "rt711-aif1",
.init = sof_sdw_rt711_init,
.exit = sof_sdw_rt711_exit,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0x1308,
@@ -475,12 +462,14 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dai_name = "rt1308-aif",
.ops = &sof_sdw_rt1308_i2s_ops,
.init = sof_sdw_rt1308_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x1316,
.direction = {true, true},
.dai_name = "rt1316-aif",
.init = sof_sdw_rt1316_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x714,
@@ -489,6 +478,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_sdca_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_MIC,
},
{
.part_id = 0x715,
@@ -497,6 +487,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_sdca_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_MIC,
},
{
.part_id = 0x714,
@@ -505,6 +496,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_MIC,
},
{
.part_id = 0x715,
@@ -513,6 +505,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_MIC,
},
{
.part_id = 0x8373,
@@ -520,12 +513,14 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dai_name = "max98373-aif1",
.init = sof_sdw_mx8373_init,
.codec_card_late_probe = sof_sdw_mx8373_late_probe,
+ .codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x5682,
.direction = {true, true},
.dai_name = "rt5682-sdw",
.init = sof_sdw_rt5682_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0xaaaa, /* generic codec mockup */
@@ -533,6 +528,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.direction = {true, true},
.dai_name = "sdw-mockup-aif1",
.init = NULL,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0xaa55, /* headset codec mockup */
@@ -540,6 +536,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.direction = {true, true},
.dai_name = "sdw-mockup-aif1",
.init = NULL,
+ .codec_type = SOF_SDW_CODEC_TYPE_JACK,
},
{
.part_id = 0x55aa, /* amplifier mockup */
@@ -547,13 +544,14 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.direction = {true, false},
.dai_name = "sdw-mockup-aif1",
.init = NULL,
+ .codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x5555,
.version_id = 0,
.direction = {false, true},
.dai_name = "sdw-mockup-aif1",
- .init = sof_sdw_mic_codec_mockup_init,
+ .codec_type = SOF_SDW_CODEC_TYPE_MIC,
},
};
@@ -601,10 +599,11 @@ static inline int find_codec_info_acpi(const u8 *acpi_id)
* Since some sdw slaves may be aggregated, the CPU DAI number
* may be larger than the number of BE dailinks.
*/
-static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links,
+static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links,
int *sdw_be_num, int *sdw_cpu_dai_num)
{
const struct snd_soc_acpi_link_adr *link;
+ int _codec_type = SOF_SDW_CODEC_TYPE_JACK;
bool group_visited[SDW_MAX_GROUPS];
bool no_aggregation;
int i;
@@ -630,6 +629,12 @@ static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links,
if (codec_index < 0)
return codec_index;
+ if (codec_info_list[codec_index].codec_type < _codec_type)
+ dev_warn(dev,
+ "Unexpected address table ordering. Expected order: jack -> amp -> mic\n");
+
+ _codec_type = codec_info_list[codec_index].codec_type;
+
endpoint = link->adr_d->endpoints;
/* count DAI number for playback and capture */
@@ -888,14 +893,14 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
}
static int create_sdw_dailink(struct snd_soc_card *card,
- struct device *dev, int *be_index,
+ struct device *dev, int *link_index,
struct snd_soc_dai_link *dai_links,
int sdw_be_num, int sdw_cpu_dai_num,
struct snd_soc_dai_link_component *cpus,
const struct snd_soc_acpi_link_adr *link,
int *cpu_id, bool *group_generated,
struct snd_soc_codec_conf *codec_conf,
- int codec_count,
+ int codec_count, int *link_id,
int *codec_conf_index,
bool *ignore_pch_dmic)
{
@@ -953,6 +958,19 @@ static int create_sdw_dailink(struct snd_soc_card *card,
if (codec_info_list[codec_index].ignore_pch_dmic)
*ignore_pch_dmic = true;
+ /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */
+ if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP &&
+ *link_id < SDW_AMP_DAI_ID)
+ *link_id = SDW_AMP_DAI_ID;
+
+ /*
+ * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to
+ * keep sdw DMIC and HDMI setting static in UCM
+ */
+ if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC &&
+ *link_id < SDW_DMIC_DAI_ID)
+ *link_id = SDW_DMIC_DAI_ID;
+
cpu_dai_index = *cpu_id;
for_each_pcm_streams(stream) {
char *name, *cpu_name;
@@ -991,8 +1009,12 @@ static int create_sdw_dailink(struct snd_soc_card *card,
cpus[cpu_dai_index++].dai_name = cpu_name;
}
- if (*be_index >= sdw_be_num) {
- dev_err(dev, " invalid be dai index %d", *be_index);
+ /*
+ * We create sdw dai links at first stage, so link index should
+ * not be larger than sdw_be_num
+ */
+ if (*link_index >= sdw_be_num) {
+ dev_err(dev, "invalid dai link index %d", *link_index);
return -EINVAL;
}
@@ -1003,18 +1025,19 @@ static int create_sdw_dailink(struct snd_soc_card *card,
playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
- init_dai_link(dev, dai_links + *be_index, *be_index, name,
+ init_dai_link(dev, dai_links + *link_index, (*link_id)++, name,
playback, capture,
cpus + *cpu_id, cpu_dai_num,
codecs, codec_num,
NULL, &sdw_ops);
+
/*
* SoundWire DAILINKs use 'stream' functions and Bank Switch operations
* based on wait_for_completion(), tag them as 'nonatomic'.
*/
- dai_links[*be_index].nonatomic = true;
+ dai_links[*link_index].nonatomic = true;
- ret = set_codec_init_func(card, link, dai_links + (*be_index)++,
+ ret = set_codec_init_func(card, link, dai_links + (*link_index)++,
playback, group_id);
if (ret < 0) {
dev_err(dev, "failed to init codec %d", codec_index);
@@ -1028,17 +1051,6 @@ static int create_sdw_dailink(struct snd_soc_card *card,
return 0;
}
-/*
- * DAI link ID of SSP & DMIC & HDMI are based on last
- * link ID used by sdw link. Since be_id may be changed
- * in init func of sdw codec, it is not equal to be_id
- */
-static inline int get_next_be_id(struct snd_soc_dai_link *links,
- int be_id)
-{
- return links[be_id - 1].id + 1;
-}
-
#define IDISP_CODEC_MASK 0x4
static int sof_card_codec_conf_alloc(struct device *dev,
@@ -1095,7 +1107,7 @@ static int sof_card_dai_links_create(struct device *dev,
bool group_generated[SDW_MAX_GROUPS];
int ssp_codec_index, ssp_mask;
struct snd_soc_dai_link *links;
- int num_links, link_id = 0;
+ int num_links, link_index = 0;
char *name, *cpu_name;
int total_cpu_dai_num;
int sdw_cpu_dai_num;
@@ -1131,7 +1143,7 @@ static int sof_card_dai_links_create(struct device *dev,
ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0;
comp_num = hdmi_num + ssp_num;
- ret = get_sdw_dailink_info(mach_params->links,
+ ret = get_sdw_dailink_info(dev, mach_params->links,
&sdw_be_num, &sdw_cpu_dai_num);
if (ret < 0) {
dev_err(dev, "failed to get sdw link info %d", ret);
@@ -1195,24 +1207,18 @@ static int sof_card_dai_links_create(struct device *dev,
group_generated[endpoint->group_id])
continue;
- ret = create_sdw_dailink(card, dev, &be_id, links, sdw_be_num,
+ ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num,
sdw_cpu_dai_num, cpus, adr_link,
&cpu_id, group_generated,
codec_conf, codec_conf_count,
- &codec_conf_index,
+ &be_id, &codec_conf_index,
&ignore_pch_dmic);
if (ret < 0) {
- dev_err(dev, "failed to create dai link %d", be_id);
- return -ENOMEM;
+ dev_err(dev, "failed to create dai link %d", link_index);
+ return ret;
}
}
- /* non-sdw DAI follows sdw DAI */
- link_id = be_id;
-
- /* get BE ID for non-sdw DAI */
- be_id = get_next_be_id(links, be_id);
-
SSP:
/* SSP */
if (!ssp_num)
@@ -1252,17 +1258,17 @@ SSP:
playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK];
capture = info->direction[SNDRV_PCM_STREAM_CAPTURE];
- init_dai_link(dev, links + link_id, be_id, name,
+ init_dai_link(dev, links + link_index, be_id, name,
playback, capture,
cpus + cpu_id, 1,
ssp_components, 1,
NULL, info->ops);
- ret = info->init(card, NULL, links + link_id, info, 0);
+ ret = info->init(card, NULL, links + link_index, info, 0);
if (ret < 0)
return ret;
- INC_ID(be_id, cpu_id, link_id);
+ INC_ID(be_id, cpu_id, link_index);
}
DMIC:
@@ -1273,21 +1279,21 @@ DMIC:
goto HDMI;
}
cpus[cpu_id].dai_name = "DMIC01 Pin";
- init_dai_link(dev, links + link_id, be_id, "dmic01",
+ init_dai_link(dev, links + link_index, be_id, "dmic01",
0, 1, // DMIC only supports capture
cpus + cpu_id, 1,
dmic_component, 1,
sof_sdw_dmic_init, NULL);
- INC_ID(be_id, cpu_id, link_id);
+ INC_ID(be_id, cpu_id, link_index);
cpus[cpu_id].dai_name = "DMIC16k Pin";
- init_dai_link(dev, links + link_id, be_id, "dmic16k",
+ init_dai_link(dev, links + link_index, be_id, "dmic16k",
0, 1, // DMIC only supports capture
cpus + cpu_id, 1,
dmic_component, 1,
/* don't call sof_sdw_dmic_init() twice */
NULL, NULL);
- INC_ID(be_id, cpu_id, link_id);
+ INC_ID(be_id, cpu_id, link_index);
}
HDMI:
@@ -1325,12 +1331,12 @@ HDMI:
return -ENOMEM;
cpus[cpu_id].dai_name = cpu_name;
- init_dai_link(dev, links + link_id, be_id, name,
+ init_dai_link(dev, links + link_index, be_id, name,
1, 0, // HDMI only supports playback
cpus + cpu_id, 1,
idisp_components + i, 1,
sof_sdw_hdmi_init, NULL);
- INC_ID(be_id, cpu_id, link_id);
+ INC_ID(be_id, cpu_id, link_index);
}
if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
@@ -1354,7 +1360,7 @@ HDMI:
return -ENOMEM;
cpus[cpu_id].dai_name = cpu_name;
- init_dai_link(dev, links + link_id, be_id, name, 1, 1,
+ init_dai_link(dev, links + link_index, be_id, name, 1, 1,
cpus + cpu_id, 1, ssp_components, 1, NULL, NULL);
}
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
index b35f5a9b96f5..e2457738a332 100644
--- a/sound/soc/intel/boards/sof_sdw_common.h
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -15,6 +15,7 @@
#define MAX_NO_PROPS 2
#define MAX_HDMI_NUM 4
+#define SDW_AMP_DAI_ID 2
#define SDW_DMIC_DAI_ID 4
#define SDW_MAX_CPU_DAIS 16
#define SDW_INTEL_BIDIR_PDI_BASE 2
@@ -42,7 +43,6 @@ enum {
#define SOF_SDW_PCH_DMIC BIT(6)
#define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 7)
#define SOF_SSP_GET_PORT(quirk) (((quirk) >> 7) & GENMASK(5, 0))
-#define SOF_RT715_DAI_ID_FIX BIT(13)
#define SOF_SDW_NO_AGGREGATION BIT(14)
/* BT audio offload: reserve 3 bits for future */
@@ -52,9 +52,14 @@ enum {
(((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18)
+#define SOF_SDW_CODEC_TYPE_JACK 0
+#define SOF_SDW_CODEC_TYPE_AMP 1
+#define SOF_SDW_CODEC_TYPE_MIC 2
+
struct sof_sdw_codec_info {
const int part_id;
const int version_id;
+ const int codec_type;
int amp_num;
const u8 acpi_id[ACPI_ID_LEN];
const bool direction[2]; // playback & capture support
diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c
index c8af3780cbc3..7c068dc6b9cf 100644
--- a/sound/soc/intel/boards/sof_sdw_rt715.c
+++ b/sound/soc/intel/boards/sof_sdw_rt715.c
@@ -30,13 +30,6 @@ int sof_sdw_rt715_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback)
{
- /*
- * DAI ID is fixed at SDW_DMIC_DAI_ID for 715 to
- * keep sdw DMIC and HDMI setting static in UCM
- */
- if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX)
- dai_links->id = SDW_DMIC_DAI_ID;
-
dai_links->init = rt715_rtd_init;
return 0;
diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c
index 85d3d8c355cc..ca0cf3db2e4d 100644
--- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c
+++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c
@@ -30,13 +30,6 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback)
{
- /*
- * DAI ID is fixed at SDW_DMIC_DAI_ID for 715-SDCA to
- * keep sdw DMIC and HDMI setting static in UCM
- */
- if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX)
- dai_links->id = SDW_DMIC_DAI_ID;
-
dai_links->init = rt715_sdca_rtd_init;
return 0;
diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c
index 9c5fd18f2600..346bec000306 100644
--- a/sound/soc/intel/catpt/dsp.c
+++ b/sound/soc/intel/catpt/dsp.c
@@ -65,6 +65,7 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan,
{
struct dma_async_tx_descriptor *desc;
enum dma_status status;
+ int ret;
desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size,
DMA_CTRL_ACK);
@@ -77,13 +78,22 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan,
catpt_updatel_shim(cdev, HMDC,
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id),
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id));
- dmaengine_submit(desc);
+
+ ret = dma_submit_error(dmaengine_submit(desc));
+ if (ret) {
+ dev_err(cdev->dev, "submit tx failed: %d\n", ret);
+ goto clear_hdda;
+ }
+
status = dma_wait_for_async_tx(desc);
+ ret = (status == DMA_COMPLETE) ? 0 : -EPROTO;
+
+clear_hdda:
/* regardless of status, disable access to HOST memory in demand mode */
catpt_updatel_shim(cdev, HMDC,
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0);
- return (status == DMA_COMPLETE) ? 0 : -EPROTO;
+ return ret;
}
int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan,
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index ebb27daeb1c7..939a9b801dec 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -259,9 +259,9 @@ static enum catpt_channel_config catpt_get_channel_config(u32 num_channels)
static int catpt_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct catpt_stream_template *template;
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct resource *res;
int ret;
@@ -306,8 +306,8 @@ err_pgtbl:
static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
stream = snd_soc_dai_get_dma_data(dai, substream);
@@ -329,9 +329,9 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
struct catpt_stream_runtime *stream)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct snd_soc_component *component = dai->component;
struct snd_kcontrol *pos;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
const char *name;
int ret;
u32 id = stream->info.stream_hw_id;
@@ -374,12 +374,12 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ struct snd_pcm_runtime *rtm = substream->runtime;
+ struct snd_dma_buffer *dmab;
struct catpt_stream_runtime *stream;
struct catpt_audio_format afmt;
struct catpt_ring_info rinfo;
- struct snd_pcm_runtime *rtm = substream->runtime;
- struct snd_dma_buffer *dmab;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
int ret;
stream = snd_soc_dai_get_dma_data(dai, substream);
@@ -427,8 +427,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
stream = snd_soc_dai_get_dma_data(dai, substream);
if (!stream->allocated)
@@ -444,8 +444,8 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
static int catpt_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
int ret;
stream = snd_soc_dai_get_dma_data(dai, substream);
@@ -467,9 +467,9 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream,
static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
snd_pcm_uframes_t pos;
int ret;
@@ -595,9 +595,8 @@ static int catpt_component_open(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtm = substream->private_data;
- if (rtm->dai_link->no_pcm)
- return 0;
- snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);
+ if (!rtm->dai_link->no_pcm)
+ snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);
return 0;
}
@@ -605,10 +604,10 @@ static snd_pcm_uframes_t
catpt_component_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct catpt_dev *cdev = dev_get_drvdata(component->dev);
- struct catpt_stream_runtime *stream;
struct snd_soc_pcm_runtime *rtm = substream->private_data;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0);
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
u32 pos;
if (rtm->dai_link->no_pcm)
@@ -633,8 +632,8 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
struct snd_soc_dai *dai)
{
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0);
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
struct catpt_ssp_device_format devfmt;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
int ret;
devfmt.iface = dai->driver->id;
@@ -894,8 +893,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct catpt_dev *cdev = dev_get_drvdata(component->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
long *ctlvol = (long *)kcontrol->private_value;
u32 dspvol;
int i;
@@ -926,8 +925,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct catpt_dev *cdev = dev_get_drvdata(component->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
long *ctlvol = (long *)kcontrol->private_value;
int ret, i;
@@ -1002,8 +1001,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
- struct catpt_dev *cdev = dev_get_drvdata(component->dev);
struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
bool mute;
int ret;
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 b61a778a9d26..f32bcb2b2e09 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -374,6 +374,16 @@ static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = {
.codecs = {"10EC5682", "RTL5682"},
};
+static const struct snd_soc_acpi_codecs adl_rt1019p_amp = {
+ .num_codecs = 1,
+ .codecs = {"RTL1019"}
+};
+
+static const struct snd_soc_acpi_codecs adl_max98390_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98390"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
{
.comp_ids = &adl_rt5682_rt5682s_hp,
@@ -399,6 +409,44 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
.sof_fw_filename = "sof-adl.ri",
.sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg",
},
+ {
+ .id = "10508825",
+ .drv_name = "adl_rt1019p_nau8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_rt1019p_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "adl_max98373_nau8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98373_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "adl_mx98360a_nau8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98360a_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "sof_nau8825",
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-nau8825.tplg",
+ },
+ {
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_max98390_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98390_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines);
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 64226072f0ee..2439a574ac2f 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -13,108 +13,6 @@
#include "skl.h"
#include "skl-i2s.h"
-static struct nhlt_specific_cfg *skl_get_specific_cfg(
- struct device *dev, struct nhlt_fmt *fmt,
- u8 no_ch, u32 rate, u16 bps, u8 linktype)
-{
- struct nhlt_specific_cfg *sp_config;
- struct wav_fmt *wfmt;
- struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config;
- int i;
-
- dev_dbg(dev, "Format count =%d\n", fmt->fmt_count);
-
- for (i = 0; i < fmt->fmt_count; i++) {
- wfmt = &fmt_config->fmt_ext.fmt;
- dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
- wfmt->bits_per_sample, wfmt->samples_per_sec);
- if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) {
- /*
- * if link type is dmic ignore rate check as the blob is
- * generic for all rates
- */
- sp_config = &fmt_config->config;
- if (linktype == NHLT_LINK_DMIC)
- return sp_config;
-
- if (wfmt->samples_per_sec == rate)
- return sp_config;
- }
-
- fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
- fmt_config->config.size);
- }
-
- return NULL;
-}
-
-static void dump_config(struct device *dev, u32 instance_id, u8 linktype,
- u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps)
-{
- dev_dbg(dev, "Input configuration\n");
- dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate);
- dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype);
- dev_dbg(dev, "bits_per_sample=%d\n", bps);
-}
-
-static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
- u32 instance_id, u8 link_type, u8 dirn, u8 dev_type)
-{
- dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
- epnt->virtual_bus_id, epnt->linktype,
- epnt->direction, epnt->device_type);
-
- if ((epnt->virtual_bus_id == instance_id) &&
- (epnt->linktype == link_type) &&
- (epnt->direction == dirn)) {
- /* do not check dev_type for DMIC link type */
- if (epnt->linktype == NHLT_LINK_DMIC)
- return true;
-
- if (epnt->device_type == dev_type)
- return true;
- }
-
- return false;
-}
-
-struct nhlt_specific_cfg
-*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type,
- u8 s_fmt, u8 num_ch, u32 s_rate,
- u8 dirn, u8 dev_type)
-{
- struct nhlt_fmt *fmt;
- struct nhlt_endpoint *epnt;
- struct hdac_bus *bus = skl_to_bus(skl);
- struct device *dev = bus->dev;
- struct nhlt_specific_cfg *sp_config;
- struct nhlt_acpi_table *nhlt = skl->nhlt;
- u16 bps = (s_fmt == 16) ? 16 : 32;
- u8 j;
-
- dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
-
- epnt = (struct nhlt_endpoint *)nhlt->desc;
-
- dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);
-
- for (j = 0; j < nhlt->endpoint_count; j++) {
- if (skl_check_ep_match(dev, epnt, instance, link_type,
- dirn, dev_type)) {
- fmt = (struct nhlt_fmt *)(epnt->config.caps +
- epnt->config.size);
- sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
- s_rate, bps, link_type);
- if (sp_config)
- return sp_config;
- }
-
- epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
- }
-
- return NULL;
-}
-
static void skl_nhlt_trim_space(char *trim)
{
char *s = trim;
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 9ecaf6a1e847..55f310e91b55 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -317,6 +317,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
p_params.ch = params_channels(params);
p_params.s_freq = params_rate(params);
p_params.host_dma_id = dma_id;
@@ -405,6 +406,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
struct skl_pipe_params p_params = {0};
p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
p_params.ch = params_channels(params);
p_params.s_freq = params_rate(params);
p_params.stream = substream->stream;
@@ -562,13 +564,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
stream_tag = hdac_stream(link_dev)->stream_tag;
- /* set the stream tag in the codec dai dma params */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
- else
- snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0);
+ /* set the hdac_stream in the codec dai */
+ snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.s_cont = snd_pcm_format_physical_width(params_format(params));
p_params.ch = params_channels(params);
p_params.s_freq = params_rate(params);
p_params.stream = substream->stream;
@@ -1251,7 +1251,6 @@ static int skl_platform_soc_get_time_info(
snd_pcm_gettime(substream->runtime, system_ts);
nsec = timecounter_read(&hstr->tc);
- nsec = div_u64(nsec, 3); /* can be optimized */
if (audio_tstamp_config->report_delay)
nsec = skl_adjust_codec_delay(substream, nsec);
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 89e4231304dd..9bdf020a2b64 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -285,7 +285,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
{
struct skl_module_cfg *m_cfg = w->priv;
int link_type, dir;
- u32 ch, s_freq, s_fmt;
+ u32 ch, s_freq, s_fmt, s_cont;
struct nhlt_specific_cfg *cfg;
u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type);
int fmt_idx = m_cfg->fmt_idx;
@@ -301,7 +301,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
link_type = NHLT_LINK_DMIC;
dir = SNDRV_PCM_STREAM_CAPTURE;
s_freq = m_iface->inputs[0].fmt.s_freq;
- s_fmt = m_iface->inputs[0].fmt.bit_depth;
+ s_fmt = m_iface->inputs[0].fmt.valid_bit_depth;
+ s_cont = m_iface->inputs[0].fmt.bit_depth;
ch = m_iface->inputs[0].fmt.channels;
break;
@@ -310,12 +311,14 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) {
dir = SNDRV_PCM_STREAM_PLAYBACK;
s_freq = m_iface->outputs[0].fmt.s_freq;
- s_fmt = m_iface->outputs[0].fmt.bit_depth;
+ s_fmt = m_iface->outputs[0].fmt.valid_bit_depth;
+ s_cont = m_iface->outputs[0].fmt.bit_depth;
ch = m_iface->outputs[0].fmt.channels;
} else {
dir = SNDRV_PCM_STREAM_CAPTURE;
s_freq = m_iface->inputs[0].fmt.s_freq;
- s_fmt = m_iface->inputs[0].fmt.bit_depth;
+ s_fmt = m_iface->inputs[0].fmt.valid_bit_depth;
+ s_cont = m_iface->inputs[0].fmt.bit_depth;
ch = m_iface->inputs[0].fmt.channels;
}
break;
@@ -325,16 +328,17 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
}
/* update the blob based on virtual bus_id and default params */
- cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
- s_fmt, ch, s_freq, dir, dev_type);
+ cfg = intel_nhlt_get_endpoint_blob(skl->dev, skl->nhlt, m_cfg->vbus_id,
+ link_type, s_fmt, s_cont, ch,
+ s_freq, dir, dev_type);
if (cfg) {
m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size;
m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps;
} else {
dev_err(skl->dev, "Blob NULL for id %x type %d dirn %d\n",
m_cfg->vbus_id, link_type, dir);
- dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d\n",
- ch, s_freq, s_fmt);
+ dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d/%d\n",
+ ch, s_freq, s_fmt, s_cont);
return -EIO;
}
@@ -1849,10 +1853,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt;
/* update the blob based on virtual bus_id*/
- cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
- pipe_fmt->bps, pipe_fmt->channels,
- pipe_fmt->freq, pipe->direction,
- dev_type);
+ cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt,
+ mconfig->vbus_id, link_type,
+ pipe_fmt->bps, params->s_cont,
+ pipe_fmt->channels, pipe_fmt->freq,
+ pipe->direction, dev_type);
if (cfg) {
mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size;
mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps;
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index f0695b2ac5dd..22963634fbea 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -284,6 +284,7 @@ struct skl_pipe_params {
u32 ch;
u32 s_freq;
u32 s_fmt;
+ u32 s_cont;
u8 linktype;
snd_pcm_format_t format;
int link_index;
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 5b1a15e39912..148ddf4cace0 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -439,7 +439,7 @@ static int skl_free(struct hdac_bus *bus)
skl->init_done = 0; /* to be sure */
- snd_hdac_ext_stop_streams(bus);
+ snd_hdac_stop_streams_and_chip(bus);
if (bus->irq >= 0)
free_irq(bus->irq, (void *)bus);
@@ -1096,7 +1096,7 @@ static void skl_shutdown(struct pci_dev *pci)
if (!skl->init_done)
return;
- snd_hdac_ext_stop_streams(bus);
+ snd_hdac_stop_streams_and_chip(bus);
list_for_each_entry(s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
snd_hdac_ext_stream_decouple(bus, stream, false);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 33ed274fc0cb..f55f8b3dbdc3 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -165,10 +165,6 @@ struct skl_dsp_ops {
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
-struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance,
- u8 link_type, u8 s_fmt, u8 num_ch,
- u32 s_rate, u8 dirn, u8 dev_type);
-
int skl_nhlt_update_topology_bin(struct skl_dev *skl);
int skl_init_dsp(struct skl_dev *skl);
int skl_free_dsp(struct skl_dev *skl);
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3b1ddea26a9e..9306b7ca2644 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -205,6 +205,7 @@ config SND_SOC_MT8195_MT6359_RT1019_RT5682
select SND_SOC_MT6359
select SND_SOC_RT1015P
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_DMIC
select SND_SOC_HDMI_CODEC
help
@@ -220,6 +221,7 @@ config SND_SOC_MT8195_MT6359_RT1011_RT5682
select SND_SOC_MT6359
select SND_SOC_RT1011
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_DMIC
select SND_SOC_HDMI_CODEC
help
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index bc3d0466472b..0f178de92a0f 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -1474,9 +1474,7 @@ static struct platform_driver mt2701_afe_pcm_driver = {
.driver = {
.name = "mt2701-audio",
.of_match_table = mt2701_afe_pcm_dt_match,
-#ifdef CONFIG_PM
.pm = &mt2701_afe_pm_ops,
-#endif
},
.probe = mt2701_afe_pcm_dev_probe,
.remove = mt2701_afe_pcm_dev_remove,
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
index 3d68e4726ea2..fb4abec9aa5f 100644
--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -901,9 +901,7 @@ static struct platform_driver mt6797_afe_pcm_driver = {
.driver = {
.name = "mt6797-audio",
.of_match_table = mt6797_afe_pcm_dt_match,
-#ifdef CONFIG_PM
.pm = &mt6797_afe_pm_ops,
-#endif
},
.probe = mt6797_afe_pcm_dev_probe,
.remove = mt6797_afe_pcm_dev_remove,
diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index fc94314bfc02..4cb90da89262 100644
--- a/sound/soc/mediatek/mt8173/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -177,9 +177,9 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
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(codec_node);
+ of_node_put(platform_node);
return ret;
}
@@ -193,9 +193,7 @@ static struct platform_driver mt8173_max98090_driver = {
.driver = {
.name = "mt8173-max98090",
.of_match_table = mt8173_max98090_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_max98090_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 0f28dc2217c0..b55122b99f07 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -215,9 +215,8 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
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(platform_node);
return ret;
}
@@ -231,9 +230,7 @@ static struct platform_driver mt8173_rt5650_rt5514_driver = {
.driver = {
.name = "mtk-rt5650-rt5514",
.of_match_table = mt8173_rt5650_rt5514_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_rt5650_rt5514_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index 077c6ee06780..5716d9299066 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -282,9 +282,8 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
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(platform_node);
return ret;
}
@@ -298,9 +297,7 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
.driver = {
.name = "mtk-rt5650-rt5676",
.of_match_table = mt8173_rt5650_rt5676_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_rt5650_rt5676_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 2cbf679f5c74..fc164f4f95f8 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -320,9 +320,8 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
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(platform_node);
return ret;
}
@@ -336,9 +335,7 @@ static struct platform_driver mt8173_rt5650_driver = {
.driver = {
.name = "mtk-rt5650",
.of_match_table = mt8173_rt5650_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_rt5650_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index 14e77df06b01..86c8a523fe9e 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -1279,9 +1279,7 @@ static struct platform_driver mt8183_afe_pcm_driver = {
.driver = {
.name = "mt8183-audio",
.of_match_table = mt8183_afe_pcm_dt_match,
-#ifdef CONFIG_PM
.pm = &mt8183_afe_pm_ops,
-#endif
},
.probe = mt8183_afe_pcm_dev_probe,
.remove = mt8183_afe_pcm_dev_remove,
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index a4d26a6fc849..718505c75418 100644
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -155,9 +155,9 @@ static const struct snd_soc_ops mt8183_da7219_rt1015_i2s_ops = {
static int mt8183_i2s_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 */
+ /* fix BE i2s format to S32_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, SNDRV_PCM_FORMAT_LAST);
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
@@ -167,9 +167,9 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
static int mt8183_rt1015_i2s_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 */
+ /* fix BE i2s format to S24_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, SNDRV_PCM_FORMAT_LAST);
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
@@ -685,7 +685,6 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
struct snd_soc_dai_link *dai_link;
struct mt8183_da7219_max98357_priv *priv;
struct pinctrl *pinctrl;
- const struct of_device_id *match;
int ret, i;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -695,11 +694,9 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
- if (!match || !match->data)
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card)
return -EINVAL;
-
- card = (struct snd_soc_card *)match->data;
card->dev = &pdev->dev;
hdmi_codec = of_parse_phandle(pdev->dev.of_node,
@@ -781,7 +778,11 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
return ret;
}
- return devm_snd_soc_register_card(&pdev->dev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+ of_node_put(platform_node);
+ of_node_put(hdmi_codec);
+ return ret;
}
#ifdef CONFIG_OF
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index aeb1af86047e..b0ec5ebd4f2d 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -94,11 +94,11 @@ static const struct snd_soc_ops mt8183_mt6358_rt1015_i2s_ops = {
static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
- dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__);
+ dev_dbg(rtd->dev, "%s(), fix format to S32_LE\n", __func__);
- /* fix BE i2s format to 32bit, clean param mask first */
+ /* fix BE i2s format to S32_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, SNDRV_PCM_FORMAT_LAST);
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
return 0;
@@ -107,11 +107,11 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
- dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__);
+ dev_dbg(rtd->dev, "%s(), fix format to S24_LE\n", __func__);
- /* fix BE i2s format to 32bit, clean param mask first */
+ /* fix BE i2s format to S24_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, SNDRV_PCM_FORMAT_LAST);
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
return 0;
@@ -637,7 +637,6 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
struct device_node *platform_node, *ec_codec, *hdmi_codec;
struct snd_soc_dai_link *dai_link;
struct mt8183_mt6358_ts3a227_max98357_priv *priv;
- const struct of_device_id *match;
int ret, i;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -647,11 +646,9 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
- if (!match || !match->data)
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card)
return -EINVAL;
-
- card = (struct snd_soc_card *)match->data;
card->dev = &pdev->dev;
ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0);
@@ -780,7 +777,12 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
__func__, ret);
}
- return devm_snd_soc_register_card(&pdev->dev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+ of_node_put(platform_node);
+ of_node_put(ec_codec);
+ of_node_put(hdmi_codec);
+ return ret;
}
#ifdef CONFIG_OF
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
index 31c280339c50..e1e4ca931551 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -2381,9 +2381,7 @@ static struct platform_driver mt8192_afe_pcm_driver = {
.driver = {
.name = "mt8192-audio",
.of_match_table = mt8192_afe_pcm_dt_match,
-#ifdef CONFIG_PM
.pm = &mt8192_afe_pm_ops,
-#endif
},
.probe = mt8192_afe_pcm_dev_probe,
.remove = mt8192_afe_pcm_dev_remove,
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index a606133951b7..f7daad1bfe1e 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -350,9 +350,9 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
static int mt8192_i2s_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 */
+ /* fix BE i2s format to S24_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, SNDRV_PCM_FORMAT_LAST);
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
@@ -1106,7 +1106,6 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
struct device_node *platform_node, *hdmi_codec;
int ret, i;
struct snd_soc_dai_link *dai_link;
- const struct of_device_id *match;
struct mt8192_mt6359_priv *priv;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -1116,11 +1115,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
- if (!match || !match->data)
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card)
return -EINVAL;
-
- card = (struct snd_soc_card *)match->data;
card->dev = &pdev->dev;
hdmi_codec = of_parse_phandle(pdev->dev.of_node,
@@ -1172,7 +1169,11 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
return ret;
}
- return devm_snd_soc_register_card(&pdev->dev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+ of_node_put(platform_node);
+ of_node_put(hdmi_codec);
+ return ret;
}
#ifdef CONFIG_OF
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c
index 8420b2c71332..c2543f4cffb7 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c
@@ -326,7 +326,7 @@ int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
{
struct mt8195_afe_private *afe_priv = afe->platform_priv;
int i;
- unsigned int clk_array[] = {
+ static const unsigned int clk_array[] = {
MT8195_CLK_SCP_ADSP_AUDIODSP, /* bus clock for infra */
MT8195_CLK_TOP_AUDIO_H_SEL, /* clock for ADSP bus */
MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL, /* bus clock for DRAM access */
@@ -347,7 +347,7 @@ int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
{
struct mt8195_afe_private *afe_priv = afe->platform_priv;
int i;
- unsigned int clk_array[] = {
+ static const unsigned int clk_array[] = {
MT8195_CLK_AUD_A1SYS,
MT8195_CLK_AUD_A1SYS_HP,
MT8195_CLK_AUD_AFE,
@@ -380,11 +380,11 @@ static int mt8195_afe_enable_timing_sys(struct mtk_base_afe *afe)
{
struct mt8195_afe_private *afe_priv = afe->platform_priv;
int i;
- unsigned int clk_array[] = {
+ static const unsigned int clk_array[] = {
MT8195_CLK_AUD_A1SYS,
MT8195_CLK_AUD_A2SYS,
};
- unsigned int cg_array[] = {
+ static const unsigned int cg_array[] = {
MT8195_TOP_CG_A1SYS_TIMING,
MT8195_TOP_CG_A2SYS_TIMING,
MT8195_TOP_CG_26M_TIMING,
@@ -403,11 +403,11 @@ static int mt8195_afe_disable_timing_sys(struct mtk_base_afe *afe)
{
struct mt8195_afe_private *afe_priv = afe->platform_priv;
int i;
- unsigned int clk_array[] = {
+ static const unsigned int clk_array[] = {
MT8195_CLK_AUD_A2SYS,
MT8195_CLK_AUD_A1SYS,
};
- unsigned int cg_array[] = {
+ static const unsigned int cg_array[] = {
MT8195_TOP_CG_26M_TIMING,
MT8195_TOP_CG_A2SYS_TIMING,
MT8195_TOP_CG_A1SYS_TIMING,
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
index 2bb05a828e8d..e425f868476a 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include "mt8195-afe-common.h"
#include "mt8195-afe-clk.h"
@@ -3028,7 +3029,7 @@ static const struct reg_sequence mt8195_afe_reg_defaults[] = {
static const struct reg_sequence mt8195_cg_patch[] = {
{ AUDIO_TOP_CON0, 0xfffffffb },
- { AUDIO_TOP_CON1, 0xfffffffa },
+ { AUDIO_TOP_CON1, 0xfffffff8 },
};
static int mt8195_afe_init_registers(struct mtk_base_afe *afe)
@@ -3061,6 +3062,12 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
int i, irq_id, ret;
struct snd_soc_component *component;
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_err(dev, "failed to assign memory region: %d\n", ret);
+ return ret;
+ }
+
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
index 5d10d2c4c991..151914c873ac 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
@@ -80,8 +80,15 @@ static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
mtk_dai_pcm_o001_mix,
ARRAY_SIZE(mtk_dai_pcm_o001_mix)),
+ SND_SOC_DAPM_SUPPLY("PCM_EN", PCM_INTF_CON1,
+ PCM_INTF_CON1_PCM_EN_SHIFT, 0, NULL, 0),
+
SND_SOC_DAPM_INPUT("PCM1_INPUT"),
SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"),
};
static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
@@ -97,22 +104,18 @@ static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
{"PCM1 Playback", NULL, "O000"},
{"PCM1 Playback", NULL, "O001"},
+ {"PCM1 Playback", NULL, "PCM_EN"},
+ {"PCM1 Playback", NULL, "aud_asrc12"},
+ {"PCM1 Playback", NULL, "aud_pcmif"},
+
+ {"PCM1 Capture", NULL, "PCM_EN"},
+ {"PCM1 Capture", NULL, "aud_asrc11"},
+ {"PCM1 Capture", NULL, "aud_pcmif"},
+
{"PCM1_OUTPUT", NULL, "PCM1 Playback"},
{"PCM1 Capture", NULL, "PCM1_INPUT"},
};
-static void mtk_dai_pcm_enable(struct mtk_base_afe *afe)
-{
- regmap_update_bits(afe->regmap, PCM_INTF_CON1,
- PCM_INTF_CON1_PCM_EN, PCM_INTF_CON1_PCM_EN);
-}
-
-static void mtk_dai_pcm_disable(struct mtk_base_afe *afe)
-{
- regmap_update_bits(afe->regmap, PCM_INTF_CON1,
- PCM_INTF_CON1_PCM_EN, 0x0);
-}
-
static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -207,54 +210,22 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
}
/* dai ops */
-static int mtk_dai_pcm_startup(struct snd_pcm_substream *substream,
- 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;
-
- if (dai->component->active)
- return 0;
-
- mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]);
- mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]);
- mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]);
-
- return 0;
-}
-
-static void mtk_dai_pcm_shutdown(struct snd_pcm_substream *substream,
- 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;
-
- if (dai->component->active)
- return;
-
- mtk_dai_pcm_disable(afe);
-
- mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]);
- mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]);
- mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]);
-}
-
static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- int ret = 0;
+ int ret;
- if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) &&
- snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE))
+ dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
+ __func__, dai->id, substream->stream,
+ dai->playback_widget->active, dai->capture_widget->active);
+
+ if (dai->playback_widget->active || dai->capture_widget->active)
return 0;
ret = mtk_dai_pcm_configure(substream, dai);
if (ret)
return ret;
- mtk_dai_pcm_enable(afe);
-
return 0;
}
@@ -316,8 +287,6 @@ static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
- .startup = mtk_dai_pcm_startup,
- .shutdown = mtk_dai_pcm_shutdown,
.prepare = mtk_dai_pcm_prepare,
.set_fmt = mtk_dai_pcm_set_fmt,
};
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
index e103102d7ef6..5443a29da7b1 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
@@ -18,6 +18,7 @@
#include "../../codecs/rt1011.h"
#include "../../codecs/rt5682.h"
#include "../common/mtk-afe-platform-driver.h"
+#include "mt8195-afe-clk.h"
#include "mt8195-afe-common.h"
#define RT1011_CODEC_DAI "rt1011-aif"
@@ -27,13 +28,14 @@
#define RT5682_CODEC_DAI "rt5682-aif1"
#define RT5682_DEV0_NAME "rt5682.2-001a"
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+#define RT5682S_DEV0_NAME "rt5682s.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;
+ struct clk *i2so1_mclk;
};
static const struct snd_soc_dapm_widget
@@ -84,8 +86,8 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_BCLK1,
- rate * 64, rate * 512);
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
+ rate * 256, rate * 512);
if (ret) {
dev_err(card->dev, "failed to set pll\n");
return ret;
@@ -98,7 +100,7 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 128,
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
SND_SOC_CLOCK_OUT);
}
@@ -327,8 +329,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
struct mt8195_mt6359_rt1011_rt5682_priv *priv =
snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_jack *jack = &priv->headset_jack;
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
int ret;
+ priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
+
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 |
@@ -356,7 +364,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
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 */
+ /* fix BE i2s format to S24_LE, 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);
@@ -421,7 +429,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
SND_SOC_CLOCK_OUT);
}
-static struct snd_soc_ops mt8195_dptx_ops = {
+static const struct snd_soc_ops mt8195_dptx_ops = {
.hw_params = mt8195_dptx_hw_params,
};
@@ -461,7 +469,7 @@ 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 */
+ /* fix BE i2s format to S24_LE, 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);
@@ -562,6 +570,47 @@ static const struct snd_soc_ops mt8195_capture_ops = {
.startup = mt8195_capture_startup,
};
+static int mt8195_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = dapm->component;
+ struct mt8195_mt6359_rt1011_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+
+ /*
+ * It's required to control mclk directly in the set_bias_level_post
+ * function for rt5682 and rt5682s codec, or the unexpected pop happens
+ * at the end of playback.
+ */
+ if (!component ||
+ (strcmp(component->name, RT5682_DEV0_NAME) &&
+ strcmp(component->name, RT5682S_DEV0_NAME)))
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ if (!__clk_is_enabled(priv->i2so1_mclk))
+ return 0;
+
+ clk_disable_unprepare(priv->i2so1_mclk);
+ dev_dbg(card->dev, "Disable i2so1 mclk\n");
+ break;
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(priv->i2so1_mclk);
+ if (ret) {
+ dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(card->dev, "Enable i2so1 mclk\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
enum {
DAI_LINK_DL2_FE,
DAI_LINK_DL3_FE,
@@ -691,14 +740,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
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_DUMMY()),
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_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
@@ -998,6 +1045,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(PCM1_BE),
},
@@ -1039,6 +1087,7 @@ static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = {
.num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes),
.codec_conf = rt1011_amp_conf,
.num_configs = ARRAY_SIZE(rt1011_amp_conf),
+ .set_bias_level_post = mt8195_set_bias_level_post,
};
static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev)
@@ -1046,82 +1095,77 @@ 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;
+ struct device_node *platform_node, *dp_node, *hdmi_node;
+ int is5682s = 0;
int ret, i;
card->dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret) {
+ dev_err(&pdev->dev, "%s new card name parsing error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (strstr(card->name, "_5682s"))
+ is5682s = 1;
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) {
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
+ dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0);
+ hdmi_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,hdmi-codec", 0);
+
for_each_card_prelinks(card, i, dai_link) {
if (!dai_link->platforms->name)
- dai_link->platforms->of_node = priv->platform_node;
+ dai_link->platforms->of_node = 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) {
+ if (!dp_node) {
dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
} else {
- dai_link->codecs->of_node = priv->dp_node;
+ dai_link->codecs->of_node = 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) {
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (!hdmi_node) {
dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n");
} else {
- dai_link->codecs->of_node = priv->hdmi_node;
+ dai_link->codecs->of_node = hdmi_node;
dai_link->codecs->name = NULL;
dai_link->codecs->dai_name = "i2s-hifi";
dai_link->init = mt8195_hdmi_codec_init;
}
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ dai_link->codecs->name =
+ is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
}
}
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);
- }
+ of_node_put(platform_node);
+ of_node_put(dp_node);
+ of_node_put(hdmi_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",},
@@ -1143,7 +1187,6 @@ static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = {
.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);
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
index 95abaadcd842..29c2d3407cc7 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-//
-// mt8195-mt6359-rt1019-rt5682.c --
-// MT8195-MT6359-RT1019-RT6358 ALSA SoC machine driver
-//
-// Copyright (c) 2021 MediaTek Inc.
-// Author: Trevor Wu <trevor.wu@mediatek.com>
-//
+/*
+ * mt8195-mt6359-rt1019-rt5682.c --
+ * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ * YC Hung <yc.hung@mediatek.com>
+ */
#include <linux/input.h>
#include <linux/module.h>
@@ -13,10 +14,12 @@
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/rt5682.h>
+#include <sound/sof.h>
#include <sound/soc.h>
#include "../../codecs/mt6359.h"
#include "../../codecs/rt5682.h"
#include "../common/mtk-afe-platform-driver.h"
+#include "mt8195-afe-clk.h"
#include "mt8195-afe-common.h"
#define RT1019_CODEC_DAI "HiFi"
@@ -25,13 +28,26 @@
#define RT5682_CODEC_DAI "rt5682-aif1"
#define RT5682_DEV0_NAME "rt5682.2-001a"
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+#define RT5682S_DEV0_NAME "rt5682s.2-001a"
+
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_DL3 "SOF_DMA_DL3"
+#define SOF_DMA_UL4 "SOF_DMA_UL4"
+#define SOF_DMA_UL5 "SOF_DMA_UL5"
+
+struct sof_conn_stream {
+ const char *normal_link;
+ const char *sof_link;
+ const char *sof_dma;
+ int stream_dir;
+};
+
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;
+ struct clk *i2so1_mclk;
};
static const struct snd_soc_dapm_widget
@@ -39,6 +55,10 @@ static const struct snd_soc_dapm_widget
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = {
@@ -48,6 +68,16 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = {
{ "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" },
{ "IN1P", NULL, "Headset Mic" },
+ /* SOF Uplink */
+ {SOF_DMA_UL4, NULL, "O034"},
+ {SOF_DMA_UL4, NULL, "O035"},
+ {SOF_DMA_UL5, NULL, "O036"},
+ {SOF_DMA_UL5, NULL, "O037"},
+ /* SOF Downlink */
+ {"I070", NULL, SOF_DMA_DL2},
+ {"I071", NULL, SOF_DMA_DL2},
+ {"I020", NULL, SOF_DMA_DL3},
+ {"I021", NULL, SOF_DMA_DL3},
};
static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = {
@@ -64,8 +94,6 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
unsigned int rate = params_rate(params);
- unsigned int mclk_fs_ratio = 128;
- unsigned int mclk_fs = rate * mclk_fs_ratio;
int bitwidth;
int ret;
@@ -81,25 +109,22 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1,
- RT5682_PLL1_S_BCLK1,
- params_rate(params) * 64,
- params_rate(params) * 512);
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
+ rate * 256, 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,
- params_rate(params) * 512,
- SND_SOC_CLOCK_IN);
+ 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, mclk_fs, SND_SOC_CLOCK_OUT);
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
+ SND_SOC_CLOCK_OUT);
}
static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
@@ -294,8 +319,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
struct mt8195_mt6359_rt1019_rt5682_priv *priv =
snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_jack *jack = &priv->headset_jack;
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
int ret;
+ priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
+
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 |
@@ -323,7 +354,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
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 */
+ /* fix BE i2s format to S24_LE, 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);
@@ -391,7 +422,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
SND_SOC_CLOCK_OUT);
}
-static struct snd_soc_ops mt8195_dptx_ops = {
+static const struct snd_soc_ops mt8195_dptx_ops = {
.hw_params = mt8195_dptx_hw_params,
};
@@ -431,7 +462,7 @@ 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 */
+ /* fix BE i2s format to S24_LE, 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);
@@ -532,6 +563,48 @@ static const struct snd_soc_ops mt8195_capture_ops = {
.startup = mt8195_capture_startup,
};
+static int mt8195_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = dapm->component;
+ struct mt8195_mt6359_rt1019_rt5682_priv *priv =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+
+ /*
+ * It's required to control mclk directly in the set_bias_level_post
+ * function for rt5682 and rt5682s codec, or the unexpected pop happens
+ * at the end of playback.
+ */
+ if (!component ||
+ (strcmp(component->name, RT5682_DEV0_NAME) &&
+ strcmp(component->name, RT5682S_DEV0_NAME)))
+ return 0;
+
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ if (!__clk_is_enabled(priv->i2so1_mclk))
+ return 0;
+
+ clk_disable_unprepare(priv->i2so1_mclk);
+ dev_dbg(card->dev, "Disable i2so1 mclk\n");
+ break;
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(priv->i2so1_mclk);
+ if (ret) {
+ dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(card->dev, "Enable i2so1 mclk\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
enum {
DAI_LINK_DL2_FE,
DAI_LINK_DL3_FE,
@@ -559,8 +632,17 @@ enum {
DAI_LINK_PCM1_BE,
DAI_LINK_UL_SRC1_BE,
DAI_LINK_UL_SRC2_BE,
+ DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC2_BE,
+ DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL3_BE,
+ DAI_LINK_SOF_UL4_BE,
+ DAI_LINK_SOF_UL5_BE,
+ DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE,
};
+#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1)
+
/* FE */
SND_SOC_DAILINK_DEFS(DL2_FE,
DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
@@ -661,14 +743,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
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_DUMMY()),
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_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
@@ -701,6 +781,154 @@ SND_SOC_DAILINK_DEFS(UL_SRC2_BE,
"mt6359-snd-codec-aif2")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL3,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL4,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL5,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
+ { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK},
+ { "UL_SRC1_BE", "AFE_SOF_UL4", SOF_DMA_UL4, SNDRV_PCM_STREAM_CAPTURE},
+ { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE},
+};
+
+/* fixup the BE DAI link to match any values from topology */
+static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai_link *sof_dai_link = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+ struct snd_soc_dai *cpu_dai;
+ int i, j, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
+ const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
+
+ if (strcmp(rtd->dai_link->name, conn->normal_link))
+ continue;
+
+ for_each_card_rtds(card, runtime) {
+ if (strcmp(runtime->dai_link->name, conn->sof_link))
+ continue;
+
+ for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
+ if (cpu_dai->stream_active[conn->stream_dir] > 0) {
+ sof_dai_link = runtime->dai_link;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (sof_dai_link && sof_dai_link->be_hw_params_fixup)
+ ret = sof_dai_link->be_hw_params_fixup(runtime, params);
+
+ break;
+ }
+
+ if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") ||
+ !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) {
+ mt8195_etdm_hw_params_fixup(runtime, params);
+ }
+
+ return ret;
+}
+
+static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *runtime;
+ struct snd_soc_component *sof_comp = NULL;
+ int i;
+
+ /* 1. find sof component */
+ for_each_card_rtds(card, runtime) {
+ for (i = 0; i < runtime->num_components; i++) {
+ if (!runtime->components[i]->driver->name)
+ continue;
+ if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) {
+ sof_comp = runtime->components[i];
+ break;
+ }
+ }
+ }
+
+ if (!sof_comp) {
+ dev_info(card->dev, " probe without component\n");
+ return 0;
+ }
+ /* 2. add route path and fixup callback */
+ for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
+ const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
+ struct snd_soc_pcm_runtime *sof_rtd = NULL;
+ struct snd_soc_pcm_runtime *normal_rtd = NULL;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+
+ for_each_card_rtds(card, rtd) {
+ if (!strcmp(rtd->dai_link->name, conn->sof_link)) {
+ sof_rtd = rtd;
+ continue;
+ }
+ if (!strcmp(rtd->dai_link->name, conn->normal_link)) {
+ normal_rtd = rtd;
+ continue;
+ }
+ if (normal_rtd && sof_rtd)
+ break;
+ }
+ if (normal_rtd && sof_rtd) {
+ int j;
+ struct snd_soc_dai *cpu_dai;
+
+ for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
+ struct snd_soc_dapm_route route;
+ struct snd_soc_dapm_path *p = NULL;
+ struct snd_soc_dapm_widget *play_widget =
+ cpu_dai->playback_widget;
+ struct snd_soc_dapm_widget *cap_widget =
+ cpu_dai->capture_widget;
+ memset(&route, 0, sizeof(route));
+ if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE &&
+ cap_widget) {
+ snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) {
+ route.source = conn->sof_dma;
+ route.sink = p->sink->name;
+ snd_soc_dapm_add_routes(&card->dapm, &route, 1);
+ }
+ } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK &&
+ play_widget){
+ snd_soc_dapm_widget_for_each_source_path(play_widget, p) {
+ route.source = p->source->name;
+ route.sink = conn->sof_dma;
+ snd_soc_dapm_add_routes(&card->dapm, &route, 1);
+ }
+ } else {
+ dev_err(cpu_dai->dev, "stream dir and widget not pair\n");
+ }
+ }
+ normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup;
+ }
+ }
+
+ return 0;
+}
+
static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
/* FE */
[DAI_LINK_DL2_FE] = {
@@ -895,7 +1123,6 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
/* 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),
@@ -964,6 +1191,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(PCM1_BE),
},
@@ -979,6 +1207,31 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(UL_SRC2_BE),
},
+ /* SOF BE */
+ [DAI_LINK_SOF_DL2_BE] = {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ [DAI_LINK_SOF_DL3_BE] = {
+ .name = "AFE_SOF_DL3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL3),
+ },
+ [DAI_LINK_SOF_UL4_BE] = {
+ .name = "AFE_SOF_UL4",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL4),
+ },
+ [DAI_LINK_SOF_UL5_BE] = {
+ .name = "AFE_SOF_UL5",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL5),
+ },
};
static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = {
@@ -992,89 +1245,166 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = {
.num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets),
.dapm_routes = mt8195_mt6359_rt1019_rt5682_routes,
.num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes),
+ .set_bias_level_post = mt8195_set_bias_level_post,
};
+static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
+ const char *propname)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *link;
+ const char *dai_name = NULL;
+ int i, j, ret, num_links;
+
+ num_links = of_property_count_strings(np, "mediatek,dai-link");
+
+ if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) {
+ dev_dbg(dev, "number of dai-link is invalid\n");
+ return -EINVAL;
+ }
+
+ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+
+ card->num_links = 0;
+ link = card->dai_link;
+
+ for (i = 0; i < num_links; i++) {
+ ret = of_property_read_string_index(np, propname, i, &dai_name);
+ if (ret) {
+ dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n",
+ propname, i, ret);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) {
+ if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) {
+ memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j],
+ sizeof(struct snd_soc_dai_link));
+ link++;
+ card->num_links++;
+ break;
+ }
+ }
+ }
+
+ if (card->num_links != num_links)
+ return -EINVAL;
+
+ return 0;
+}
+
static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card;
struct snd_soc_dai_link *dai_link;
struct mt8195_mt6359_rt1019_rt5682_priv *priv;
+ struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node;
+ int is5682s = 0;
+ int init6359 = 0;
+ int sof_on = 0;
int ret, i;
card->dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret) {
+ dev_err(&pdev->dev, "%s new card name parsing error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (strstr(card->name, "_5682s"))
+ is5682s = 1;
+
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) {
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
+ adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);
+ if (adsp_node)
+ sof_on = 1;
+
+ dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0);
+ hdmi_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,hdmi-codec", 0);
+
+ if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
+ ret = mt8195_dailink_parse_of(card, pdev->dev.of_node,
+ "mediatek,dai-link");
+ if (ret) {
+ dev_dbg(&pdev->dev, "Parse dai-link fail\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!sof_on)
+ card->num_links = DAI_LINK_REGULAR_NUM;
+ }
+
for_each_card_prelinks(card, i, dai_link) {
- if (!dai_link->platforms->name)
- dai_link->platforms->of_node = priv->platform_node;
+ if (!dai_link->platforms->name) {
+ if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on)
+ dai_link->platforms->of_node = adsp_node;
+ else
+ dai_link->platforms->of_node = 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) {
+ if (!dp_node) {
dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
} else {
- dai_link->codecs->of_node = priv->dp_node;
+ dai_link->codecs->of_node = 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) {
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (!hdmi_node) {
dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n");
} else {
- dai_link->codecs->of_node = priv->hdmi_node;
+ dai_link->codecs->of_node = hdmi_node;
dai_link->codecs->name = NULL;
dai_link->codecs->dai_name = "i2s-hifi";
dai_link->init = mt8195_hdmi_codec_init;
}
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ dai_link->codecs->name =
+ is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC2_BE") == 0) {
+ if (!init6359) {
+ dai_link->init = mt8195_mt6359_init;
+ init6359 = 1;
+ }
}
}
+ if (sof_on)
+ card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe;
+
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);
- }
+ of_node_put(platform_node);
+ of_node_put(adsp_node);
+ of_node_put(dp_node);
+ of_node_put(hdmi_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",},
@@ -1096,7 +1426,6 @@ 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);
@@ -1104,5 +1433,6 @@ module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver);
/* Module information */
MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver");
MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>");
+MODULE_LICENSE("GPL");
MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card");
diff --git a/sound/soc/mediatek/mt8195/mt8195-reg.h b/sound/soc/mediatek/mt8195/mt8195-reg.h
index d06f9cf85a4e..d3871353db41 100644
--- a/sound/soc/mediatek/mt8195/mt8195-reg.h
+++ b/sound/soc/mediatek/mt8195/mt8195-reg.h
@@ -2550,6 +2550,7 @@
#define PCM_INTF_CON1_PCM_FMT(x) (((x) & 0x3) << 1)
#define PCM_INTF_CON1_PCM_FMT_MASK (0x3 << 1)
#define PCM_INTF_CON1_PCM_EN BIT(0)
+#define PCM_INTF_CON1_PCM_EN_SHIFT 0
/* PCM_INTF_CON2 */
#define PCM_INTF_CON2_CLK_DOMAIN_SEL(x) (((x) & 0x3) << 23)
diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c
index ba15d5762b0b..d299a70db7e5 100644
--- a/sound/soc/meson/aiu.c
+++ b/sound/soc/meson/aiu.c
@@ -218,34 +218,23 @@ static int aiu_clk_get(struct device *dev)
int ret;
aiu->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(aiu->pclk)) {
- if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER)
- dev_err(dev, "Can't get the aiu pclk\n");
- return PTR_ERR(aiu->pclk);
- }
+ if (IS_ERR(aiu->pclk))
+ return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n");
aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
- if (IS_ERR(aiu->spdif_mclk)) {
- if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER)
- dev_err(dev, "Can't get the aiu spdif master clock\n");
- return PTR_ERR(aiu->spdif_mclk);
- }
+ if (IS_ERR(aiu->spdif_mclk))
+ return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk),
+ "Can't get the aiu spdif master clock\n");
ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
&aiu->i2s);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Can't get the i2s clocks\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't get the i2s clocks\n");
ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
&aiu->spdif);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Can't get the spdif clocks\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't get the spdif clocks\n");
ret = clk_prepare_enable(aiu->pclk);
if (ret) {
@@ -281,11 +270,8 @@ static int aiu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, aiu);
ret = device_reset(dev);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to reset device\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index b9af2d513e09..bccfb770b339 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -351,20 +351,12 @@ int axg_fifo_probe(struct platform_device *pdev)
}
fifo->pclk = devm_clk_get(dev, NULL);
- if (IS_ERR(fifo->pclk)) {
- if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %ld\n",
- PTR_ERR(fifo->pclk));
- return PTR_ERR(fifo->pclk);
- }
+ if (IS_ERR(fifo->pclk))
+ return dev_err_probe(dev, PTR_ERR(fifo->pclk), "failed to get pclk\n");
fifo->arb = devm_reset_control_get_exclusive(dev, NULL);
- if (IS_ERR(fifo->arb)) {
- if (PTR_ERR(fifo->arb) != -EPROBE_DEFER)
- dev_err(dev, "failed to get arb reset: %ld\n",
- PTR_ERR(fifo->arb));
- return PTR_ERR(fifo->arb);
- }
+ if (IS_ERR(fifo->arb))
+ return dev_err_probe(dev, PTR_ERR(fifo->arb), "failed to get arb reset\n");
fifo->irq = of_irq_get(dev->of_node, 0);
if (fifo->irq <= 0) {
diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c
index bfd37d49a73e..672e43a9729d 100644
--- a/sound/soc/meson/axg-pdm.c
+++ b/sound/soc/meson/axg-pdm.c
@@ -586,7 +586,6 @@ static int axg_pdm_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct axg_pdm *priv;
void __iomem *regs;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -611,28 +610,16 @@ static int axg_pdm_probe(struct platform_device *pdev)
}
priv->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(priv->pclk)) {
- ret = PTR_ERR(priv->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
priv->dclk = devm_clk_get(dev, "dclk");
- if (IS_ERR(priv->dclk)) {
- ret = PTR_ERR(priv->dclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get dclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->dclk))
+ return dev_err_probe(dev, PTR_ERR(priv->dclk), "failed to get dclk\n");
priv->sysclk = devm_clk_get(dev, "sysclk");
- if (IS_ERR(priv->sysclk)) {
- ret = PTR_ERR(priv->sysclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get dclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->sysclk))
+ return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n");
return devm_snd_soc_register_component(dev, &axg_pdm_component_drv,
&axg_pdm_dai_drv, 1);
diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c
index d0d09f945b48..4ba44e0d65d9 100644
--- a/sound/soc/meson/axg-spdifin.c
+++ b/sound/soc/meson/axg-spdifin.c
@@ -454,7 +454,6 @@ static int axg_spdifin_probe(struct platform_device *pdev)
struct axg_spdifin *priv;
struct snd_soc_dai_driver *dai_drv;
void __iomem *regs;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -479,20 +478,12 @@ static int axg_spdifin_probe(struct platform_device *pdev)
}
priv->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(priv->pclk)) {
- ret = PTR_ERR(priv->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
priv->refclk = devm_clk_get(dev, "refclk");
- if (IS_ERR(priv->refclk)) {
- ret = PTR_ERR(priv->refclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get mclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->refclk))
+ return dev_err_probe(dev, PTR_ERR(priv->refclk), "failed to get mclk\n");
dai_drv = axg_spdifin_get_dai_drv(dev, priv);
if (IS_ERR(dai_drv)) {
diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c
index e769a5ee6e27..3960d082e143 100644
--- a/sound/soc/meson/axg-spdifout.c
+++ b/sound/soc/meson/axg-spdifout.c
@@ -403,7 +403,6 @@ static int axg_spdifout_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct axg_spdifout *priv;
void __iomem *regs;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -422,20 +421,12 @@ static int axg_spdifout_probe(struct platform_device *pdev)
}
priv->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(priv->pclk)) {
- ret = PTR_ERR(priv->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
priv->mclk = devm_clk_get(dev, "mclk");
- if (IS_ERR(priv->mclk)) {
- ret = PTR_ERR(priv->mclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get mclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(priv->mclk))
+ return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n");
return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv,
axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv));
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index cab7fa2851aa..9883dc777f63 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -255,7 +255,6 @@ int axg_tdm_formatter_probe(struct platform_device *pdev)
const struct axg_tdm_formatter_driver *drv;
struct axg_tdm_formatter *formatter;
void __iomem *regs;
- int ret;
drv = of_device_get_match_data(dev);
if (!drv) {
@@ -282,57 +281,34 @@ int axg_tdm_formatter_probe(struct platform_device *pdev)
/* Peripharal clock */
formatter->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(formatter->pclk)) {
- ret = PTR_ERR(formatter->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->pclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n");
/* Formatter bit clock */
formatter->sclk = devm_clk_get(dev, "sclk");
- if (IS_ERR(formatter->sclk)) {
- ret = PTR_ERR(formatter->sclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get sclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->sclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n");
/* Formatter sample clock */
formatter->lrclk = devm_clk_get(dev, "lrclk");
- if (IS_ERR(formatter->lrclk)) {
- ret = PTR_ERR(formatter->lrclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get lrclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->lrclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n");
/* Formatter bit clock input multiplexer */
formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
- if (IS_ERR(formatter->sclk_sel)) {
- ret = PTR_ERR(formatter->sclk_sel);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get sclk_sel: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->sclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n");
/* Formatter sample clock input multiplexer */
formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
- if (IS_ERR(formatter->lrclk_sel)) {
- ret = PTR_ERR(formatter->lrclk_sel);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get lrclk_sel: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->lrclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel),
+ "failed to get lrclk_sel\n");
/* Formatter dedicated reset line */
formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
- if (IS_ERR(formatter->reset)) {
- ret = PTR_ERR(formatter->reset);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get reset: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->reset))
+ return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n");
return devm_snd_soc_register_component(dev, drv->component_drv,
NULL, 0);
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index db077773af7a..0c31934a9630 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -533,21 +533,13 @@ static int axg_tdm_iface_probe(struct platform_device *pdev)
/* Bit clock provided on the pad */
iface->sclk = devm_clk_get(dev, "sclk");
- if (IS_ERR(iface->sclk)) {
- ret = PTR_ERR(iface->sclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get sclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(iface->sclk))
+ return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n");
/* Sample clock provided on the pad */
iface->lrclk = devm_clk_get(dev, "lrclk");
- if (IS_ERR(iface->lrclk)) {
- ret = PTR_ERR(iface->lrclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get lrclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(iface->lrclk))
+ return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n");
/*
* mclk maybe be missing when the cpu dai is in slave mode and
@@ -558,13 +550,10 @@ static int axg_tdm_iface_probe(struct platform_device *pdev)
iface->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(iface->mclk)) {
ret = PTR_ERR(iface->mclk);
- if (ret == -ENOENT) {
+ if (ret == -ENOENT)
iface->mclk = NULL;
- } else {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get mclk: %d\n", ret);
- return ret;
- }
+ else
+ return dev_err_probe(dev, ret, "failed to get mclk\n");
}
return devm_snd_soc_register_component(dev,
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 29b0174f4b5c..2d8d5717fd8b 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -85,11 +85,9 @@ int meson_card_parse_dai(struct snd_soc_card *card,
ret = of_parse_phandle_with_args(node, "sound-dai",
"#sound-dai-cells", 0, &args);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(card->dev, "can't parse dai %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(card->dev, ret, "can't parse dai\n");
+
*dai_of_node = args.np;
return snd_soc_get_dai_name(&args, dai_name);
diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
index 4c1349dd1e06..a9b8c4e77d40 100644
--- a/sound/soc/meson/t9015.c
+++ b/sound/soc/meson/t9015.c
@@ -258,18 +258,12 @@ static int t9015_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
priv->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(priv->pclk)) {
- if (PTR_ERR(priv->pclk) != -EPROBE_DEFER)
- dev_err(dev, "failed to get core clock\n");
- return PTR_ERR(priv->pclk);
- }
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n");
priv->avdd = devm_regulator_get(dev, "AVDD");
- if (IS_ERR(priv->avdd)) {
- if (PTR_ERR(priv->avdd) != -EPROBE_DEFER)
- dev_err(dev, "failed to AVDD\n");
- return PTR_ERR(priv->avdd);
- }
+ if (IS_ERR(priv->avdd))
+ return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n");
ret = clk_prepare_enable(priv->pclk);
if (ret) {
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index a6407f4388de..2412dc7e65d4 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -160,12 +160,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
return 0;
}
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index b2173847dc47..cf3e151bb635 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -169,6 +169,7 @@ config SND_SOC_SC7180
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_ADAU7002
help
To add support for audio on Qualcomm Technologies Inc.
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index ba2a98268ee4..f9d69375320e 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -17,6 +17,9 @@
#include <uapi/linux/input-event-codes.h>
#include <dt-bindings/sound/apq8016-lpass.h>
#include "common.h"
+#include "qdsp6/q6afe.h"
+
+#define MI2S_COUNT (MI2S_QUATERNARY + 1)
struct apq8016_sbc_data {
struct snd_soc_card card;
@@ -24,6 +27,7 @@ struct apq8016_sbc_data {
void __iomem *spkr_iomux;
struct snd_soc_jack jack;
bool jack_setup;
+ int mi2s_clk_count[MI2S_COUNT];
};
#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
@@ -38,10 +42,10 @@ struct apq8016_sbc_data {
#define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18)
#define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18)
#define DEFAULT_MCLK_RATE 9600000
+#define MI2S_BCLK_RATE 1536000
-static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s)
{
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai;
struct snd_soc_component *component;
struct snd_soc_card *card = rtd->card;
@@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
int i, rval;
u32 value;
- switch (cpu_dai->id) {
+ switch (mi2s) {
case MI2S_PRIMARY:
writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
pdata->spkr_iomux);
@@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ return apq8016_dai_init(rtd, cpu_dai->id);
+}
+
static void apq8016_sbc_add_ops(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
@@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card)
link->init = apq8016_sbc_dai_init;
}
+static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai)
+{
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ return MI2S_PRIMARY;
+ case SECONDARY_MI2S_RX:
+ case SECONDARY_MI2S_TX:
+ return MI2S_SECONDARY;
+ case TERTIARY_MI2S_RX:
+ case TERTIARY_MI2S_TX:
+ return MI2S_TERTIARY;
+ case QUATERNARY_MI2S_RX:
+ case QUATERNARY_MI2S_TX:
+ return MI2S_QUATERNARY;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai));
+}
+
+static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int mi2s, ret;
+
+ mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
+ if (mi2s < 0)
+ return mi2s;
+
+ if (++data->mi2s_clk_count[mi2s] > 1)
+ return 0;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0);
+ if (ret)
+ dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret);
+ return ret;
+}
+
+static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int mi2s, ret;
+
+ mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
+ if (mi2s < 0)
+ return;
+
+ if (--data->mi2s_clk_count[mi2s] > 0)
+ return;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0);
+ if (ret)
+ dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret);
+}
+
+static const struct snd_soc_ops msm8916_qdsp6_be_ops = {
+ .startup = msm8916_qdsp6_startup,
+ .shutdown = msm8916_qdsp6_shutdown,
+};
+
+static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static void msm8916_qdsp6_add_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ /* Make it obvious to userspace that QDSP6 is used */
+ card->components = "qdsp6";
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm) {
+ link->init = msm8916_qdsp6_dai_init;
+ link->ops = &msm8916_qdsp6_be_ops;
+ link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup;
+ }
+ }
+}
+
static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Handset Mic", NULL),
@@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
static int apq8016_sbc_platform_probe(struct platform_device *pdev)
{
+ void (*add_ops)(struct snd_soc_card *card);
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
struct apq8016_sbc_data *data;
int ret;
+ add_ops = device_get_match_data(&pdev->dev);
+ if (!add_ops)
+ return -EINVAL;
+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, data);
- apq8016_sbc_add_ops(card);
+ add_ops(card);
return devm_snd_soc_register_card(&pdev->dev, card);
}
static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = {
- { .compatible = "qcom,apq8016-sbc-sndcard" },
+ { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops },
+ { .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops },
{},
};
MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 2e1c618f7529..c407684ce1a2 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -26,6 +26,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
return ret;
}
+ if (of_property_read_bool(dev->of_node, "widgets")) {
+ ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets");
+ if (ret)
+ return ret;
+ }
+
/* DAPM routes */
if (of_property_read_bool(dev->of_node, "audio-routing")) {
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
@@ -39,6 +45,10 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
return ret;
}
+ ret = snd_soc_of_parse_pin_switches(card, "pin-switches");
+ if (ret)
+ return ret;
+
ret = snd_soc_of_parse_aux_devs(card, "aux-devs");
if (ret)
return ret;
@@ -94,9 +104,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(card->dev, "%s: error getting cpu dai name: %d\n",
- link->name, ret);
+ dev_err_probe(card->dev, ret,
+ "%s: error getting cpu dai name\n", link->name);
goto err;
}
@@ -116,9 +125,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
if (codec) {
ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(card->dev, "%s: codec dai not found: %d\n",
- link->name, ret);
+ dev_err_probe(card->dev, ret,
+ "%s: codec dai not found\n", link->name);
goto err;
}
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 13598ef5bacb..f424d7aa389a 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -615,7 +615,7 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
graph = kzalloc(sizeof(*graph), GFP_KERNEL);
if (!graph) {
ret = -ENOMEM;
- goto err;
+ goto put_ar_graph;
}
graph->apm = apm;
@@ -630,14 +630,16 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
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;
+ if (IS_ERR(graph->port)) {
+ ret = PTR_ERR(graph->port);
+ goto free_graph;
}
return graph;
-err:
+
+free_graph:
+ kfree(graph);
+put_ar_graph:
kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
return ERR_PTR(ret);
}
diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c
index 768566bb57a5..37225ef2563a 100644
--- a/sound/soc/qcom/sc7180.c
+++ b/sound/soc/qcom/sc7180.c
@@ -17,6 +17,7 @@
#include <uapi/linux/input-event-codes.h>
#include "../codecs/rt5682.h"
+#include "../codecs/rt5682s.h"
#include "common.h"
#include "lpass.h"
@@ -128,7 +129,21 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream)
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(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);
- int ret;
+ int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
+
+ if (!strcmp(codec_dai->name, "rt5682-aif1")) {
+ pll_source = RT5682_PLL1_S_MCLK;
+ pll_id = 0;
+ clk_id = RT5682_SCLK_S_PLL1;
+ pll_out = RT5682_PLL1_FREQ;
+ pll_in = DEFAULT_MCLK_RATE;
+ } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
+ pll_source = RT5682S_PLL_S_MCLK;
+ pll_id = RT5682S_PLL2;
+ clk_id = RT5682S_SCLK_S_PLL2;
+ pll_out = RT5682_PLL1_FREQ;
+ pll_in = DEFAULT_MCLK_RATE;
+ }
switch (cpu_dai->id) {
case MI2S_PRIMARY:
@@ -145,16 +160,15 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream)
SND_SOC_DAIFMT_I2S);
/* Configure PLL1 for codec */
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK,
- DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ);
+ ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
+ pll_in, pll_out);
if (ret) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
return ret;
}
/* Configure sysclk for codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
- RT5682_PLL1_FREQ,
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
SND_SOC_CLOCK_IN);
if (ret)
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 0adfc5708949..5c1d13eccbee 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -33,6 +33,7 @@
struct sdm845_snd_data {
struct snd_soc_jack jack;
bool jack_setup;
+ bool slim_port_setup;
bool stream_prepared[AFE_PORT_MAX];
struct snd_soc_card *card;
uint32_t pri_mi2s_clk_count;
@@ -56,8 +57,8 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream,
int ret = 0, i;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
- sruntime = snd_soc_dai_get_sdw_stream(codec_dai,
- substream->stream);
+ sruntime = snd_soc_dai_get_stream(codec_dai,
+ substream->stream);
if (sruntime != ERR_PTR(-ENOTSUPP))
pdata->sruntime[cpu_dai->id] = sruntime;
@@ -224,6 +225,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *link = rtd->dai_link;
struct snd_jack *jack;
/*
* Codec SLIMBUS configuration
@@ -276,6 +278,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
}
break;
case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ /* setting up wcd multiple times for slim port is redundant */
+ if (pdata->slim_port_setup || !link->no_pcm)
+ return 0;
+
for_each_rtd_codec_dais(rtd, i, codec_dai) {
rval = snd_soc_dai_set_channel_map(codec_dai,
ARRAY_SIZE(tx_ch),
@@ -295,8 +301,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
return rval;
}
-
}
+
+ pdata->slim_port_setup = true;
+
break;
default:
break;
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index b2ca2579810b..114a29e01c0f 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -136,8 +136,8 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream,
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);
+ sruntime = snd_soc_dai_get_stream(codec_dai,
+ substream->stream);
if (sruntime != ERR_PTR(-ENOTSUPP))
pdata->sruntime[cpu_dai->id] = sruntime;
}
diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
index 33a00774746d..b052642ea620 100644
--- a/sound/soc/rockchip/rk3288_hdmi_analog.c
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -249,13 +249,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, machine);
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (ret) {
- dev_err(&pdev->dev,
- "Soc register card failed %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Soc register card failed\n");
return ret;
}
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index 313ab650f8d9..5265e546b124 100644
--- a/sound/soc/samsung/aries_wm8994.c
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -585,19 +585,16 @@ static int aries_audio_probe(struct platform_device *pdev)
extcon_np = of_parse_phandle(np, "extcon", 0);
priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
- if (IS_ERR(priv->usb_extcon)) {
- if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER)
- dev_err(dev, "Failed to get extcon device");
- return PTR_ERR(priv->usb_extcon);
- }
+ if (IS_ERR(priv->usb_extcon))
+ return dev_err_probe(dev, PTR_ERR(priv->usb_extcon),
+ "Failed to get extcon device");
of_node_put(extcon_np);
priv->adc = devm_iio_channel_get(dev, "headset-detect");
- if (IS_ERR(priv->adc)) {
- if (PTR_ERR(priv->adc) != -EPROBE_DEFER)
- dev_err(dev, "Failed to get ADC channel");
- return PTR_ERR(priv->adc);
- }
+ if (IS_ERR(priv->adc))
+ return dev_err_probe(dev, PTR_ERR(priv->adc),
+ "Failed to get ADC channel");
+
if (priv->adc->channel->type != IIO_VOLTAGE)
return -EINVAL;
diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c
index 606ac5e33a8e..a5dc640d0d76 100644
--- a/sound/soc/samsung/arndale.c
+++ b/sound/soc/samsung/arndale.c
@@ -174,9 +174,8 @@ static int arndale_audio_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(card->dev, card);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "snd_soc_register_card() failed: %d\n", ret);
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
goto err_put_of_nodes;
}
return 0;
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index 66bcc2f97544..c3f1b054e238 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -360,6 +360,8 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
buf->addr = idma.lp_tx_addr;
buf->bytes = idma_hardware.buffer_bytes_max;
buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes);
+ if (!buf->area)
+ return -ENOMEM;
return 0;
}
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index 390f2dd735ad..34067cc314ff 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -325,9 +325,8 @@ static int littlemill_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 998d10cf8c94..7b12ccd2a9b2 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -183,9 +183,8 @@ static int lowland_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
index ca643a488c3c..4ff12e2e704f 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -311,9 +311,7 @@ static int odroid_audio_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ dev_err_probe(dev, ret, "snd_soc_register_card() failed\n");
goto err_put_clk_i2s;
}
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index 7661b637946d..821ad1eb1b79 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -179,8 +179,8 @@ static int smdk_audio_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c
index 029448f5bedb..d77dc54cae9c 100644
--- a/sound/soc/samsung/smdk_wm8994pcm.c
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -118,8 +118,8 @@ static int snd_smdk_probe(struct platform_device *pdev)
smdk_pcm.dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
return ret;
}
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
index 6da674e901ca..02372109c251 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -212,12 +212,9 @@ static int snow_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "snd_soc_register_card failed (%d)\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card failed\n");
return ret;
}
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index f5f6ba00d073..37b1f4f60b21 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -330,9 +330,8 @@ static int speyside_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c
index a2c77e6defec..d611ec9e5325 100644
--- a/sound/soc/samsung/tm2_wm5110.c
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -612,8 +612,7 @@ static int tm2_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to register card: %d\n", ret);
+ dev_err_probe(dev, ret, "Failed to register card\n");
goto dai_node_put;
}
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index 15223d860cb7..8d3149a47a4c 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -229,9 +229,8 @@ static int tobermory_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret && ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c
index fa0cc08f70ec..e8d98b362f9d 100644
--- a/sound/soc/sh/rz-ssi.c
+++ b/sound/soc/sh/rz-ssi.c
@@ -1020,7 +1020,12 @@ static int rz_ssi_probe(struct platform_device *pdev)
reset_control_deassert(ssi->rstc);
pm_runtime_enable(&pdev->dev);
- pm_runtime_resume_and_get(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0) {
+ pm_runtime_disable(ssi->dev);
+ reset_control_assert(ssi->rstc);
+ return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n");
+ }
spin_lock_init(&ssi->lock);
dev_set_drvdata(&pdev->dev, ssi);
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index c76ff9c59dfb..c0664f94990c 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -932,6 +932,34 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
return 0;
}
+void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *cpu_delay,
+ snd_pcm_sframes_t *codec_delay)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ snd_pcm_sframes_t delay;
+ int i;
+
+ /*
+ * We're looking for the delay through the full audio path so it needs to
+ * be the maximum of the Components doing transmit and the maximum of the
+ * Components doing receive (ie, all CPUs and all CODECs) rather than
+ * just the maximum of all Components.
+ */
+ for_each_rtd_components(rtd, i, component) {
+ if (!component->driver->delay)
+ continue;
+
+ delay = component->driver->delay(component, substream);
+
+ if (snd_soc_component_is_codec(component))
+ *codec_delay = max(*codec_delay, delay);
+ else
+ *cpu_delay = max(*cpu_delay, delay);
+ }
+}
+
int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index dcf6be4c4aaa..434e61b46983 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2315,7 +2315,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
mutex_init(&card->pcm_mutex);
- spin_lock_init(&card->dpcm_lock);
return snd_soc_bind_card(card);
}
@@ -2824,6 +2823,56 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
+int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop)
+{
+ const unsigned int nb_controls_max = 16;
+ const char **strings, *control_name;
+ struct snd_kcontrol_new *controls;
+ struct device *dev = card->dev;
+ unsigned int i, nb_controls;
+ int ret;
+
+ if (!of_property_read_bool(dev->of_node, prop))
+ return 0;
+
+ strings = devm_kcalloc(dev, nb_controls_max,
+ sizeof(*strings), GFP_KERNEL);
+ if (!strings)
+ return -ENOMEM;
+
+ ret = of_property_read_string_array(dev->of_node, prop,
+ strings, nb_controls_max);
+ if (ret < 0)
+ return ret;
+
+ nb_controls = (unsigned int)ret;
+
+ controls = devm_kcalloc(dev, nb_controls,
+ sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ for (i = 0; i < nb_controls; i++) {
+ control_name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s Switch", strings[i]);
+ if (!control_name)
+ return -ENOMEM;
+
+ controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ controls[i].name = control_name;
+ controls[i].info = snd_soc_dapm_info_pin_switch;
+ controls[i].get = snd_soc_dapm_get_pin_switch;
+ controls[i].put = snd_soc_dapm_put_pin_switch;
+ controls[i].private_value = (unsigned long)strings[i];
+ }
+
+ card->controls = controls;
+ card->num_controls = nb_controls;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_pin_switches);
+
int snd_soc_of_get_slot_mask(struct device_node *np,
const char *prop_name,
unsigned int *mask)
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index 3db0fcf24385..6078afe335f8 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -453,18 +453,6 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
soc_dai_mark_pop(dai, substream, startup);
}
-snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
- struct snd_pcm_substream *substream)
-{
- int delay = 0;
-
- if (dai->driver->ops &&
- dai->driver->ops->delay)
- delay = dai->driver->ops->delay(substream, dai);
-
- return delay;
-}
-
int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
struct snd_soc_pcm_runtime *rtd, int num)
{
@@ -693,6 +681,34 @@ int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
return 0;
}
+void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *cpu_delay,
+ snd_pcm_sframes_t *codec_delay)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *dai;
+ int i;
+
+ /*
+ * We're looking for the delay through the full audio path so it needs to
+ * be the maximum of the DAIs doing transmit and the maximum of the DAIs
+ * doing receive (ie, all CPUs and all CODECs) rather than just the maximum
+ * of all DAIs.
+ */
+
+ /* for CPU */
+ for_each_rtd_cpu_dais(rtd, i, dai)
+ if (dai->driver->ops &&
+ dai->driver->ops->delay)
+ *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai));
+
+ /* for Codec */
+ for_each_rtd_codec_dais(rtd, i, dai)
+ if (dai->driver->ops &&
+ dai->driver->ops->delay)
+ *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai));
+}
+
int snd_soc_dai_compr_startup(struct snd_soc_dai *dai,
struct snd_compr_stream *cstream)
{
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 4d41ad302802..7abfc48b26ca 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -27,6 +27,37 @@
#include <sound/soc-link.h>
#include <sound/initval.h>
+static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd)
+{
+ mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+}
+
+static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd)
+{
+ mutex_unlock(&rtd->card->pcm_mutex);
+}
+
+#define snd_soc_dpcm_mutex_assert_held(rtd) \
+ lockdep_assert_held(&(rtd)->card->pcm_mutex)
+
+static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
+ int stream)
+{
+ snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream));
+}
+
+#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \
+ snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags)
+
+static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd,
+ int stream)
+{
+ snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream));
+}
+
+#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \
+ snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags)
+
#define DPCM_MAX_BE_USERS 8
static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd)
@@ -73,7 +104,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
struct snd_soc_dpcm *dpcm;
ssize_t offset = 0;
- unsigned long flags;
/* FE state */
offset += scnprintf(buf + offset, size - offset,
@@ -101,7 +131,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
goto out;
}
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
params = &dpcm->hw_params;
@@ -122,7 +151,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
params_channels(params),
params_rate(params));
}
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
out:
return offset;
}
@@ -145,11 +173,13 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
+ snd_soc_dpcm_mutex_lock(fe);
for_each_pcm_streams(stream)
if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream))
offset += dpcm_show_state(fe, stream,
buf + offset,
out_count - offset);
+ snd_soc_dpcm_mutex_unlock(fe);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
@@ -221,14 +251,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
struct snd_pcm_substream *substream =
snd_soc_dpcm_get_substream(fe, stream);
- snd_pcm_stream_lock_irq(substream);
+ snd_soc_dpcm_stream_lock_irq(fe, stream);
if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
dpcm_fe_dai_do_trigger(substream,
fe->dpcm[stream].trigger_pending - 1);
fe->dpcm[stream].trigger_pending = 0;
}
fe->dpcm[stream].runtime_update = state;
- snd_pcm_stream_unlock_irq(substream);
+ snd_soc_dpcm_stream_unlock_irq(fe, stream);
}
static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be,
@@ -256,7 +286,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai;
int i;
- lockdep_assert_held(&rtd->card->pcm_mutex);
+ snd_soc_dpcm_mutex_assert_held(rtd);
for_each_rtd_dais(rtd, i, dai)
snd_soc_dai_action(dai, stream, action);
@@ -309,6 +339,8 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
{
struct snd_soc_dpcm *dpcm;
+ snd_soc_dpcm_mutex_assert_held(fe);
+
for_each_dpcm_be(fe, dir, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
@@ -646,14 +678,14 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream,
return ret;
}
-static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
+static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream, int rollback)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component;
struct snd_soc_dai *dai;
int i;
- mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+ snd_soc_dpcm_mutex_assert_held(rtd);
if (!rollback)
snd_soc_runtime_deactivate(rtd, substream->stream);
@@ -665,9 +697,6 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
soc_pcm_components_close(substream, rollback);
-
- mutex_unlock(&rtd->card->pcm_mutex);
-
snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback);
for_each_rtd_components(rtd, i, component)
@@ -682,9 +711,21 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
* freed here. The cpu DAI, codec DAI, machine and components are also
* shutdown.
*/
+static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream)
+{
+ return soc_pcm_clean(rtd, substream, 0);
+}
+
+/* PCM close ops for non-DPCM streams */
static int soc_pcm_close(struct snd_pcm_substream *substream)
{
- return soc_pcm_clean(substream, 0);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ snd_soc_dpcm_mutex_lock(rtd);
+ soc_pcm_clean(rtd, substream, 0);
+ snd_soc_dpcm_mutex_unlock(rtd);
+ return 0;
}
static int soc_hw_sanity_check(struct snd_pcm_substream *substream)
@@ -730,21 +771,21 @@ config_err:
* then initialized and any private data can be allocated. This also calls
* startup for the cpu DAI, component, machine and codec DAI.
*/
-static int soc_pcm_open(struct snd_pcm_substream *substream)
+static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component;
struct snd_soc_dai *dai;
int i, ret = 0;
+ snd_soc_dpcm_mutex_assert_held(rtd);
+
for_each_rtd_components(rtd, i, component)
pinctrl_pm_select_default_state(component->dev);
ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream);
if (ret < 0)
- goto pm_err;
-
- mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+ goto err;
ret = soc_pcm_components_open(substream);
if (ret < 0)
@@ -791,16 +832,26 @@ dynamic:
snd_soc_runtime_activate(rtd, substream->stream);
ret = 0;
err:
- mutex_unlock(&rtd->card->pcm_mutex);
-pm_err:
if (ret < 0) {
- soc_pcm_clean(substream, 1);
+ soc_pcm_clean(rtd, substream, 1);
dev_err(rtd->dev, "%s() failed (%d)", __func__, ret);
}
return ret;
}
+/* PCM open ops for non-DPCM streams */
+static int soc_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ret;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+ ret = __soc_pcm_open(rtd, substream);
+ snd_soc_dpcm_mutex_unlock(rtd);
+ return ret;
+}
+
static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
{
/*
@@ -816,13 +867,13 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
* rate, etc. This function is non atomic and can be called multiple times,
* it can refer to the runtime info.
*/
-static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai;
int i, ret = 0;
- mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+ snd_soc_dpcm_mutex_assert_held(rtd);
ret = snd_soc_link_prepare(substream);
if (ret < 0)
@@ -850,14 +901,24 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dai_digital_mute(dai, 0, substream->stream);
out:
- mutex_unlock(&rtd->card->pcm_mutex);
-
if (ret < 0)
dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
return ret;
}
+/* PCM prepare ops for non-DPCM streams */
+static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ret;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+ ret = __soc_pcm_prepare(rtd, substream);
+ snd_soc_dpcm_mutex_unlock(rtd);
+ return ret;
+}
+
static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
unsigned int mask)
{
@@ -869,13 +930,13 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
interval->max = channels;
}
-static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
+static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream, int rollback)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai;
int i;
- mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+ snd_soc_dpcm_mutex_assert_held(rtd);
/* clear the corresponding DAIs parameters when going to be inactive */
for_each_rtd_dais(rtd, i, dai) {
@@ -900,16 +961,28 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
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;
}
/*
* Frees resources allocated by hw_params, can be called multiple times
*/
+static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream)
+{
+ return soc_pcm_hw_clean(rtd, substream, 0);
+}
+
+/* hw_free PCM ops for non-DPCM streams */
static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
{
- return soc_pcm_hw_clean(substream, 0);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ret;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+ ret = __soc_pcm_hw_free(rtd, substream);
+ snd_soc_dpcm_mutex_unlock(rtd);
+ return ret;
}
/*
@@ -917,15 +990,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
* function can also be called multiple times and can allocate buffers
* (using snd_pcm_lib_* ). It's non-atomic.
*/
-static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
+ 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 *cpu_dai;
struct snd_soc_dai *codec_dai;
int i, ret = 0;
- mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+ snd_soc_dpcm_mutex_assert_held(rtd);
ret = soc_pcm_params_symmetry(substream, params);
if (ret)
@@ -997,16 +1070,27 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_pcm_component_hw_params(substream, params);
out:
- mutex_unlock(&rtd->card->pcm_mutex);
-
if (ret < 0) {
- soc_pcm_hw_clean(substream, 1);
+ soc_pcm_hw_clean(rtd, substream, 1);
dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
}
return ret;
}
+/* hw_params PCM ops for non-DPCM streams */
+static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ret;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+ ret = __soc_pcm_hw_params(rtd, substream, params);
+ snd_soc_dpcm_mutex_unlock(rtd);
+ return ret;
+}
+
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
@@ -1080,41 +1164,22 @@ start_err:
/*
* soc level wrapper for pointer callback
* If cpu_dai, codec_dai, component driver has the delay callback, then
- * the runtime->delay will be updated accordingly.
+ * the runtime->delay will be updated via snd_soc_pcm_component/dai_delay().
*/
static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai;
- struct snd_soc_dai *codec_dai;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t offset = 0;
- snd_pcm_sframes_t delay = 0;
snd_pcm_sframes_t codec_delay = 0;
snd_pcm_sframes_t cpu_delay = 0;
- int i;
-
- /* clearing the previous total delay */
- runtime->delay = 0;
offset = snd_soc_pcm_component_pointer(substream);
- /* base delay if assigned in pointer callback */
- delay = runtime->delay;
+ /* should be called *after* snd_soc_pcm_component_pointer() */
+ snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay);
+ snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay);
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- cpu_delay = max(cpu_delay,
- snd_soc_dai_delay(cpu_dai, substream));
- }
- delay += cpu_delay;
-
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- codec_delay = max(codec_delay,
- snd_soc_dai_delay(codec_dai, substream));
- }
- delay += codec_delay;
-
- runtime->delay = delay;
+ runtime->delay = cpu_delay + codec_delay;
return offset;
}
@@ -1123,8 +1188,11 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream)
{
+ struct snd_pcm_substream *fe_substream;
+ struct snd_pcm_substream *be_substream;
struct snd_soc_dpcm *dpcm;
- unsigned long flags;
+
+ snd_soc_dpcm_mutex_assert_held(fe);
/* only add new dpcms */
for_each_dpcm_be(fe, stream, dpcm) {
@@ -1132,7 +1200,21 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
return 0;
}
- dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
+ fe_substream = snd_soc_dpcm_get_substream(fe, stream);
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+ if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) {
+ dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) {
+ dev_warn(be->dev, "%s: FE is nonatomic but BE is not, forcing BE as nonatomic\n",
+ __func__);
+ be_substream->pcm->nonatomic = 1;
+ }
+
+ dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC);
if (!dpcm)
return -ENOMEM;
@@ -1140,10 +1222,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
dpcm->fe = fe;
be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+ snd_soc_dpcm_stream_lock_irq(fe, stream);
list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+ snd_soc_dpcm_stream_unlock_irq(fe, stream);
dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
stream ? "capture" : "playback", fe->dai_link->name,
@@ -1186,8 +1268,10 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm, *d;
- unsigned long flags;
+ snd_soc_dpcm_mutex_assert_held(fe);
+
+ snd_soc_dpcm_stream_lock_irq(fe, stream);
for_each_dpcm_be_safe(fe, stream, dpcm, d) {
dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
stream ? "capture" : "playback",
@@ -1205,12 +1289,11 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
dpcm_remove_debugfs_state(dpcm);
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
list_del(&dpcm->list_be);
list_del(&dpcm->list_fe);
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
kfree(dpcm);
}
+ snd_soc_dpcm_stream_unlock_irq(fe, stream);
}
/* get BE for DAI widget and stream */
@@ -1434,12 +1517,9 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
- unsigned long flags;
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm)
dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_NO);
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
}
void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
@@ -1475,12 +1555,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
continue;
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) {
- soc_pcm_hw_free(be_substream);
+ __soc_pcm_hw_free(be, be_substream);
be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
}
}
- soc_pcm_close(be_substream);
+ __soc_pcm_close(be, be_substream);
be_substream->runtime = NULL;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
}
@@ -1528,7 +1608,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
stream ? "capture" : "playback", be->dai_link->name);
be_substream->runtime = be->dpcm[stream].runtime;
- err = soc_pcm_open(be_substream);
+ err = __soc_pcm_open(be, be_substream);
if (err < 0) {
be->dpcm[stream].users--;
if (be->dpcm[stream].users < 0)
@@ -1539,7 +1619,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
goto unwind;
}
-
+ be->dpcm[stream].be_start = 0;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
count++;
}
@@ -1772,7 +1852,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
/* start the DAI frontend */
- ret = soc_pcm_open(fe_substream);
+ ret = __soc_pcm_open(fe, fe_substream);
if (ret < 0)
goto unwind;
@@ -1803,6 +1883,8 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
int stream = substream->stream;
+ snd_soc_dpcm_mutex_assert_held(fe);
+
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
@@ -1811,7 +1893,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
/* now shutdown the frontend */
- soc_pcm_close(substream);
+ __soc_pcm_close(fe, substream);
/* run the stream stop event */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
@@ -1856,7 +1938,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
be->dai_link->name);
- soc_pcm_hw_free(be_substream);
+ __soc_pcm_hw_free(be, be_substream);
be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
}
@@ -1867,13 +1949,13 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
int stream = substream->stream;
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ snd_soc_dpcm_mutex_lock(fe);
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
/* call hw_free on the frontend */
- soc_pcm_hw_free(substream);
+ soc_pcm_hw_clean(fe, substream, 0);
/* only hw_params backends that are either sinks or sources
* to this frontend DAI */
@@ -1882,7 +1964,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_dpcm_mutex_unlock(fe);
return 0;
}
@@ -1926,7 +2008,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
be->dai_link->name);
- ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
+ ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params);
if (ret < 0)
goto unwind;
@@ -1956,7 +2038,7 @@ unwind:
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
continue;
- soc_pcm_hw_free(be_substream);
+ __soc_pcm_hw_free(be, be_substream);
}
return ret;
@@ -1968,7 +2050,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
int ret, stream = substream->stream;
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ snd_soc_dpcm_mutex_lock(fe);
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[stream].hw_params, params,
@@ -1982,7 +2064,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
params_channels(params), params_format(params));
/* call hw_params on the frontend */
- ret = soc_pcm_hw_params(substream, params);
+ ret = __soc_pcm_hw_params(fe, substream, params);
if (ret < 0)
dpcm_be_dai_hw_free(fe, stream);
else
@@ -1990,7 +2072,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
out:
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_dpcm_mutex_unlock(fe);
if (ret < 0)
dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret);
@@ -2003,6 +2085,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
{
struct snd_soc_pcm_runtime *be;
struct snd_soc_dpcm *dpcm;
+ unsigned long flags;
int ret = 0;
for_each_dpcm_be(fe, stream, dpcm) {
@@ -2011,89 +2094,128 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
be = dpcm->be;
be_substream = snd_soc_dpcm_get_substream(be, stream);
+ snd_soc_dpcm_stream_lock_irqsave(be, stream, flags);
+
/* is this op for this BE ? */
if (!snd_soc_dpcm_be_can_update(fe, be, stream))
- continue;
+ goto next;
dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n",
be->dai_link->name, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+ if (!be->dpcm[stream].be_start &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
- continue;
+ goto next;
+
+ be->dpcm[stream].be_start++;
+ if (be->dpcm[stream].be_start != 1)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ be->dpcm[stream].be_start--;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
case SNDRV_PCM_TRIGGER_RESUME:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
- continue;
+ goto next;
+
+ be->dpcm[stream].be_start++;
+ if (be->dpcm[stream].be_start != 1)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ be->dpcm[stream].be_start--;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
- continue;
+ if (!be->dpcm[stream].be_start &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+ goto next;
+
+ be->dpcm[stream].be_start++;
+ if (be->dpcm[stream].be_start != 1)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ be->dpcm[stream].be_start--;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
case SNDRV_PCM_TRIGGER_STOP:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
- continue;
+ goto next;
- if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
- continue;
+ if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
+ be->dpcm[stream].be_start--;
+
+ if (be->dpcm[stream].be_start != 0)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
+ be->dpcm[stream].be_start++;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
- continue;
+ goto next;
- if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
- continue;
+ be->dpcm[stream].be_start--;
+ if (be->dpcm[stream].be_start != 0)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ be->dpcm[stream].be_start++;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
- continue;
+ goto next;
- if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
- continue;
+ be->dpcm[stream].be_start--;
+ if (be->dpcm[stream].be_start != 0)
+ goto next;
ret = soc_pcm_trigger(be_substream, cmd);
- if (ret)
- goto end;
+ if (ret) {
+ be->dpcm[stream].be_start++;
+ goto next;
+ }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
break;
}
+next:
+ snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags);
+ if (ret)
+ break;
}
-end:
if (ret < 0)
dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n",
__func__, be->dai_link->name, ret);
@@ -2261,7 +2383,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: prepare BE %s\n",
be->dai_link->name);
- ret = soc_pcm_prepare(be_substream);
+ ret = __soc_pcm_prepare(be, be_substream);
if (ret < 0)
break;
@@ -2279,7 +2401,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
int stream = substream->stream, ret = 0;
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ snd_soc_dpcm_mutex_lock(fe);
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
@@ -2298,7 +2420,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
goto out;
/* call prepare on the frontend */
- ret = soc_pcm_prepare(substream);
+ ret = __soc_pcm_prepare(fe, substream);
if (ret < 0)
goto out;
@@ -2306,7 +2428,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
out:
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_dpcm_mutex_unlock(fe);
if (ret < 0)
dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
@@ -2357,7 +2479,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
struct snd_soc_dpcm *dpcm;
enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
int ret = 0;
- unsigned long flags;
dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
stream ? "capture" : "playback", fe->dai_link->name);
@@ -2426,7 +2547,6 @@ close:
dpcm_be_dai_shutdown(fe, stream);
disconnect:
/* disconnect any pending BEs */
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
@@ -2438,7 +2558,6 @@ disconnect:
be->dpcm[stream].state == SND_SOC_DPCM_STATE_NEW)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
}
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
if (ret < 0)
dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
@@ -2513,7 +2632,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
struct snd_soc_pcm_runtime *fe;
int ret = 0;
- mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass);
/* shutdown all old paths first */
for_each_card_rtds(card, fe) {
ret = soc_dpcm_fe_runtime_update(fe, 0);
@@ -2529,7 +2648,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
}
out:
- mutex_unlock(&card->mutex);
+ mutex_unlock(&card->pcm_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update);
@@ -2540,6 +2659,8 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream)
struct snd_soc_dpcm *dpcm;
int stream = fe_substream->stream;
+ snd_soc_dpcm_mutex_assert_held(fe);
+
/* mark FE's links ready to prune */
for_each_dpcm_be(fe, stream, dpcm)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
@@ -2554,12 +2675,12 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream);
int ret;
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ snd_soc_dpcm_mutex_lock(fe);
ret = dpcm_fe_dai_shutdown(fe_substream);
dpcm_fe_dai_cleanup(fe_substream);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_dpcm_mutex_unlock(fe);
return ret;
}
@@ -2570,7 +2691,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
int ret;
int stream = fe_substream->stream;
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ snd_soc_dpcm_mutex_lock(fe);
fe->dpcm[stream].runtime = fe_substream->runtime;
ret = dpcm_path_get(fe, stream, &list);
@@ -2587,7 +2708,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
dpcm_clear_pending_state(fe, stream);
dpcm_path_put(&list);
open_end:
- mutex_unlock(&fe->card->mutex);
+ snd_soc_dpcm_mutex_unlock(fe);
return ret;
}
@@ -2848,10 +2969,8 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
struct snd_soc_dpcm *dpcm;
int state;
int ret = 1;
- unsigned long flags;
int i;
- spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_fe(be, stream, dpcm) {
if (dpcm->fe == fe)
@@ -2865,7 +2984,6 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
}
}
}
- spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
/* it's safe to do this BE DAI */
return ret;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index f5b9e66ac3b8..2630df024dff 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -56,7 +56,7 @@ struct soc_tplg {
const struct firmware *fw;
/* runtime FW parsing */
- const u8 *pos; /* read postion */
+ const u8 *pos; /* read position */
const u8 *hdr_pos; /* header position */
unsigned int pass; /* pass number */
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index 041c54639c4d..1a7d6cefd3b7 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -40,12 +40,14 @@ config SND_SOC_SOF_ACPI_DEV
config SND_SOC_SOF_OF
tristate "SOF OF enumeration support"
depends on OF || COMPILE_TEST
- select SND_SOC_SOF
help
This adds support for Device Tree enumeration. This option is
- required to enable i.MX8 devices.
+ required to enable i.MX8 or Mediatek devices.
Say Y if you need this option. If unsure select "N".
+config SND_SOC_SOF_OF_DEV
+ tristate
+
config SND_SOC_SOF_COMPRESS
bool
select SND_SOC_COMPRESS
@@ -61,7 +63,7 @@ config SND_SOC_SOF_DEBUG_PROBES
config SND_SOC_SOF_DEVELOPER_SUPPORT
bool "SOF developer options support"
- depends on EXPERT
+ depends on EXPERT && SND_SOC_SOF
help
This option unlocks SOF developer options for debug/performance/
code hardening.
@@ -192,6 +194,14 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
Say Y if you want to enable IPC flood test.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR
+ bool "SOF enable IPC message injector"
+ help
+ This option enables the IPC message injector which can be used to send
+ crafted IPC messages to the DSP to test its robustness.
+ Say Y if you want to enable the IPC message injector.
+ If unsure, select "N".
+
config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT
bool "SOF retain DSP context on any FW exceptions"
help
@@ -223,8 +233,10 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE
When selected, the probe is handled in two steps, for example to
avoid lockdeps if request_module is used in the probe.
+source "sound/soc/sof/amd/Kconfig"
source "sound/soc/sof/imx/Kconfig"
source "sound/soc/sof/intel/Kconfig"
+source "sound/soc/sof/mediatek/Kconfig"
source "sound/soc/sof/xtensa/Kconfig"
endif
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 06e5f49f7ee8..964b429146be 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -17,9 +17,11 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o
obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o
-obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o
+obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o
obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
+obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/
obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/
+obj-$(CONFIG_SND_SOC_SOF_MTK_TOPLEVEL) += mediatek/
diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig
new file mode 100644
index 000000000000..085232e04582
--- /dev/null
+++ b/sound/soc/sof/amd/Kconfig
@@ -0,0 +1,33 @@
+# 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_SOF_AMD_TOPLEVEL
+ tristate "SOF support for AMD audio DSPs"
+ depends on X86 || COMPILE_TEST
+ help
+ This adds support for Sound Open Firmware for AMD platforms.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_SOF_AMD_TOPLEVEL
+
+config SND_SOC_SOF_AMD_COMMON
+ tristate
+ select SND_SOC_SOF
+ select SND_SOC_SOF_PCI_DEV
+ select SND_AMD_ACP_CONFIG
+ select SND_SOC_ACPI if ACPI
+ help
+ This option is not user-selectable but automatically handled by
+ 'select' statements at a higher level
+
+config SND_SOC_SOF_AMD_RENOIR
+ tristate "SOF support for RENOIR"
+ depends on SND_SOC_SOF_PCI
+ select SND_SOC_SOF_AMD_COMMON
+ help
+ Select this option for SOF support on AMD Renoir platform
+endif
diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile
new file mode 100644
index 000000000000..7b9f1a0af3c8
--- /dev/null
+++ b/sound/soc/sof/amd/Makefile
@@ -0,0 +1,11 @@
+# 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.
+
+snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o
+snd-sof-amd-renoir-objs := pci-rn.o renoir.o
+
+obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o
+obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
new file mode 100644
index 000000000000..63f13c111b24
--- /dev/null
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -0,0 +1,78 @@
+/* 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_DSP_IP_OFFSET_H
+#define _ACP_DSP_IP_OFFSET_H
+
+/* Registers from ACP_DMA_0 block */
+#define ACP_DMA_CNTL_0 0x00
+#define ACP_DMA_DSCR_STRT_IDX_0 0x20
+#define ACP_DMA_DSCR_CNT_0 0x40
+#define ACP_DMA_PRIO_0 0x60
+#define ACP_DMA_CUR_DSCR_0 0x80
+#define ACP_DMA_ERR_STS_0 0xC0
+#define ACP_DMA_DESC_BASE_ADDR 0xE0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0xE4
+#define ACP_DMA_CH_STS 0xE8
+#define ACP_DMA_CH_GROUP 0xEC
+#define ACP_DMA_CH_RST_STS 0xF0
+
+/* Registers from ACP_DSP_0 block */
+#define ACP_DSP0_RUNSTALL 0x414
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0xC10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0xC14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0xC18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0xC1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0xC28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0xC2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0xC30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0xC34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0xC38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0xC3C
+#define ACPAXI2AXI_ATU_CTRL 0xC40
+#define ACP_SOFT_RESET 0x1000
+
+#define ACP_I2S_PIN_CONFIG 0x1400
+
+/* Registers from ACP_PGFSM block */
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+
+/* Registers from ACP_INTR block */
+#define ACP_EXTERNAL_INTR_ENB 0x1800
+#define ACP_EXTERNAL_INTR_CNTL 0x1804
+#define ACP_EXTERNAL_INTR_STAT 0x1808
+#define ACP_DSP_SW_INTR_CNTL 0x1814
+#define ACP_DSP_SW_INTR_STAT 0x1818
+#define ACP_SW_INTR_TRIG 0x181C
+#define ACP_ERROR_STATUS 0x18C4
+
+/* Registers from ACP_SHA block */
+#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70
+#define ACP_SHA_DMA_CMD 0x1CB0
+#define ACP_SHA_MSG_LENGTH 0x1CB4
+#define ACP_SHA_DMA_STRT_ADDR 0x1CB8
+#define ACP_SHA_DMA_DESTINATION_ADDR 0x1CBC
+#define ACP_SHA_DMA_CMD_STS 0x1CC0
+#define ACP_SHA_DMA_ERR_STATUS 0x1CC4
+#define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8
+#define ACP_SHA_PSP_ACK 0x1C74
+
+#define ACP_SCRATCH_REG_0 0x10000
+
+#endif
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
new file mode 100644
index 000000000000..e132223b4c66
--- /dev/null
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -0,0 +1,187 @@
+// 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: Balakishore Pati <Balakishore.pati@amd.com>
+// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/* ACP-specific SOF IPC code */
+
+#include <linux/module.h>
+#include "../ops.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
+{
+ memcpy_to_scratch(sdev, offset, message, bytes);
+}
+EXPORT_SYMBOL_NS(acp_mailbox_write, SND_SOC_SOF_AMD_COMMON);
+
+void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
+{
+ memcpy_from_scratch(sdev, offset, message, bytes);
+}
+EXPORT_SYMBOL_NS(acp_mailbox_read, SND_SOC_SOF_AMD_COMMON);
+
+static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ u32 swintr_trigger;
+
+ swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG);
+ swintr_trigger |= 0x01;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger);
+}
+
+static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
+{
+ unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
+}
+
+static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
+{
+ unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
+}
+
+static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
+{
+ unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
+}
+
+int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
+
+ acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
+ acp_ipc_host_msg_set(sdev);
+
+ /* Trigger host to dsp interrupt for the msg */
+ acpbus_trigger_host_to_dsp_swintr(adata);
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
+
+static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc_reply reply;
+ struct sof_ipc_cmd_hdr *hdr;
+ unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
+ int ret = 0;
+
+ /*
+ * Sometimes, there is unexpected reply ipc arriving. The reply
+ * ipc belongs to none of the ipcs sent from driver.
+ * In this case, the driver must ignore the ipc.
+ */
+ if (!msg) {
+ dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
+ return;
+ }
+ hdr = msg->msg_data;
+ if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
+ hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
+ /*
+ * memory windows are powered off before sending IPC reply,
+ * so we can't read the mailbox for CTX_SAVE and PM_GATE
+ * replies.
+ */
+ reply.error = 0;
+ reply.hdr.cmd = SOF_IPC_GLB_REPLY;
+ reply.hdr.size = sizeof(reply);
+ memcpy(msg->reply_data, &reply, sizeof(reply));
+ goto out;
+ }
+ /* get IPC reply from DSP in the mailbox */
+ acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
+ if (reply.error < 0) {
+ memcpy(msg->reply_data, &reply, sizeof(reply));
+ ret = reply.error;
+ } else {
+ /* reply correct size ? */
+ if (reply.hdr.size != msg->reply_size &&
+ !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
+ dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
+ msg->reply_size, reply.hdr.size);
+ ret = -EINVAL;
+ }
+ /* read the message */
+ if (msg->reply_size > 0)
+ acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
+ }
+out:
+ msg->reply_error = ret;
+}
+
+irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
+ unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
+ bool ipc_irq = false;
+ int dsp_msg, dsp_ack;
+
+ dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
+ if (dsp_msg) {
+ snd_sof_ipc_msgs_rx(sdev);
+ acp_dsp_ipc_host_done(sdev);
+ ipc_irq = true;
+ }
+
+ dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
+ if (dsp_ack) {
+ spin_lock_irq(&sdev->ipc_lock);
+ /* handle immediate reply from DSP core */
+ acp_dsp_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, 0);
+ /* set the done bit */
+ acp_dsp_ipc_dsp_done(sdev);
+ spin_unlock_irq(&sdev->ipc_lock);
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq)
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
+
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ void *p, size_t sz)
+{
+ unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
+
+ if (!substream || !sdev->stream_box.size)
+ acp_mailbox_read(sdev, offset, p, sz);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
+
+int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ const struct sof_ipc_pcm_params_reply *reply)
+{
+ /* TODO: Implement stream hw params to validate stream offset */
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON);
+
+int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return ACP_SCRATCH_MEMORY_ADDRESS;
+}
+EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
+
+MODULE_DESCRIPTION("AMD ACP sof-ipc driver");
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
new file mode 100644
index 000000000000..2dc15ae38155
--- /dev/null
+++ b/sound/soc/sof/amd/acp-loader.c
@@ -0,0 +1,199 @@
+// 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 ACP DSP Firmware binaries loader
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../ops.h"
+#include "acp-dsp-offset.h"
+#include "acp.h"
+
+#define FW_BIN 0
+#define FW_DATA_BIN 1
+
+#define FW_BIN_PTE_OFFSET 0x00
+#define FW_DATA_BIN_PTE_OFFSET 0x08
+
+#define ACP_DSP_RUN 0x00
+
+int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *dest, size_t size)
+{
+ switch (blk_type) {
+ case SOF_FW_BLK_TYPE_SRAM:
+ offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
+ memcpy_from_scratch(sdev, offset, dest, size);
+ break;
+ default:
+ dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *src, size_t size)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct acp_dev_data *adata;
+ void *dest;
+ u32 dma_size, page_count;
+ unsigned int size_fw;
+
+ adata = sdev->pdata->hw_pdata;
+
+ switch (blk_type) {
+ case SOF_FW_BLK_TYPE_IRAM:
+ if (!adata->bin_buf) {
+ size_fw = plat_data->fw->size;
+ page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
+ dma_size = page_count * ACP_PAGE_SIZE;
+ adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size,
+ &adata->sha_dma_addr,
+ GFP_ATOMIC);
+ if (!adata->bin_buf)
+ return -ENOMEM;
+ }
+ adata->fw_bin_size = size + offset;
+ dest = adata->bin_buf + offset;
+ break;
+ case SOF_FW_BLK_TYPE_DRAM:
+ if (!adata->data_buf) {
+ adata->data_buf = dma_alloc_coherent(&pci->dev,
+ ACP_DEFAULT_DRAM_LENGTH,
+ &adata->dma_addr,
+ GFP_ATOMIC);
+ if (!adata->data_buf)
+ return -ENOMEM;
+ }
+ dest = adata->data_buf + offset;
+ adata->fw_data_bin_size = size + offset;
+ break;
+ case SOF_FW_BLK_TYPE_SRAM:
+ offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
+ memcpy_to_scratch(sdev, offset, src, size);
+ return 0;
+ default:
+ dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
+ return -EINVAL;
+ }
+
+ memcpy(dest, src, size);
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_block_write, SND_SOC_SOF_AMD_COMMON);
+
+int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ return type;
+}
+EXPORT_SYMBOL_NS(acp_get_bar_index, SND_SOC_SOF_AMD_COMMON);
+
+static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata)
+{
+ struct snd_sof_dev *sdev;
+ unsigned int low, high;
+ dma_addr_t addr;
+ u16 page_idx;
+ u32 offset;
+
+ sdev = adata->dev;
+
+ switch (type) {
+ case FW_BIN:
+ offset = FW_BIN_PTE_OFFSET;
+ addr = adata->sha_dma_addr;
+ break;
+ case FW_DATA_BIN:
+ offset = adata->fw_bin_page_count * 8;
+ addr = adata->dma_addr;
+ break;
+ default:
+ dev_err(sdev->dev, "Invalid data type %x\n", type);
+ return;
+ }
+
+ for (page_idx = 0; page_idx < num_pages; page_idx++) {
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
+ high |= BIT(31);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
+ offset += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+/* pre fw run operations */
+int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+{
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct acp_dev_data *adata;
+ unsigned int src_addr, size_fw;
+ u32 page_count, dma_size;
+ int ret;
+
+ adata = sdev->pdata->hw_pdata;
+ size_fw = adata->fw_bin_size;
+
+ page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
+ adata->fw_bin_page_count = page_count;
+
+ configure_pte_for_fw_loading(FW_BIN, page_count, adata);
+ ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW,
+ ACP_IRAM_BASE_ADDRESS, size_fw);
+ if (ret < 0) {
+ dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret);
+ return ret;
+ }
+ configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata);
+
+ src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE;
+ ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS,
+ adata->fw_data_bin_size);
+ if (ret < 0) {
+ dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = acp_dma_status(adata, 0);
+ if (ret < 0)
+ dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
+
+ /* Free memory once DMA is complete */
+ dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
+ dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
+ dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr);
+ adata->bin_buf = NULL;
+ adata->data_buf = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON);
+
+int acp_sof_dsp_run(struct snd_sof_dev *sdev)
+{
+ int val;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN);
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL);
+ dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c
new file mode 100644
index 000000000000..5b23830cb1f3
--- /dev/null
+++ b/sound/soc/sof/amd/acp-pcm.c
@@ -0,0 +1,82 @@
+// 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>
+
+/*
+ * PCM interface for generic AMD audio ACP DSP block
+ */
+#include <sound/pcm_params.h>
+
+#include "../ops.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params)
+{
+ struct acp_dsp_stream *stream = substream->runtime->private_data;
+ unsigned int buf_offset, index;
+ u32 size;
+ int ret;
+
+ size = ipc_params->buffer.size;
+ stream->num_pages = ipc_params->buffer.pages;
+ stream->dmab = substream->runtime->dma_buffer_p;
+
+ ret = acp_dsp_stream_config(sdev, stream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "stream configuration failed\n");
+ return ret;
+ }
+
+ ipc_params->buffer.phy_addr = stream->reg_offset;
+ ipc_params->stream_tag = stream->stream_tag;
+
+ /* write buffer size of stream in scratch memory */
+
+ buf_offset = offsetof(struct scratch_reg_conf, buf_size);
+ index = stream->stream_tag - 1;
+ buf_offset = buf_offset + index * 4;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_pcm_hw_params, SND_SOC_SOF_AMD_COMMON);
+
+int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+ struct acp_dsp_stream *stream;
+
+ stream = acp_dsp_stream_get(sdev, 0);
+ if (!stream)
+ return -ENODEV;
+
+ substream->runtime->private_data = stream;
+ stream->substream = substream;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_pcm_open, SND_SOC_SOF_AMD_COMMON);
+
+int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+ struct acp_dsp_stream *stream;
+
+ stream = substream->runtime->private_data;
+ if (!stream) {
+ dev_err(sdev->dev, "No open stream\n");
+ return -EINVAL;
+ }
+
+ stream->substream = NULL;
+ substream->runtime->private_data = NULL;
+
+ return acp_dsp_stream_put(sdev, stream);
+}
+EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c
new file mode 100644
index 000000000000..f2837bfbdb20
--- /dev/null
+++ b/sound/soc/sof/amd/acp-stream.c
@@ -0,0 +1,181 @@
+// 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 generic AMD audio DSP ACP IP
+ */
+
+#include "../ops.h"
+#include "acp-dsp-offset.h"
+#include "acp.h"
+
+#define PTE_GRP1_OFFSET 0x00000000
+#define PTE_GRP2_OFFSET 0x00800000
+#define PTE_GRP3_OFFSET 0x01000000
+#define PTE_GRP4_OFFSET 0x01800000
+#define PTE_GRP5_OFFSET 0x02000000
+#define PTE_GRP6_OFFSET 0x02800000
+#define PTE_GRP7_OFFSET 0x03000000
+#define PTE_GRP8_OFFSET 0x03800000
+
+int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream)
+{
+ unsigned int pte_reg, pte_size, phy_addr_offset, index;
+ int stream_tag = stream->stream_tag;
+ u32 low, high, offset, reg_val;
+ dma_addr_t addr;
+ int page_idx;
+
+ switch (stream_tag) {
+ case 1:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1;
+ offset = offsetof(struct scratch_reg_conf, grp1_pte);
+ stream->reg_offset = PTE_GRP1_OFFSET;
+ break;
+ case 2:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2;
+ offset = offsetof(struct scratch_reg_conf, grp2_pte);
+ stream->reg_offset = PTE_GRP2_OFFSET;
+ break;
+ case 3:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3;
+ offset = offsetof(struct scratch_reg_conf, grp3_pte);
+ stream->reg_offset = PTE_GRP3_OFFSET;
+ break;
+ case 4:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4;
+ offset = offsetof(struct scratch_reg_conf, grp4_pte);
+ stream->reg_offset = PTE_GRP4_OFFSET;
+ break;
+ case 5:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
+ offset = offsetof(struct scratch_reg_conf, grp5_pte);
+ stream->reg_offset = PTE_GRP5_OFFSET;
+ break;
+ case 6:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6;
+ offset = offsetof(struct scratch_reg_conf, grp6_pte);
+ stream->reg_offset = PTE_GRP6_OFFSET;
+ break;
+ case 7:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7;
+ offset = offsetof(struct scratch_reg_conf, grp7_pte);
+ stream->reg_offset = PTE_GRP7_OFFSET;
+ break;
+ case 8:
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8;
+ offset = offsetof(struct scratch_reg_conf, grp8_pte);
+ stream->reg_offset = PTE_GRP8_OFFSET;
+ break;
+ default:
+ dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag);
+ return -EINVAL;
+ }
+
+ /* write phy_addr in scratch memory */
+
+ phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset);
+ index = stream_tag - 1;
+ phy_addr_offset = phy_addr_offset + index * 4;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 +
+ phy_addr_offset, stream->reg_offset);
+
+ /* Group Enable */
+ reg_val = ACP_SRAM_PTE_OFFSET + offset;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31));
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE);
+
+ for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
+ addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE);
+
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
+
+ high |= BIT(31);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
+ /* Move to next physically contiguous page */
+ offset += 8;
+ }
+
+ return 0;
+}
+
+struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ struct acp_dsp_stream *stream = adata->stream_buf;
+ int i;
+
+ for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
+ if (stream->active)
+ continue;
+
+ /* return stream if tag not specified*/
+ if (!tag) {
+ stream->active = 1;
+ return stream;
+ }
+
+ /* check if this is the requested stream tag */
+ if (stream->stream_tag == tag) {
+ stream->active = 1;
+ return stream;
+ }
+ }
+
+ dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag);
+ return NULL;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_stream_put(struct snd_sof_dev *sdev,
+ struct acp_dsp_stream *acp_stream)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ struct acp_dsp_stream *stream = adata->stream_buf;
+ int i;
+
+ /* Free an active stream */
+ for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
+ if (stream == acp_stream) {
+ stream->active = 0;
+ return 0;
+ }
+ }
+
+ dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_stream_init(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ int i;
+
+ for (i = 0; i < ACP_MAX_STREAM; i++) {
+ adata->stream_buf[i].sdev = sdev;
+ adata->stream_buf[i].active = 0;
+ adata->stream_buf[i].stream_tag = i + 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c
new file mode 100644
index 000000000000..fa4da8947186
--- /dev/null
+++ b/sound/soc/sof/amd/acp-trace.c
@@ -0,0 +1,84 @@
+// 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.
+//
+// Authors: Vishnuvardhanrao Ravuapati <vishnuvardhanrao.ravulapati@amd.com>
+// V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com>
+
+/*This file support Host TRACE Logger driver callback for SOF FW */
+
+#include "acp.h"
+
+#define ACP_LOGGER_STREAM 8
+#define NUM_PAGES 16
+
+int acp_sof_trace_release(struct snd_sof_dev *sdev)
+{
+ struct acp_dsp_stream *stream;
+ struct acp_dev_data *adata;
+ int ret;
+
+ adata = sdev->pdata->hw_pdata;
+ stream = adata->dtrace_stream;
+ ret = acp_dsp_stream_put(sdev, stream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to release trace stream\n");
+ return ret;
+ }
+
+ adata->dtrace_stream = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON);
+
+static int acp_sof_trace_prepare(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_params_ext *params)
+{
+ struct acp_dsp_stream *stream;
+ struct acp_dev_data *adata;
+ int ret;
+
+ adata = sdev->pdata->hw_pdata;
+ stream = adata->dtrace_stream;
+ stream->dmab = &sdev->dmatb;
+ stream->num_pages = NUM_PAGES;
+
+ ret = acp_dsp_stream_config(sdev, stream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to configure trace stream\n");
+ return ret;
+ }
+
+ params->buffer.phy_addr = stream->reg_offset;
+ params->stream_tag = stream->stream_tag;
+
+ return 0;
+}
+
+int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
+{
+ struct sof_ipc_dma_trace_params_ext *params;
+ struct acp_dsp_stream *stream;
+ struct acp_dev_data *adata;
+ int ret;
+
+ adata = sdev->pdata->hw_pdata;
+ stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM);
+ if (!stream)
+ return -ENODEV;
+
+ adata->dtrace_stream = stream;
+ params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag);
+ ret = acp_sof_trace_prepare(sdev, params);
+ if (ret < 0) {
+ acp_dsp_stream_put(sdev, stream);
+ return ret;
+ }
+
+ *stream_tag = stream->stream_tag;
+ return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
new file mode 100644
index 000000000000..fe9b7dc5bc86
--- /dev/null
+++ b/sound/soc/sof/amd/acp.c
@@ -0,0 +1,446 @@
+// 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.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Hardware interface for generic AMD ACP processor
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../ops.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)
+{
+ pci_write_config_dword(dev, 0x60, smn_addr);
+ pci_write_config_dword(dev, 0x64, data);
+
+ return 0;
+}
+
+static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data)
+{
+ pci_write_config_dword(dev, 0x60, smn_addr);
+ pci_read_config_dword(dev, 0x64, data);
+
+ return 0;
+}
+
+static void configure_acp_groupregisters(struct acp_dev_data *adata)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+
+ /* Group Enable */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
+ ACP_SRAM_PTE_OFFSET | BIT(31));
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
+ PAGE_SIZE_4K_ENABLE);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
+}
+
+static void init_dma_descriptor(struct acp_dev_data *adata)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int addr;
+
+ addr = ACP_SRAM_PTE_OFFSET + offsetof(struct scratch_reg_conf, dma_desc);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_BASE_ADDR, addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_MAX_NUM_DSCR, ACP_MAX_DESC_CNT);
+}
+
+static void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx,
+ struct dma_descriptor *dscr_info)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int offset;
+
+ offset = ACP_SCRATCH_REG_0 + offsetof(struct scratch_reg_conf, dma_desc) +
+ idx * sizeof(struct dma_descriptor);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all);
+}
+
+static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch,
+ unsigned int idx, unsigned int dscr_count)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int val, status;
+ int ret;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32),
+ ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_RST_STS, val,
+ val & (1 << ch), ACP_REG_POLL_INTERVAL,
+ ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0) {
+ status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_ERROR_STATUS);
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_ERR_STS_0 + ch * sizeof(u32));
+
+ dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status);
+ return ret;
+ }
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, (ACP_DMA_CNTL_0 + ch * sizeof(u32)), 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_CNT_0 + ch * sizeof(u32), dscr_count);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_STRT_IDX_0 + ch * sizeof(u32), idx);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_PRIO_0 + ch * sizeof(u32), 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), ACP_DMA_CH_RUN);
+
+ return ret;
+}
+
+static int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch,
+ unsigned int dscr_count, struct dma_descriptor *dscr_info)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ int ret;
+ u16 dscr;
+
+ if (!dscr_info || !dscr_count)
+ return -EINVAL;
+
+ for (dscr = 0; dscr < dscr_count; dscr++)
+ configure_dma_descriptor(adata, dscr, dscr_info++);
+
+ ret = config_dma_channel(adata, ch, 0, dscr_count);
+ if (ret < 0)
+ dev_err(sdev->dev, "config dma ch failed:%d\n", ret);
+
+ return ret;
+}
+
+int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
+ unsigned int dest_addr, int dsp_data_size)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int desc_count, index;
+ int ret;
+
+ for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0;
+ desc_count++, dsp_data_size -= ACP_PAGE_SIZE) {
+ adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE;
+ adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE;
+ adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE;
+ if (dsp_data_size < ACP_PAGE_SIZE)
+ adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size;
+ }
+
+ ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info);
+ if (ret)
+ dev_err(sdev->dev, "acpbus_dma_start failed\n");
+
+ /* Clear descriptor array */
+ for (index = 0; index < desc_count; index++)
+ memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor));
+
+ return ret;
+}
+
+static int psp_fw_validate(struct acp_dev_data *adata)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ int timeout;
+ u32 data;
+
+ smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND);
+
+ for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
+ msleep(20);
+ smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data);
+ if (data & MBOX_READY_MASK)
+ return 0;
+ }
+
+ dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK);
+ return -ETIMEDOUT;
+}
+
+int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
+ unsigned int start_addr, unsigned int dest_addr,
+ unsigned int image_length)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int tx_count, fw_qualifier, val;
+ int ret;
+
+ if (!image_addr) {
+ dev_err(sdev->dev, "SHA DMA image address is NULL\n");
+ return -EINVAL;
+ }
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD);
+ if (val & ACP_SHA_RUN) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET);
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS,
+ val, val & ACP_SHA_RESET,
+ ACP_REG_POLL_INTERVAL,
+ ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "SHA DMA Failed to Reset\n");
+ return ret;
+ }
+ }
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT,
+ tx_count, tx_count == image_length,
+ ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count);
+ return ret;
+ }
+
+ ret = psp_fw_validate(adata);
+ if (ret)
+ return ret;
+
+ fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER);
+ if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) {
+ dev_err(sdev->dev, "PSP validation failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ unsigned int val;
+ int ret = 0;
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32));
+ if (val & ACP_DMA_CH_RUN) {
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_STS, val, !val,
+ ACP_REG_POLL_INTERVAL,
+ ACP_DMA_COMPLETE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch);
+ }
+
+ return ret;
+}
+
+void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes)
+{
+ unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
+ int i, j;
+
+ for (i = 0, j = 0; i < bytes; i = i + 4, j++)
+ dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i);
+}
+
+void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes)
+{
+ unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
+ int i, j;
+
+ for (i = 0, j = 0; i < bytes; i = i + 4, j++)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]);
+}
+
+static int acp_memory_init(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+
+ snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL,
+ ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
+ configure_acp_groupregisters(adata);
+ init_dma_descriptor(adata);
+
+ return 0;
+}
+
+static irqreturn_t acp_irq_thread(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ unsigned int val;
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT);
+ if (val & ACP_SHA_STAT) {
+ /* Clear SHA interrupt raised by PSP */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT, val);
+ return IRQ_HANDLED;
+ }
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT);
+ if (val & ACP_DSP_TO_HOST_IRQ) {
+ sof_ops(sdev)->irq_thread(irq, sdev);
+ val |= ACP_DSP_TO_HOST_IRQ;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+};
+
+static irqreturn_t acp_irq_handler(int irq, void *dev_id)
+{
+ struct snd_sof_dev *sdev = dev_id;
+ unsigned int val;
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT);
+ if (val)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static int acp_power_on(struct snd_sof_dev *sdev)
+{
+ unsigned int val;
+ int ret;
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS);
+
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if (val & ACP_PGFSM_STATUS_MASK)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_PGFSM_CONTROL,
+ ACP_PGFSM_CNTL_POWER_ON_MASK);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS, val, !val,
+ ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n");
+
+ return ret;
+}
+
+static int acp_reset(struct snd_sof_dev *sdev)
+{
+ unsigned int val;
+ int ret;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_ASSERT_RESET);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_DONE_MASK,
+ ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "timeout asserting reset\n");
+ return ret;
+ }
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_RELEASE_RESET);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
+ ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "timeout in releasing reset\n");
+
+ return ret;
+}
+
+static int acp_init(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* power on */
+ ret = acp_power_on(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP power on failed\n");
+ return ret;
+ }
+ /* Reset */
+ return acp_reset(sdev);
+}
+
+int amd_sof_acp_probe(struct snd_sof_dev *sdev)
+{
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct acp_dev_data *adata;
+ const struct sof_amd_acp_desc *chip;
+ unsigned int addr;
+ int ret;
+
+ adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->dev = sdev;
+ addr = pci_resource_start(pci, ACP_DSP_BAR);
+ sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR));
+ if (!sdev->bar[ACP_DSP_BAR]) {
+ dev_err(sdev->dev, "ioremap error\n");
+ return -ENXIO;
+ }
+
+ pci_set_master(pci);
+
+ sdev->pdata->hw_pdata = adata;
+
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
+ return -EIO;
+ }
+
+ adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
+ if (!adata->smn_dev) {
+ dev_err(sdev->dev, "Failed to get host bridge device\n");
+ return -ENODEV;
+ }
+
+ sdev->ipc_irq = pci->irq;
+ ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread,
+ IRQF_SHARED, "AudioDSP", sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to register IRQ %d\n",
+ sdev->ipc_irq);
+ pci_dev_put(adata->smn_dev);
+ return ret;
+ }
+
+ ret = acp_init(sdev);
+ if (ret < 0) {
+ free_irq(sdev->ipc_irq, sdev);
+ pci_dev_put(adata->smn_dev);
+ return ret;
+ }
+
+ acp_memory_init(sdev);
+
+ acp_dsp_stream_init(sdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
+
+int amd_sof_acp_remove(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+
+ if (adata->smn_dev)
+ pci_dev_put(adata->smn_dev);
+
+ if (sdev->ipc_irq)
+ free_irq(sdev->ipc_irq, sdev);
+
+ return acp_reset(sdev);
+}
+EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
+
+MODULE_DESCRIPTION("AMD ACP sof driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
new file mode 100644
index 000000000000..a2f8e4219066
--- /dev/null
+++ b/sound/soc/sof/amd/acp.h
@@ -0,0 +1,226 @@
+/* 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 __SOF_AMD_ACP_H
+#define __SOF_AMD_ACP_H
+
+#include "../sof-priv.h"
+
+#define ACP_MAX_STREAM 8
+
+#define ACP_DSP_BAR 0
+
+#define ACP_REG_POLL_INTERVAL 500
+#define ACP_REG_POLL_TIMEOUT_US 2000
+#define ACP_DMA_COMPLETE_TIMEOUT_US 5000
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_ASSERT_RESET 0x01
+#define ACP_RELEASE_RESET 0x00
+#define ACP_SOFT_RESET_DONE_MASK 0x00010001
+
+#define ACP_DSP_INTR_EN_MASK 0x00000001
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define ACP_PAGE_SIZE 0x1000
+#define ACP_DMA_CH_RUN 0x02
+#define ACP_MAX_DESC_CNT 0x02
+#define DSP_FW_RUN_ENABLE 0x01
+#define ACP_SHA_RUN 0x01
+#define ACP_SHA_RESET 0x02
+#define ACP_DMA_CH_RST 0x01
+#define ACP_DMA_CH_GRACEFUL_RST_EN 0x10
+#define ACP_ATU_CACHE_INVALID 0x01
+#define ACP_MAX_DESC 128
+#define ACPBUS_REG_BASE_OFFSET ACP_DMA_CNTL_0
+
+#define ACP_DEFAULT_DRAM_LENGTH 0x00080000
+#define ACP_SCRATCH_MEMORY_ADDRESS 0x02050000
+#define ACP_SYSTEM_MEMORY_WINDOW 0x4000000
+#define ACP_IRAM_BASE_ADDRESS 0x000000
+#define ACP_DATA_RAM_BASE_ADDRESS 0x01000000
+#define ACP_DRAM_PAGE_COUNT 128
+
+#define ACP_DSP_TO_HOST_IRQ 0x04
+
+#define HOST_BRIDGE_CZN 0x1630
+#define ACP_SHA_STAT 0x8000
+#define ACP_PSP_TIMEOUT_COUNTER 5
+#define ACP_EXT_INTR_ERROR_STAT 0x20000000
+#define MP0_C2PMSG_26_REG 0x03810570
+#define MBOX_ACP_SHA_DMA_COMMAND 0x330000
+#define MBOX_READY_MASK 0x80000000
+#define MBOX_STATUS_MASK 0xFFFF
+
+struct acp_atu_grp_pte {
+ u32 low;
+ u32 high;
+};
+
+union dma_tx_cnt {
+ struct {
+ unsigned int count : 19;
+ unsigned int reserved : 12;
+ unsigned ioc : 1;
+ } bitfields, bits;
+ unsigned int u32_all;
+ signed int i32_all;
+};
+
+struct dma_descriptor {
+ unsigned int src_addr;
+ unsigned int dest_addr;
+ union dma_tx_cnt tx_cnt;
+ unsigned int reserved;
+};
+
+/* Scratch memory structure for communication b/w host and dsp */
+struct scratch_ipc_conf {
+ /* DSP mailbox */
+ u8 sof_out_box[512];
+ /* Host mailbox */
+ u8 sof_in_box[512];
+ /* Debug memory */
+ u8 sof_debug_box[1024];
+ /* Exception memory*/
+ u8 sof_except_box[1024];
+ /* Stream buffer */
+ u8 sof_stream_box[1024];
+ /* Trace buffer */
+ u8 sof_trace_box[1024];
+ /* Host msg flag */
+ u32 sof_host_msg_write;
+ /* Host ack flag*/
+ u32 sof_host_ack_write;
+ /* DSP msg flag */
+ u32 sof_dsp_msg_write;
+ /* Dsp ack flag */
+ u32 sof_dsp_ack_write;
+};
+
+struct scratch_reg_conf {
+ struct scratch_ipc_conf info;
+ struct acp_atu_grp_pte grp1_pte[16];
+ struct acp_atu_grp_pte grp2_pte[16];
+ struct acp_atu_grp_pte grp3_pte[16];
+ struct acp_atu_grp_pte grp4_pte[16];
+ struct acp_atu_grp_pte grp5_pte[16];
+ struct acp_atu_grp_pte grp6_pte[16];
+ struct acp_atu_grp_pte grp7_pte[16];
+ struct acp_atu_grp_pte grp8_pte[16];
+ struct dma_descriptor dma_desc[64];
+ unsigned int reg_offset[8];
+ unsigned int buf_size[8];
+ u8 acp_tx_fifo_buf[256];
+ u8 acp_rx_fifo_buf[256];
+ unsigned int reserve[];
+};
+
+struct acp_dsp_stream {
+ struct list_head list;
+ struct snd_sof_dev *sdev;
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *dmab;
+ int num_pages;
+ int stream_tag;
+ int active;
+ unsigned int reg_offset;
+};
+
+/* Common device data struct for ACP devices */
+struct acp_dev_data {
+ struct snd_sof_dev *dev;
+ unsigned int fw_bin_size;
+ unsigned int fw_data_bin_size;
+ u32 fw_bin_page_count;
+ dma_addr_t sha_dma_addr;
+ u8 *bin_buf;
+ dma_addr_t dma_addr;
+ u8 *data_buf;
+ struct dma_descriptor dscr_info[ACP_MAX_DESC];
+ struct acp_dsp_stream stream_buf[ACP_MAX_STREAM];
+ struct acp_dsp_stream *dtrace_stream;
+ struct pci_dev *smn_dev;
+};
+
+void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
+void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes);
+
+int acp_dma_status(struct acp_dev_data *adata, unsigned char ch);
+int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
+ unsigned int dest_addr, int dsp_data_size);
+int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
+ unsigned int start_addr, unsigned int dest_addr,
+ unsigned int image_length);
+
+/* ACP device probe/remove */
+int amd_sof_acp_probe(struct snd_sof_dev *sdev);
+int amd_sof_acp_remove(struct snd_sof_dev *sdev);
+
+/* DSP Loader callbacks */
+int acp_sof_dsp_run(struct snd_sof_dev *sdev);
+int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev);
+int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type);
+
+/* Block IO callbacks */
+int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *src, size_t size);
+int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+ u32 offset, void *dest, size_t size);
+
+/* IPC callbacks */
+irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context);
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ void *p, size_t sz);
+int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev,
+ struct snd_sof_ipc_msg *msg);
+int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev);
+int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
+int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ const struct sof_ipc_pcm_params_reply *reply);
+void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
+void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
+
+/* ACP - DSP stream callbacks */
+int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream);
+int acp_dsp_stream_init(struct snd_sof_dev *sdev);
+struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag);
+int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stream);
+
+/*
+ * DSP PCM Operations.
+ */
+int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
+int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
+int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params);
+
+extern const struct snd_sof_dsp_ops sof_renoir_ops;
+
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+/* Trace */
+int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
+int acp_sof_trace_release(struct snd_sof_dev *sdev);
+
+struct sof_amd_acp_desc {
+ unsigned int host_bridge_id;
+};
+
+static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata *pdata)
+{
+ const struct sof_dev_desc *desc = pdata->desc;
+
+ return desc->chip_info;
+}
+#endif
diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c
new file mode 100644
index 000000000000..392ffbdf6417
--- /dev/null
+++ b/sound/soc/sof/amd/pci-rn.c
@@ -0,0 +1,165 @@
+// 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.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * PCI interface for Renoir ACP device
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <sound/sof.h>
+#include <sound/soc-acpi.h>
+
+#include "../ops.h"
+#include "../sof-pci-dev.h"
+#include "../../amd/mach-config.h"
+#include "acp.h"
+
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x125C000
+
+static struct platform_device *dmic_dev;
+static struct platform_device *pdev;
+
+static const struct resource renoir_res[] = {
+ {
+ .start = 0,
+ .end = ACP3x_REG_END - ACP3x_REG_START,
+ .name = "acp_mem",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .name = "acp_dai_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct sof_amd_acp_desc renoir_chip_info = {
+ .host_bridge_id = HOST_BRIDGE_CZN,
+};
+
+static const struct sof_dev_desc renoir_desc = {
+ .machines = snd_soc_acpi_amd_sof_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &renoir_chip_info,
+ .default_fw_path = "amd/sof",
+ .default_tplg_path = "amd/sof-tplg",
+ .default_fw_filename = "sof-rn.ri",
+ .nocodec_tplg_filename = "sof-acp.tplg",
+ .ops = &sof_renoir_ops,
+};
+
+static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct platform_device_info pdevinfo;
+ struct device *dev = &pci->dev;
+ const struct resource *res_i2s;
+ struct resource *res;
+ unsigned int flag, i, addr;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
+ return -ENODEV;
+
+ ret = sof_pci_probe(pci, pci_id);
+ if (ret != 0)
+ return ret;
+
+ dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(dmic_dev)) {
+ dev_err(dev, "failed to create DMIC device\n");
+ sof_pci_remove(pci);
+ return PTR_ERR(dmic_dev);
+ }
+
+ /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
+ if (flag != FLAG_AMD_SOF_ONLY_DMIC)
+ return 0;
+
+ addr = pci_resource_start(pci, 0);
+ res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
+ if (!res) {
+ sof_pci_remove(pci);
+ return -ENOMEM;
+ }
+
+ res_i2s = renoir_res;
+ for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) {
+ res[i].name = res_i2s->name;
+ res[i].flags = res_i2s->flags;
+ res[i].start = addr + res_i2s->start;
+ res[i].end = addr + res_i2s->end;
+ if (res_i2s->flags == IORESOURCE_IRQ) {
+ res[i].start = pci->irq;
+ res[i].end = res[i].start;
+ }
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ /*
+ * We have common PCI driver probe for ACP device but we have to support I2S without SOF
+ * for some distributions. Register platform device that will be used to support non dsp
+ * ACP's audio ends points on some machines.
+ */
+
+ pdevinfo.name = "acp_asoc_renoir";
+ pdevinfo.id = 0;
+ pdevinfo.parent = &pci->dev;
+ pdevinfo.num_res = ARRAY_SIZE(renoir_res);
+ pdevinfo.res = &res[0];
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
+ sof_pci_remove(pci);
+ platform_device_unregister(dmic_dev);
+ ret = PTR_ERR(pdev);
+ }
+
+ return ret;
+};
+
+static void acp_pci_rn_remove(struct pci_dev *pci)
+{
+ if (dmic_dev)
+ platform_device_unregister(dmic_dev);
+ if (pdev)
+ platform_device_unregister(pdev);
+
+ return sof_pci_remove(pci);
+}
+
+/* PCI IDs */
+static const struct pci_device_id rn_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
+ .driver_data = (unsigned long)&renoir_desc},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, rn_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_sof_pci_amd_rn_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rn_pci_ids,
+ .probe = acp_pci_rn_probe,
+ .remove = acp_pci_rn_remove,
+};
+module_pci_driver(snd_sof_pci_amd_rn_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
+MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c
new file mode 100644
index 000000000000..c3ecb9e9d5ba
--- /dev/null
+++ b/sound/soc/sof/amd/renoir.c
@@ -0,0 +1,186 @@
+// 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 Audio DSP on Renoir platform
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include "../ops.h"
+#include "../sof-audio.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+#define I2S_BT_INSTANCE 0
+#define I2S_SP_INSTANCE 1
+#define PDM_DMIC_INSTANCE 2
+
+#define I2S_MODE 0x04
+
+static int renoir_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_I2S_PIN_CONFIG);
+ if (val != I2S_MODE) {
+ dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver renoir_sof_dai[] = {
+ [I2S_BT_INSTANCE] = {
+ .id = I2S_BT_INSTANCE,
+ .name = "acp-sof-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 = {
+ .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,
+ /* Supporting only stereo for I2S BT controller capture */
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .probe = &renoir_dai_probe,
+ },
+
+ [I2S_SP_INSTANCE] = {
+ .id = I2S_SP_INSTANCE,
+ .name = "acp-sof-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 = {
+ .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,
+ /* Supporting only stereo for I2S SP controller capture */
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .probe = &renoir_dai_probe,
+ },
+
+ [PDM_DMIC_INSTANCE] = {
+ .id = PDM_DMIC_INSTANCE,
+ .name = "acp-sof-dmic",
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ },
+};
+
+static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *sof_pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = sof_pdata->desc;
+ struct snd_soc_acpi_mach *mach;
+
+ mach = snd_soc_acpi_find_machine(desc->machines);
+ if (!mach) {
+ dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
+ return NULL;
+ }
+
+ sof_pdata->tplg_filename = mach->sof_tplg_filename;
+ sof_pdata->fw_filename = mach->fw_filename;
+
+ return mach;
+}
+
+/* AMD Renoir DSP ops */
+const struct snd_sof_dsp_ops sof_renoir_ops = {
+ /* probe and remove */
+ .probe = amd_sof_acp_probe,
+ .remove = amd_sof_acp_remove,
+
+ /* Register IO */
+ .write = sof_io_write,
+ .read = sof_io_read,
+
+ /* Block IO */
+ .block_read = acp_dsp_block_read,
+ .block_write = acp_dsp_block_write,
+
+ /* Module loading */
+ .load_module = snd_sof_parse_module_memcpy,
+
+ /*Firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+ .pre_fw_run = acp_dsp_pre_fw_run,
+ .get_bar_index = acp_get_bar_index,
+
+ /* DSP core boot */
+ .run = acp_sof_dsp_run,
+
+ /*IPC */
+ .send_msg = acp_sof_ipc_send_msg,
+ .ipc_msg_data = acp_sof_ipc_msg_data,
+ .ipc_pcm_params = acp_sof_ipc_pcm_params,
+ .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset,
+ .irq_thread = acp_sof_ipc_irq_thread,
+ .fw_ready = sof_fw_ready,
+
+ /* DAI drivers */
+ .drv = renoir_sof_dai,
+ .num_drv = ARRAY_SIZE(renoir_sof_dai),
+
+ /* stream callbacks */
+ .pcm_open = acp_pcm_open,
+ .pcm_close = acp_pcm_close,
+ .pcm_hw_params = acp_pcm_hw_params,
+
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+
+ /* Machine driver callbacks */
+ .machine_select = amd_sof_machine_select,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
+ /* Trace Logger */
+ .trace_init = acp_sof_trace_init,
+ .trace_release = acp_sof_trace_release,
+};
+EXPORT_SYMBOL(sof_renoir_ops);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
+MODULE_DESCRIPTION("RENOIR SOF Driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index bb1dfe4f6d40..ef61936dad59 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -69,7 +69,6 @@ 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;
- u32 ipc_cmd;
int ret;
if (!scontrol->comp_data_dirty)
@@ -78,20 +77,13 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
if (!pm_runtime_active(scomp->dev))
return;
- if (scontrol->cmd == SOF_CTRL_CMD_BINARY)
- ipc_cmd = SOF_IPC_COMP_GET_DATA;
- else
- ipc_cmd = 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, ipc_cmd,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- scontrol->cmd, false);
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, 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 */
@@ -142,11 +134,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_SET,
- SOF_CTRL_CMD_VOLUME,
- true);
+ snd_sof_ipc_set_get_comp_data(scontrol, true);
return change;
}
@@ -215,11 +203,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_SET,
- SOF_CTRL_CMD_SWITCH,
- true);
+ snd_sof_ipc_set_get_comp_data(scontrol, true);
return change;
}
@@ -264,11 +248,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
/* notify DSP of enum updates */
if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_SET,
- SOF_CTRL_CMD_ENUM,
- true);
+ snd_sof_ipc_set_get_comp_data(scontrol, true);
return change;
}
@@ -342,11 +322,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
+ snd_sof_ipc_set_get_comp_data(scontrol, true);
return 0;
}
@@ -391,7 +367,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
}
/* Check that header id matches the command */
- if (header.numid != scontrol->cmd) {
+ if (header.numid != cdata->cmd) {
dev_err_ratelimited(scomp->dev,
"error: incorrect numid %d\n",
header.numid);
@@ -422,11 +398,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
+ snd_sof_ipc_set_get_comp_data(scontrol, true);
return 0;
}
@@ -463,8 +435,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
cdata->data->magic = SOF_ABI_MAGIC;
cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */
- ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET,
- scontrol->cmd, false);
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
if (ret < 0)
goto out;
@@ -485,7 +456,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
goto out;
}
- header.numid = scontrol->cmd;
+ header.numid = cdata->cmd;
header.length = data_size;
if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) {
ret = -EFAULT;
@@ -545,7 +516,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
if (data_size > size)
return -ENOSPC;
- header.numid = scontrol->cmd;
+ header.numid = cdata->cmd;
header.length = data_size;
if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
return -EFAULT;
@@ -600,6 +571,13 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev,
bool found = false;
int i, type;
+ if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET ||
+ cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) {
+ dev_err(sdev->dev,
+ "Component data is not supported in control notification\n");
+ return;
+ }
+
/* Find the swidget first */
list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->comp_id == cdata->comp_id) {
@@ -666,11 +644,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev,
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);
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 2c3de295f11f..8f32b5b12b3e 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -19,7 +19,7 @@
#endif
/* see SOF_DBG_ flags */
-int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
+static 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)");
@@ -27,6 +27,22 @@ MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
#define TIMEOUT_DEFAULT_IPC_MS 500
#define TIMEOUT_DEFAULT_BOOT_MS 2000
+/**
+ * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug
+ * @mask: Flag or combination of flags to check
+ *
+ * Returns true if all bits set in mask is also set in sof_core_debug, otherwise
+ * false
+ */
+bool sof_debug_check_flag(int mask)
+{
+ if ((sof_core_debug & mask) == mask)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(sof_debug_check_flag);
+
/*
* FW Panic/fault handling.
*/
@@ -52,23 +68,33 @@ static const struct sof_panic_msg panic_msg[] = {
{SOF_IPC_PANIC_ASSERT, "assertion failed"},
};
-/*
+/**
+ * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace
+ * @sdev: Pointer to the device's sdev
+ * @level: prink log level to use for the printing
+ * @panic_code: the panic code
+ * @tracep_code: tracepoint code
+ * @oops: Pointer to DSP specific oops data
+ * @panic_info: Pointer to the received panic information message
+ * @stack: Pointer to the call stack data
+ * @stack_words: Number of words in the stack data
+ *
* helper to be called from .dbg_dump callbacks. No error code is
* provided, it's left as an exercise for the caller of .dbg_dump
* (typically IPC or loader)
*/
-void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
- u32 tracep_code, void *oops,
- struct sof_ipc_panic_info *panic_info,
- void *stack, size_t stack_words)
+void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
+ u32 panic_code, u32 tracep_code, void *oops,
+ struct sof_ipc_panic_info *panic_info,
+ void *stack, size_t stack_words)
{
u32 code;
int i;
/* is firmware dead ? */
if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
- dev_err(sdev->dev, "unexpected fault %#010x trace %#010x\n",
- panic_code, tracep_code);
+ dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n",
+ panic_code, tracep_code);
return; /* no fault ? */
}
@@ -76,54 +102,55 @@ 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, "reason: %s (%#x)\n", panic_msg[i].msg,
- code & SOF_IPC_PANIC_CODE_MASK);
- dev_err(sdev->dev, "trace point: %#010x\n", tracep_code);
+ dev_printk(level, sdev->dev, "reason: %s (%#x)\n",
+ panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK);
+ dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
goto out;
}
}
/* unknown error */
- 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);
+ dev_printk(level, sdev->dev, "unknown panic code: %#x\n",
+ code & SOF_IPC_PANIC_CODE_MASK);
+ dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
out:
- 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);
+ dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename,
+ panic_info->linenum);
+ sof_oops(sdev, level, oops);
+ sof_stack(sdev, level, oops, stack, stack_words);
}
-EXPORT_SYMBOL(snd_sof_get_status);
+EXPORT_SYMBOL(sof_print_oops_and_stack);
/*
* FW Boot State Transition Diagram
*
- * +-----------------------------------------------------------------------+
- * | |
- * ------------------ ------------------ |
- * | | | | |
- * | BOOT_FAILED | | READY_FAILED |-------------------------+ |
- * | | | | | |
- * ------------------ ------------------ | |
- * ^ ^ | |
- * | | | |
- * (FW Boot Timeout) (FW_READY FAIL) | |
- * | | | |
- * | | | |
- * ------------------ | ------------------ | |
- * | | | | | | |
- * | IN_PROGRESS |---------------+------------->| COMPLETE | | |
- * | | (FW Boot OK) (FW_READY OK) | | | |
- * ------------------ ------------------ | |
- * ^ | | |
- * | | | |
- * (FW Loading OK) (System Suspend/Runtime Suspend)
- * | | | |
- * | | | |
- * ------------------ ------------------ | | |
- * | | | |<-----+ | |
- * | PREPARE | | NOT_STARTED |<---------------------+ |
- * | | | |<---------------------------+
+ * +----------------------------------------------------------------------+
+ * | |
+ * ------------------ ------------------ |
+ * | | | | |
+ * | BOOT_FAILED |<-------| READY_FAILED | |
+ * | |<--+ | | ------------------ |
+ * ------------------ | ------------------ | | |
+ * ^ | ^ | CRASHED |---+ |
+ * | | | | | | |
+ * (FW Boot Timeout) | (FW_READY FAIL) ------------------ | |
+ * | | | ^ | |
+ * | | | |(DSP Panic) | |
+ * ------------------ | | ------------------ | |
+ * | | | | | | | |
+ * | IN_PROGRESS |---------------+------------->| COMPLETE | | |
+ * | | (FW Boot OK) (FW_READY OK) | | | |
+ * ------------------ | ------------------ | |
+ * ^ | | | |
+ * | | | | |
+ * (FW Loading OK) | (System Suspend/Runtime Suspend)
+ * | | | | |
+ * | (FW Loading Fail) | | |
+ * ------------------ | ------------------ | | |
+ * | | | | |<-----+ | |
+ * | PREPARE |---+ | NOT_STARTED |<---------------------+ |
+ * | | | |<--------------------------+
* ------------------ ------------------
* | ^ | ^
* | | | |
@@ -186,6 +213,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
ret);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
goto fw_load_err;
}
@@ -199,10 +227,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
ret);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
goto fw_run_err;
}
- if (sof_core_debug & SOF_DBG_ENABLE_TRACE) {
+ if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
sdev->dtrace_is_supported = true;
/* init DMA trace */
@@ -362,7 +391,15 @@ int snd_sof_device_remove(struct device *dev)
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
+ /*
+ * Unregister machine driver. This will unbind the snd_card which
+ * will remove the component driver and unload the topology
+ * before freeing the snd_card.
+ */
+ snd_sof_machine_unregister(sdev, pdata);
+
if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
+ snd_sof_free_trace(sdev);
ret = snd_sof_dsp_power_down_notify(sdev);
if (ret < 0)
dev_warn(dev, "error: %d failed to prepare DSP for device removal",
@@ -370,17 +407,9 @@ int snd_sof_device_remove(struct device *dev)
snd_sof_ipc_free(sdev);
snd_sof_free_debug(sdev);
- snd_sof_free_trace(sdev);
}
/*
- * Unregister machine driver. This will unbind the snd_card which
- * will remove the component driver and unload the topology
- * before freeing the snd_card.
- */
- snd_sof_machine_unregister(sdev, pdata);
-
- /*
* Unregistering the machine driver results in unloading the topology.
* Some widgets, ex: scheduler, attempt to power down the core they are
* scheduled on, when they are unloaded. Therefore, the DSP must be
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index dc1df5fb7b4c..6d6757075f7c 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -336,6 +336,104 @@ static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev,
}
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+static ssize_t msg_inject_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct sof_ipc_reply *rhdr = dfse->msg_inject_rx;
+
+ if (!rhdr->hdr.size || !count || *ppos)
+ return 0;
+
+ if (count > rhdr->hdr.size)
+ count = rhdr->hdr.size;
+
+ if (copy_to_user(buffer, dfse->msg_inject_rx, count))
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+}
+
+static ssize_t msg_inject_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx;
+ size_t size;
+ int ret, err;
+
+ if (*ppos)
+ return 0;
+
+ size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE,
+ ppos, buffer, count);
+ if (size != count)
+ return size > 0 ? -EFAULT : size;
+
+ ret = pm_runtime_get_sync(sdev->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(sdev->dev);
+ goto out;
+ }
+
+ /* send the message */
+ memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE);
+ ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count,
+ dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE);
+
+ pm_runtime_mark_last_busy(sdev->dev);
+ err = pm_runtime_put_autosuspend(sdev->dev);
+ if (err < 0)
+ dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n",
+ __func__, err);
+
+ /* return size if test is successful */
+ if (ret >= 0)
+ ret = size;
+
+out:
+ return ret;
+}
+
+static const struct file_operations msg_inject_fops = {
+ .open = simple_open,
+ .read = msg_inject_read,
+ .write = msg_inject_write,
+ .llseek = default_llseek,
+};
+
+static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev,
+ const char *name, mode_t mode,
+ const struct file_operations *fops)
+{
+ struct snd_sof_dfsentry *dfse;
+
+ dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+ if (!dfse)
+ return -ENOMEM;
+
+ /* pre allocate the tx and rx buffers */
+ dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!dfse->msg_inject_tx || !dfse->msg_inject_rx)
+ return -ENOMEM;
+
+ dfse->type = SOF_DFSENTRY_TYPE_BUF;
+ dfse->sdev = sdev;
+
+ debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops);
+ /* add to dfsentry list */
+ list_add(&dfse->list, &sdev->dfsentry_list);
+
+ return 0;
+}
+#endif
+
static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
@@ -812,6 +910,15 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
return err;
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+ err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644,
+ &msg_inject_fops);
+
+ /* errors are only due to memory allocation, not debugfs */
+ if (err < 0)
+ return err;
+#endif
+
return 0;
}
EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
@@ -823,7 +930,7 @@ 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;
+ enum sof_fw_state state;
const char *name;
} fw_state_dbg[] = {
{SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
@@ -831,37 +938,47 @@ static const struct soc_fw_state_info {
{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_READY_OK, "SOF_FW_BOOT_READY_OK"},
{SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"},
+ {SOF_FW_CRASHED, "SOF_FW_CRASHED"},
};
-static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev)
+static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level)
{
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);
+ dev_printk(level, 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);
+ dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state);
}
-void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags)
+void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags)
{
- bool print_all = !!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS);
+ char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR;
+ bool print_all = sof_debug_check_flag(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);
+ dev_printk(level, sdev->dev,
+ "------------[ DSP dump start ]------------\n");
+ if (msg)
+ dev_printk(level, sdev->dev, "%s\n", msg);
+ snd_sof_dbg_print_fw_state(sdev, level);
sof_ops(sdev)->dbg_dump(sdev, flags);
- dev_err(sdev->dev, "------------[ DSP dump end ]------------\n");
+ dev_printk(level, sdev->dev,
+ "------------[ DSP dump end ]------------\n");
if (!print_all)
sdev->dbg_dump_printed = true;
+ } else if (msg) {
+ dev_printk(level, sdev->dev, "%s\n", msg);
}
}
EXPORT_SYMBOL(snd_sof_dsp_dbg_dump);
@@ -872,7 +989,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
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))
+ if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS))
sdev->ipc_dump_printed = true;
}
}
@@ -880,7 +997,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
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)) {
+ sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) {
/* should we prevent DSP entering D3 ? */
if (!sdev->ipc_dump_printed)
dev_info(sdev->dev,
@@ -890,7 +1007,8 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev)
/* dump vital information to the logs */
snd_sof_ipc_dump(sdev);
- snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
+ snd_sof_dsp_dbg_dump(sdev, "Firmware exception",
+ 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 34cf228c188f..9b8d5bb1e449 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -11,53 +11,33 @@ config SND_SOC_SOF_IMX_TOPLEVEL
if SND_SOC_SOF_IMX_TOPLEVEL
-config SND_SOC_SOF_IMX_OF
- def_tristate SND_SOC_SOF_OF
- select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT
- select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT
- help
- This option is not user-selectable but automagically handled by
- 'select' statements at a higher level.
-
config SND_SOC_SOF_IMX_COMMON
tristate
+ select SND_SOC_SOF_OF_DEV
+ select SND_SOC_SOF
+ 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.
-config SND_SOC_SOF_IMX8_SUPPORT
- bool "SOF support for i.MX8"
- depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF
- depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_IMX_OF
+config SND_SOC_SOF_IMX8
+ tristate "SOF support for i.MX8"
+ depends on IMX_SCU
+ depends on IMX_DSP
+ select SND_SOC_SOF_IMX_COMMON
help
This adds support for Sound Open Firmware for NXP i.MX8 platforms.
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_IMX8
- tristate
+config SND_SOC_SOF_IMX8M
+ tristate "SOF support for i.MX8M"
+ depends on IMX_DSP
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.
-
-config SND_SOC_SOF_IMX8M_SUPPORT
- bool "SOF support for i.MX8M"
- depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_OF
help
This adds support for Sound Open Firmware for NXP i.MX8M platforms.
Say Y if you have such a device.
If unsure select "N".
-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.
-
-endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL
+endif ## SND_SOC_SOF_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index 8826ef94f04a..36e3d414a18f 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -69,9 +69,33 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags)
IMX8_STACK_DUMP_SIZE);
/* Print the information to the console */
- snd_sof_get_status(sdev, status, status, &xoops, &panic_info, stack,
- IMX8_STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, KERN_ERR, status, status, &xoops,
+ &panic_info, stack, IMX8_STACK_DUMP_SIZE);
}
EXPORT_SYMBOL(imx8_dump);
+int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks)
+{
+ int ret;
+
+ ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks);
+ if (ret)
+ dev_err(sdev->dev, "Failed to request DSP clocks\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(imx8_parse_clocks);
+
+int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks)
+{
+ return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks);
+}
+EXPORT_SYMBOL(imx8_enable_clocks);
+
+void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks)
+{
+ clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks);
+}
+EXPORT_SYMBOL(imx8_disable_clocks);
+
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h
index 1cc7d6704182..ec4b3a5c7496 100644
--- a/sound/soc/sof/imx/imx-common.h
+++ b/sound/soc/sof/imx/imx-common.h
@@ -3,6 +3,8 @@
#ifndef __IMX_COMMON_H__
#define __IMX_COMMON_H__
+#include <linux/clk.h>
+
#define EXCEPT_MAX_HDR_SIZE 0x400
#define IMX8_STACK_DUMP_SIZE 32
@@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev,
void imx8_dump(struct snd_sof_dev *sdev, u32 flags);
+struct imx_clocks {
+ struct clk_bulk_data *dsp_clks;
+ int num_dsp_clks;
+};
+
+int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks);
+int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks);
+void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks);
+
#endif
diff --git a/sound/soc/sof/imx/imx-ops.h b/sound/soc/sof/imx/imx-ops.h
deleted file mode 100644
index 24235ef8c8fa..000000000000
--- a/sound/soc/sof/imx/imx-ops.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* 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 dd59a74480d6..f6baecbb57fb 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -21,8 +21,8 @@
#include <linux/firmware/imx/svc/misc.h>
#include <dt-bindings/firmware/imx/rsrc.h>
#include "../ops.h"
+#include "../sof-of-dev.h"
#include "imx-common.h"
-#include "imx-ops.h"
/* DSP memories */
#define IRAM_OFFSET 0x10000
@@ -41,6 +41,13 @@
#define MBOX_OFFSET 0x800000
#define MBOX_SIZE 0x1000
+/* DSP clocks */
+static struct clk_bulk_data imx8_dsp_clks[] = {
+ { .id = "ipg" },
+ { .id = "ocram" },
+ { .id = "core" },
+};
+
struct imx8_priv {
struct device *dev;
struct snd_sof_dev *sdev;
@@ -57,42 +64,9 @@ struct imx8_priv {
struct device **pd_dev;
struct device_link **link;
+ struct imx_clocks *clks;
};
-static void imx8_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply has correct size? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
@@ -109,8 +83,7 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- imx8_get_reply(priv->sdev);
- snd_sof_ipc_reply(priv->sdev, 0);
+ snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
@@ -124,7 +97,7 @@ static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
/* Check to see if the message is a panic code (0x0dead***) */
if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
- snd_sof_dsp_panic(priv->sdev, p);
+ snd_sof_dsp_panic(priv->sdev, p, true);
else
snd_sof_ipc_msgs_rx(priv->sdev);
}
@@ -223,6 +196,11 @@ static int imx8_probe(struct snd_sof_dev *sdev)
if (!priv)
return -ENOMEM;
+ priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL);
+ if (!priv->clks)
+ return -ENOMEM;
+
+ sdev->num_cores = 1;
sdev->pdata->hw_pdata = priv;
priv->dev = sdev->dev;
priv->sdev = sdev;
@@ -335,6 +313,18 @@ static int imx8_probe(struct snd_sof_dev *sdev)
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = MBOX_OFFSET;
+ /* init clocks info */
+ priv->clks->dsp_clks = imx8_dsp_clks;
+ priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks);
+
+ ret = imx8_parse_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
+ ret = imx8_enable_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
return 0;
exit_pdev_unregister:
@@ -353,6 +343,7 @@ static int imx8_remove(struct snd_sof_dev *sdev)
struct imx8_priv *priv = sdev->pdata->hw_pdata;
int i;
+ imx8_disable_clocks(sdev, priv->clks);
platform_device_unregister(priv->ipc_dev);
for (i = 0; i < priv->num_domains; i++) {
@@ -376,6 +367,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
}
}
+static void imx8_suspend(struct snd_sof_dev *sdev)
+{
+ int i;
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(priv->dsp_ipc, i);
+
+ imx8_disable_clocks(sdev, priv->clks);
+}
+
+static int imx8_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
+ int ret;
+ int i;
+
+ ret = imx8_enable_clocks(sdev, priv->clks);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(priv->dsp_ipc, i);
+
+ return 0;
+}
+
+static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ ret = imx8_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D3,
+ };
+
+ imx8_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = target_state,
+ };
+
+ if (!pm_runtime_suspended(sdev->dev))
+ imx8_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8_dsp_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ ret = imx8_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ if (pm_runtime_suspended(sdev->dev)) {
+ pm_runtime_disable(sdev->dev);
+ pm_runtime_set_active(sdev->dev);
+ pm_runtime_mark_last_busy(sdev->dev);
+ pm_runtime_enable(sdev->dev);
+ pm_runtime_idle(sdev->dev);
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
static struct snd_soc_dai_driver imx8_dai[] = {
{
.name = "esai0",
@@ -401,8 +478,16 @@ static struct snd_soc_dai_driver imx8_dai[] = {
},
};
+static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ sdev->dsp_power_state = *target_state;
+
+ return 0;
+}
+
/* i.MX8 ops */
-struct snd_sof_dsp_ops sof_imx8_ops = {
+static const struct snd_sof_dsp_ops sof_imx8_ops = {
/* probe and remove */
.probe = imx8_probe,
.remove = imx8_remove,
@@ -453,11 +538,19 @@ struct snd_sof_dsp_ops sof_imx8_ops = {
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+
+ /* PM */
+ .runtime_suspend = imx8_dsp_runtime_suspend,
+ .runtime_resume = imx8_dsp_runtime_resume,
+
+ .suspend = imx8_dsp_suspend,
+ .resume = imx8_dsp_resume,
+
+ .set_power_state = imx8_dsp_set_power_state,
};
-EXPORT_SYMBOL(sof_imx8_ops);
/* i.MX8X ops */
-struct snd_sof_dsp_ops sof_imx8x_ops = {
+static const struct snd_sof_dsp_ops sof_imx8x_ops = {
/* probe and remove */
.probe = imx8_probe,
.remove = imx8_remove,
@@ -502,6 +595,15 @@ struct snd_sof_dsp_ops sof_imx8x_ops = {
.drv = imx8_dai,
.num_drv = ARRAY_SIZE(imx8_dai),
+ /* PM */
+ .runtime_suspend = imx8_dsp_runtime_suspend,
+ .runtime_resume = imx8_dsp_runtime_resume,
+
+ .suspend = imx8_dsp_suspend,
+ .resume = imx8_dsp_resume,
+
+ .set_power_state = imx8_dsp_set_power_state,
+
/* ALSA HW info flags */
.hw_info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -509,7 +611,41 @@ struct snd_sof_dsp_ops sof_imx8x_ops = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
};
-EXPORT_SYMBOL(sof_imx8x_ops);
+
+static struct sof_dev_desc sof_of_imx8qxp_desc = {
+ .default_fw_path = "imx/sof",
+ .default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8x.ri",
+ .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
+ .ops = &sof_imx8x_ops,
+};
+
+static struct sof_dev_desc sof_of_imx8qm_desc = {
+ .default_fw_path = "imx/sof",
+ .default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8.ri",
+ .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
+ .ops = &sof_imx8_ops,
+};
+
+static const struct of_device_id sof_of_imx8_ids[] = {
+ { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
+ { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_imx8_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-imx8",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_imx8_ids,
+ },
+};
+module_platform_driver(snd_sof_of_imx8_driver);
MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index e4618980cf8b..788e77bcb603 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -6,10 +6,13 @@
//
// Hardware interface for audio DSP on i.MX8M
+#include <linux/bits.h>
#include <linux/firmware.h>
+#include <linux/mfd/syscon.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/regmap.h>
#include <linux/module.h>
#include <sound/sof.h>
@@ -17,12 +20,32 @@
#include <linux/firmware/imx/dsp.h>
#include "../ops.h"
+#include "../sof-of-dev.h"
#include "imx-common.h"
-#include "imx-ops.h"
#define MBOX_OFFSET 0x800000
#define MBOX_SIZE 0x1000
+static struct clk_bulk_data imx8m_dsp_clks[] = {
+ { .id = "ipg" },
+ { .id = "ocram" },
+ { .id = "core" },
+};
+
+/* DAP registers */
+#define IMX8M_DAP_DEBUG 0x28800000
+#define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
+#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
+#define IMX8M_PWRCTL_CORERESET BIT(16)
+
+/* DSP audio mix registers */
+#define AudioDSP_REG0 0x100
+#define AudioDSP_REG1 0x104
+#define AudioDSP_REG2 0x108
+#define AudioDSP_REG3 0x10c
+
+#define AudioDSP_REG2_RUNSTALL BIT(5)
+
struct imx8m_priv {
struct device *dev;
struct snd_sof_dev *sdev;
@@ -30,41 +53,12 @@ struct imx8m_priv {
/* DSP IPC handler */
struct imx_dsp_ipc *dsp_ipc;
struct platform_device *ipc_dev;
-};
-
-static void imx8m_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt\n");
- return;
- }
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
+ struct imx_clocks *clks;
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply has correct size? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
+ void __iomem *dap;
+ struct regmap *regmap;
+};
static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev)
{
@@ -82,8 +76,7 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc)
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- imx8m_get_reply(priv->sdev);
- snd_sof_ipc_reply(priv->sdev, 0);
+ snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
@@ -97,7 +90,7 @@ static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc)
/* Check to see if the message is a panic code (0x0dead***) */
if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
- snd_sof_dsp_panic(priv->sdev, p);
+ snd_sof_dsp_panic(priv->sdev, p, true);
else
snd_sof_ipc_msgs_rx(priv->sdev);
}
@@ -123,7 +116,34 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
*/
static int imx8m_run(struct snd_sof_dev *sdev)
{
- /* TODO: start DSP using Audio MIX bits */
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
+
+ regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0);
+
+ return 0;
+}
+
+static int imx8m_reset(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
+ u32 pwrctl;
+
+ /* put DSP into reset and stall */
+ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL);
+ pwrctl |= IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL);
+
+ /* keep reset asserted for 10 cycles */
+ usleep_range(1, 2);
+
+ regmap_update_bits(priv->regmap, AudioDSP_REG2,
+ AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL);
+
+ /* take the DSP out of reset and keep stalled for FW loading */
+ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL);
+ pwrctl &= ~IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL);
+
return 0;
}
@@ -143,6 +163,11 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
if (!priv)
return -ENOMEM;
+ priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL);
+ if (!priv->clks)
+ return -ENOMEM;
+
+ sdev->num_cores = 1;
sdev->pdata->hw_pdata = priv;
priv->dev = sdev->dev;
priv->sdev = sdev;
@@ -175,6 +200,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
goto exit_pdev_unregister;
}
+ priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
+ if (!priv->dap) {
+ dev_err(sdev->dev, "error: failed to map DAP debug memory area");
+ ret = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+
sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
@@ -210,6 +242,25 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = MBOX_OFFSET;
+ priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl");
+ if (IS_ERR(priv->regmap)) {
+ dev_err(sdev->dev, "cannot find dsp-ctrl registers");
+ ret = PTR_ERR(priv->regmap);
+ goto exit_pdev_unregister;
+ }
+
+ /* init clocks info */
+ priv->clks->dsp_clks = imx8m_dsp_clks;
+ priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks);
+
+ ret = imx8_parse_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
+ ret = imx8_enable_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
return 0;
exit_pdev_unregister:
@@ -221,6 +272,7 @@ static int imx8m_remove(struct snd_sof_dev *sdev)
{
struct imx8m_priv *priv = sdev->pdata->hw_pdata;
+ imx8_disable_clocks(sdev, priv->clks);
platform_device_unregister(priv->ipc_dev);
return 0;
@@ -264,13 +316,108 @@ static struct snd_soc_dai_driver imx8m_dai[] = {
},
};
+static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ sdev->dsp_power_state = *target_state;
+
+ return 0;
+}
+
+static int imx8m_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
+ int ret;
+ int i;
+
+ ret = imx8_enable_clocks(sdev, priv->clks);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(priv->dsp_ipc, i);
+
+ return 0;
+}
+
+static void imx8m_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
+ int i;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(priv->dsp_ipc, i);
+
+ imx8_disable_clocks(sdev, priv->clks);
+}
+
+static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ ret = imx8m_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D3,
+ };
+
+ imx8m_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8m_dsp_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ ret = imx8m_resume(sdev);
+ if (ret < 0)
+ return ret;
+
+ if (pm_runtime_suspended(sdev->dev)) {
+ pm_runtime_disable(sdev->dev);
+ pm_runtime_set_active(sdev->dev);
+ pm_runtime_mark_last_busy(sdev->dev);
+ pm_runtime_enable(sdev->dev);
+ pm_runtime_idle(sdev->dev);
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = target_state,
+ };
+
+ if (!pm_runtime_suspended(sdev->dev))
+ imx8m_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
/* i.MX8 ops */
-struct snd_sof_dsp_ops sof_imx8m_ops = {
+static const struct snd_sof_dsp_ops sof_imx8m_ops = {
/* probe and remove */
.probe = imx8m_probe,
.remove = imx8m_remove,
/* DSP core boot */
.run = imx8m_run,
+ .reset = imx8m_reset,
/* Block IO */
.block_read = sof_block_read,
@@ -309,13 +456,46 @@ struct snd_sof_dsp_ops sof_imx8m_ops = {
.drv = imx8m_dai,
.num_drv = ARRAY_SIZE(imx8m_dai),
+ .suspend = imx8m_dsp_suspend,
+ .resume = imx8m_dsp_resume,
+
+ .runtime_suspend = imx8m_dsp_runtime_suspend,
+ .runtime_resume = imx8m_dsp_runtime_resume,
+
+ .set_power_state = imx8m_dsp_set_power_state,
+
.hw_info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
-EXPORT_SYMBOL(sof_imx8m_ops);
+
+static struct sof_dev_desc sof_of_imx8mp_desc = {
+ .default_fw_path = "imx/sof",
+ .default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8m.ri",
+ .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
+ .ops = &sof_imx8m_ops,
+};
+
+static const struct of_device_id sof_of_imx8m_ids[] = {
+ { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_imx8m_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-imx8m",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_imx8m_ids,
+ },
+};
+module_platform_driver(snd_sof_of_imx8m_driver);
MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 917f78cf6daf..810b8b6748a0 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -78,6 +78,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
@@ -101,9 +102,8 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
+ /* dsp core get/put */
+ .core_get = hda_dsp_core_get,
/* trace callback */
.trace_init = hda_dsp_trace_init,
@@ -147,5 +147,6 @@ const struct sof_intel_dsp_desc apl_chip_info = {
.rom_init_timeout = 150,
.ssp_count = APL_SSP_COUNT,
.ssp_base_offset = APL_SSP_BASE_OFFSET,
+ .quirks = SOF_INTEL_PROCEN_FMT_QUIRK,
};
EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
index 74c630bb9847..ff5900b155dc 100644
--- a/sound/soc/sof/intel/atom.c
+++ b/sound/soc/sof/intel/atom.c
@@ -27,7 +27,6 @@
static void atom_host_done(struct snd_sof_dev *sdev);
static void atom_dsp_done(struct snd_sof_dev *sdev);
-static void atom_get_reply(struct snd_sof_dev *sdev);
/*
* Debug
@@ -71,8 +70,8 @@ void atom_dump(struct snd_sof_dev *sdev, u32 flags)
panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
atom_get_registers(sdev, &xoops, &panic_info, stack,
STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
- STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
+ &panic_info, stack, STACK_DUMP_SIZE);
/* provide some context for firmware debug */
imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
@@ -154,8 +153,7 @@ irqreturn_t atom_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
- atom_get_reply(sdev);
- snd_sof_ipc_reply(sdev, ipcx);
+ snd_sof_ipc_process_reply(sdev, ipcx);
atom_dsp_done(sdev);
@@ -167,8 +165,8 @@ irqreturn_t atom_irq_thread(int irq, void *context)
/* Handle messages from DSP Core */
if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) +
- MBOX_OFFSET);
+ snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,
+ true);
} else {
snd_sof_ipc_msgs_rx(sdev);
}
@@ -195,45 +193,6 @@ int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
}
EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
-static void atom_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- /*
- * Sometimes, there is unexpected reply ipc arriving. The reply
- * ipc belongs to none of the ipcs sent from driver.
- * In this case, the driver must ignore the ipc.
- */
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
@@ -334,7 +293,7 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
return tplg_filename;
}
-void atom_machine_select(struct snd_sof_dev *sdev)
+struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
@@ -345,7 +304,7 @@ void atom_machine_select(struct snd_sof_dev *sdev)
mach = snd_soc_acpi_find_machine(desc->machines);
if (!mach) {
dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
- return;
+ return NULL;
}
pdev = to_platform_device(sdev->dev);
@@ -363,12 +322,13 @@ void atom_machine_select(struct snd_sof_dev *sdev)
if (!tplg_filename) {
dev_dbg(sdev->dev,
"error: no topology filename\n");
- return;
+ return NULL;
}
sof_pdata->tplg_filename = tplg_filename;
mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
- sof_pdata->machine = mach;
+
+ return mach;
}
EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
@@ -443,14 +403,14 @@ struct snd_soc_dai_driver atom_dai[] = {
};
EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
-void atom_set_mach_params(const struct snd_soc_acpi_mach *mach,
+void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
struct snd_soc_acpi_mach_params *mach_params;
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
+ mach_params = &mach->mach_params;
mach_params->platform = dev_name(sdev->dev);
mach_params->num_dai_drivers = desc->ops->num_drv;
mach_params->dai_drivers = desc->ops->drv;
diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h
index 96a462c7a2e5..b965e5e080a6 100644
--- a/sound/soc/sof/intel/atom.h
+++ b/sound/soc/sof/intel/atom.h
@@ -65,8 +65,8 @@ int atom_run(struct snd_sof_dev *sdev);
int atom_reset(struct snd_sof_dev *sdev);
void atom_dump(struct snd_sof_dev *sdev, u32 flags);
-void atom_machine_select(struct snd_sof_dev *sdev);
-void atom_set_mach_params(const struct snd_soc_acpi_mach *mach,
+struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev);
+void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev);
extern struct snd_soc_dai_driver atom_dai[];
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 2c09a523288e..d627b7498d5e 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -75,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = {
static void bdw_host_done(struct snd_sof_dev *sdev);
static void bdw_dsp_done(struct snd_sof_dev *sdev);
-static void bdw_get_reply(struct snd_sof_dev *sdev);
/*
* DSP Control.
@@ -259,8 +258,8 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags)
panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX);
bdw_get_registers(sdev, &xoops, &panic_info, stack,
BDW_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
- BDW_STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
+ &panic_info, stack, BDW_STACK_DUMP_SIZE);
/* provide some context for firmware debug */
imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX);
@@ -326,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
- bdw_get_reply(sdev);
- snd_sof_ipc_reply(sdev, ipcx);
+ snd_sof_ipc_process_reply(sdev, ipcx);
bdw_dsp_done(sdev);
@@ -346,8 +344,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
/* Handle messages from DSP Core */
if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) +
- MBOX_OFFSET);
+ snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + MBOX_OFFSET,
+ true);
} else {
snd_sof_ipc_msgs_rx(sdev);
}
@@ -372,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
-static void bdw_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- /*
- * Sometimes, there is unexpected reply ipc arriving. The reply
- * ipc belongs to none of the ipcs sent from driver.
- * In this case, the driver must ignore the ipc.
- */
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
@@ -453,10 +412,19 @@ static int bdw_probe(struct snd_sof_dev *sdev)
const struct sof_dev_desc *desc = pdata->desc;
struct platform_device *pdev =
container_of(sdev->dev, struct platform_device, dev);
+ const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
/* LPE base */
mmio = platform_get_resource(pdev, IORESOURCE_MEM,
desc->resindex_lpe_base);
@@ -541,7 +509,7 @@ static int bdw_probe(struct snd_sof_dev *sdev)
return ret;
}
-static void bdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
@@ -550,22 +518,23 @@ static void bdw_machine_select(struct snd_sof_dev *sdev)
mach = snd_soc_acpi_find_machine(desc->machines);
if (!mach) {
dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
- return;
+ return NULL;
}
sof_pdata->tplg_filename = mach->sof_tplg_filename;
mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
- sof_pdata->machine = mach;
+
+ return mach;
}
-static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach,
+static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
struct snd_soc_acpi_mach_params *mach_params;
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
+ mach_params = &mach->mach_params;
mach_params->platform = dev_name(sdev->dev);
mach_params->num_dai_drivers = desc->ops->num_drv;
mach_params->dai_drivers = desc->ops->drv;
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index e2fa08f1ae74..dcfeaedb8fd5 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -113,10 +113,19 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
const struct sof_dev_desc *desc = pdata->desc;
struct platform_device *pdev =
container_of(sdev->dev, struct platform_device, dev);
+ const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
/* DSP DMA can only access low 31 bits of host memory */
ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31));
if (ret < 0) {
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 3957e2b3db32..e615125d575e 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -82,9 +82,24 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
msg, msg_ext);
/* handle messages from DSP */
- if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) ==
- SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext));
+ if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ bool non_recoverable = true;
+
+ /*
+ * This is a PANIC message!
+ *
+ * If it is arriving during firmware boot and it is not
+ * the last boot attempt then change the non_recoverable
+ * to false as the DSP might be able to boot in the next
+ * iteration(s)
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&
+ hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)
+ non_recoverable = false;
+
+ snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),
+ non_recoverable);
} else {
snd_sof_ipc_msgs_rx(sdev);
}
@@ -283,6 +298,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
@@ -303,9 +319,8 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
+ /* dsp core get/put */
+ .core_get = hda_dsp_core_get,
/* firmware run */
.run = hda_dsp_cl_boot_firmware,
@@ -358,6 +373,13 @@ const struct sof_intel_dsp_desc cnl_chip_info = {
};
EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
+/*
+ * JasperLake is technically derived from IceLake, and should be in
+ * described in icl.c. However since JasperLake was designed with
+ * two cores, it cannot support the IceLake-specific power-up sequences
+ * which rely on core3. To simplify, JasperLake uses the CannonLake ops and
+ * is described in cnl.c
+ */
const struct sof_intel_dsp_desc jsl_chip_info = {
/* Jasperlake */
.cores_num = 2,
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 13cd96e6724a..2f3f4a733d9e 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -20,9 +20,10 @@
#include "../../codecs/hdac_hda.h"
#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
+#define CODEC_PROBE_RETRIES 3
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#define IDISP_VID_INTEL 0x80860000
-#define CODEC_PROBE_RETRIES 3
/* load the legacy HDA codec driver */
static int request_codec_module(struct hda_codec *codec)
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index fa5f0a718901..0c29bb196e59 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -353,7 +353,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
snd_hdac_bus_stop_cmd_io(bus);
#endif
/* disable position buffer */
- if (bus->posbuf.addr) {
+ if (bus->use_posbuf && bus->posbuf.addr) {
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
SOF_HDA_ADSP_DPLBASE, 0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 76579383d290..cd12589355ef 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -21,8 +21,6 @@
#endif
struct hda_pipe_params {
- u8 host_dma_id;
- u8 link_dma_id;
u32 ch;
u32 s_freq;
u32 s_fmt;
@@ -30,7 +28,6 @@ struct hda_pipe_params {
snd_pcm_format_t format;
int link_index;
int stream;
- unsigned int host_bps;
unsigned int link_bps;
};
@@ -182,24 +179,6 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg
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;
- }
-
- /* 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)
@@ -215,9 +194,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
/* 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_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP);
- return hda_ctrl_dai_widget_free(w);
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
}
static int hda_link_hw_params(struct snd_pcm_substream *substream,
@@ -264,17 +243,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
if (!link)
return -EINVAL;
- /* set the stream tag in the codec dai dma params */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
- else
- snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0);
+ /* set the hdac_stream in the codec dai */
+ snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
p_params.ch = params_channels(params);
p_params.s_freq = params_rate(params);
p_params.stream = substream->stream;
- p_params.link_dma_id = stream_tag - 1;
p_params.link_index = link->index;
p_params.format = params_format(params);
@@ -305,6 +280,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
dai);
}
+static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ 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];
+
+ /* set PAUSE command flag */
+ config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);
+
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);
+
+ return ret;
+}
+
static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
@@ -330,33 +335,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- /* set up hw_params */
- ret = hda_link_pcm_prepare(substream, dai);
- if (ret < 0) {
- dev_err(dai->dev,
- "error: setting up hw_params during resume\n");
- return ret;
- }
- fallthrough;
+ w = snd_soc_dai_get_widget(dai, substream->stream);
+
+ switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_link_stream_start(link_dev);
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;
+ snd_hdac_ext_link_stream_clear(link_dev);
/*
- * clear link DMA channel. It will be assigned when
- * hw_params is set up again after resume.
+ * free DAI widget during stop/suspend to keep widget use_count's balanced.
*/
- ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID);
+ ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
if (ret < 0)
return ret;
@@ -366,10 +360,13 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
}
link_dev->link_prepared = 0;
-
- fallthrough;
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_hdac_ext_link_stream_clear(link_dev);
+
+ ret = hda_link_dai_config_pause_push_ipc(w);
+ if (ret < 0)
+ return ret;
break;
default:
return -EINVAL;
@@ -470,9 +467,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
return 0;
if (setup)
- return hda_ctrl_dai_widget_setup(w);
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
- return hda_ctrl_dai_widget_free(w);
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
}
static int ssp_dai_startup(struct snd_pcm_substream *substream,
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 287dc0eb6686..916a257ea96b 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -614,7 +614,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
#endif
- int ret;
+ int ret, j;
hda_sdw_int_enable(sdev, false);
@@ -629,13 +629,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
#endif
/* power down DSP */
- ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
+ ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to power down core during suspend\n");
return ret;
}
+ /* reset ref counts for all cores */
+ for (j = 0; j < chip->cores_num; j++)
+ sdev->dsp_core_ref_count[j] = 0;
+
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
@@ -962,3 +966,47 @@ void hda_dsp_d0i3_work(struct work_struct *work)
"error: failed to set DSP state %d substate %d\n",
target_state.state, target_state.substate);
}
+
+int hda_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ struct sof_ipc_pm_core_config pm_core_config = {
+ .hdr = {
+ .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
+ .size = sizeof(pm_core_config),
+ },
+ .enable_mask = sdev->enabled_cores_mask | BIT(core),
+ };
+ int ret, ret1;
+
+ /* power up core */
+ ret = hda_dsp_enable_core(sdev, BIT(core));
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to power up core %d with err: %d\n",
+ core, ret);
+ return ret;
+ }
+
+ /* No need to send IPC for primary core or if FW boot is not complete */
+ if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE)
+ return 0;
+
+ /* Now notify DSP for secondary cores */
+ ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
+ &pm_core_config, sizeof(pm_core_config),
+ &pm_core_config, sizeof(pm_core_config));
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n",
+ core, ret);
+ goto power_down;
+ }
+
+ return ret;
+
+power_down:
+ /* power down core if it is host managed and return the original error if this fails too */
+ ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core));
+ if (ret1 < 0)
+ dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1);
+
+ return ret;
+}
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 11f20a5a62df..f0cf8019d72d 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -70,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
- int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
@@ -94,35 +93,11 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
reply.hdr.cmd = SOF_IPC_GLB_REPLY;
reply.hdr.size = sizeof(reply);
memcpy(msg->reply_data, &reply, sizeof(reply));
- goto out;
- }
-
- /* get IPC reply from DSP in the mailbox */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply,
- sizeof(reply));
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
+ msg->reply_error = 0;
} else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size &&
- /* getter payload is never known upfront */
- ((reply.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_PROBE)) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
+ snd_sof_ipc_get_reply(sdev);
}
-
-out:
- msg->reply_error = ret;
-
}
/* IPC handler thread */
@@ -198,8 +173,23 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
/* handle messages from DSP */
if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- /* this is a PANIC message !! */
- snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext));
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ bool non_recoverable = true;
+
+ /*
+ * This is a PANIC message!
+ *
+ * If it is arriving during firmware boot and it is not
+ * the last boot attempt then change the non_recoverable
+ * to false as the DSP might be able to boot in the next
+ * iteration(s)
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&
+ hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)
+ non_recoverable = false;
+
+ snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),
+ non_recoverable);
} else {
/* normal message - process normally */
snd_sof_ipc_msgs_rx(sdev);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index abad6d0ceb83..33306d2023a7 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -23,7 +23,6 @@
#include "../ops.h"
#include "hda.h"
-#define HDA_FW_BOOT_ATTEMPTS 3
#define HDA_CL_STREAM_FORMAT 0x40
static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
@@ -88,12 +87,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int status;
- u32 flags;
+ unsigned long mask;
+ char *dump_msg;
+ u32 flags, j;
int ret;
int i;
/* step 1: power up corex */
- ret = snd_sof_dsp_core_power_up(sdev, chip->host_managed_cores_mask);
+ ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask);
if (ret < 0) {
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n");
@@ -148,8 +149,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
chip->ipc_ack_mask);
/* step 5: power down cores that are no longer needed */
- ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask &
- ~(chip->init_core_mask));
+ ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask &
+ ~(chip->init_core_mask));
if (ret < 0) {
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev,
@@ -168,8 +169,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
HDA_DSP_REG_POLL_INTERVAL_US,
chip->rom_init_timeout *
USEC_PER_MSEC);
- if (!ret)
+ if (!ret) {
+ /* set enabled cores mask and increment ref count for cores in init_core_mask */
+ sdev->enabled_cores_mask |= chip->init_core_mask;
+ mask = sdev->enabled_cores_mask;
+ for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
+ sdev->dsp_core_ref_count[j]++;
return 0;
+ }
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev,
@@ -183,9 +190,12 @@ err:
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
flags &= ~SOF_DBG_DUMP_OPTIONAL;
- snd_sof_dsp_dbg_dump(sdev, flags);
- snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
+ dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
+ hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
+ snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
+ hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
+ kfree(dump_msg);
return ret;
}
@@ -407,16 +417,19 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
hda_sdw_process_wakeen(sdev);
/*
- * at this point DSP ROM has been initialized and
- * should be ready for code loading and firmware boot
+ * Set the boot_iteration to the last attempt, indicating that the
+ * DSP ROM has been initialized and from this point there will be no
+ * retry done to boot.
+ *
+ * Continue with code loading and firmware boot
*/
+ hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
ret = cl_copy_fw(sdev, stream);
- if (!ret) {
+ if (!ret)
dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
- } else {
- 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);
- }
+ else
+ snd_sof_dsp_dbg_dump(sdev, "Firmware download failed",
+ SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX);
cleanup:
/*
@@ -474,46 +487,6 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
return hda_dsp_ctrl_clock_power_gating(sdev, true);
}
-/*
- * post fw run operations for ICL,
- * Core 3 will be powered up and in stall when HPRO is enabled
- */
-int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev)
-{
- struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- int ret;
-
- if (sdev->first_boot) {
- ret = hda_sdw_startup(sdev);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: could not startup SoundWire links\n");
- return ret;
- }
- }
-
- hda_sdw_int_enable(sdev, true);
-
- /*
- * The recommended HW programming sequence for ICL is to
- * power up core 3 and keep it in stall if HPRO is enabled.
- * Major difference between ICL and TGL, on ICL core 3 is managed by
- * the host whereas on TGL it is handled by the firmware.
- */
- if (!hda->clk_config_lpro) {
- ret = snd_sof_dsp_core_power_up(sdev, BIT(3));
- if (ret < 0) {
- dev_err(sdev->dev, "error: dsp core power up failed on core 3\n");
- return ret;
- }
-
- snd_sof_dsp_stall(sdev, BIT(3));
- }
-
- /* re-enable clock gating and power gating */
- return hda_dsp_ctrl_clock_power_gating(sdev, true);
-}
-
int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
const struct sof_ext_man_elem_header *hdr)
{
@@ -551,24 +524,3 @@ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
return 0;
}
-
-int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask)
-{
- struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- const struct sof_intel_dsp_desc *chip = hda->desc;
-
- /* make sure core_mask in host managed cores */
- core_mask &= chip->host_managed_cores_mask;
- if (!core_mask) {
- dev_err(sdev->dev, "error: core_mask is not in host managed cores\n");
- return -EINVAL;
- }
-
- /* stall core */
- snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
- HDA_DSP_REG_ADSPCS,
- HDA_DSP_ADSPCS_CSTALL_MASK(core_mask),
- HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
-
- return 0;
-}
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index cc8ddef37f37..d78aa5d8552d 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -32,6 +32,10 @@ static bool hda_always_enable_dmi_l1;
module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
+static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS);
+module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
+MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
+
u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate)
{
switch (rate) {
@@ -120,8 +124,11 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
return ret;
}
- /* disable SPIB, to enable buffer wrap for stream */
- hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
+ /* enable SPIB when rewinds are disabled */
+ if (hda_disable_rewinds)
+ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0);
+ else
+ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
/* update no_stream_position flag for ipc params */
if (hda && hda->no_ipc_position) {
@@ -140,6 +147,29 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
return 0;
}
+/* update SPIB register with appl position */
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t appl_pos, buf_size;
+ u32 spib;
+
+ appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+
+ spib = appl_pos % buf_size;
+
+ /* Allowable value for SPIB is 1 byte to max buffer size */
+ if (!spib)
+ spib = buf_size;
+
+ sof_io_write(sdev, hext_stream->spib_addr, spib);
+
+ return 0;
+}
+
int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd)
{
@@ -172,38 +202,74 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
goto found;
}
- /*
- * DPIB/posbuf position mode:
- * For Playback, Use DPIB register from HDA space which
- * reflects the actual data transferred.
- * For Capture, Use the position buffer for pointer, as DPIB
- * is not accurate enough, its update may be completed
- * earlier than the data written to DDR.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (sof_hda_position_quirk) {
+ case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY:
+ /*
+ * This legacy code, inherited from the Skylake driver,
+ * mixes DPIB registers and DPIB DDR updates and
+ * does not seem to follow any known hardware recommendations.
+ * It's not clear e.g. why there is a different flow
+ * for capture and playback, the only information that matters is
+ * what traffic class is used, and on all SOF-enabled platforms
+ * only VC0 is supported so the work-around was likely not necessary
+ * and quite possibly wrong.
+ */
+
+ /* DPIB/posbuf position mode:
+ * For Playback, Use DPIB register from HDA space which
+ * reflects the actual data transferred.
+ * For Capture, Use the position buffer for pointer, as DPIB
+ * is not accurate enough, its update may be completed
+ * earlier than the data written to DDR.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hstream->index));
+ } else {
+ /*
+ * For capture stream, we need more workaround to fix the
+ * position incorrect issue:
+ *
+ * 1. Wait at least 20us before reading position buffer after
+ * the interrupt generated(IOC), to make sure position update
+ * happens on frame boundary i.e. 20.833uSec for 48KHz.
+ * 2. Perform a dummy Read to DPIB register to flush DMA
+ * position value.
+ * 3. Read the DMA Position from posbuf. Now the readback
+ * value should be >= period boundary.
+ */
+ usleep_range(20, 21);
+ snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hstream->index));
+ pos = snd_hdac_stream_get_pos_posbuf(hstream);
+ }
+ break;
+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS:
+ /*
+ * In case VC1 traffic is disabled this is the recommended option
+ */
pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
AZX_REG_VS_SDXDPIB_XBASE +
(AZX_REG_VS_SDXDPIB_XINTERVAL *
hstream->index));
- } else {
+ break;
+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE:
/*
- * For capture stream, we need more workaround to fix the
- * position incorrect issue:
- *
- * 1. Wait at least 20us before reading position buffer after
- * the interrupt generated(IOC), to make sure position update
- * happens on frame boundary i.e. 20.833uSec for 48KHz.
- * 2. Perform a dummy Read to DPIB register to flush DMA
- * position value.
- * 3. Read the DMA Position from posbuf. Now the readback
- * value should be >= period boundary.
+ * This is the recommended option when VC1 is enabled.
+ * While this isn't needed for SOF platforms it's added for
+ * consistency and debug.
*/
- usleep_range(20, 21);
- snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- hstream->index));
pos = snd_hdac_stream_get_pos_posbuf(hstream);
+ break;
+ default:
+ dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n",
+ sof_hda_position_quirk);
+ pos = 0;
+ break;
}
if (pos >= hstream->bufsize)
@@ -235,6 +301,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
}
/*
+ * if we want the .ack to work, we need to prevent the control from being mapped.
+ * The status can still be mapped.
+ */
+ if (hda_disable_rewinds)
+ runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR;
+
+ /*
* All playback streams are DMI L1 capable, capture streams need
* pause push/release to be disabled
*/
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 1d845c2cbc33..ba60807fbd8f 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
return 0;
}
+static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
+{
+ int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ int timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+ u32 val;
+
+ /* enter stream reset */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST,
+ SOF_STREAM_SD_OFFSET_CRST);
+ do {
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
+ if (val & SOF_STREAM_SD_OFFSET_CRST)
+ break;
+ } while (--timeout);
+ if (timeout == 0) {
+ dev_err(sdev->dev, "timeout waiting for stream reset\n");
+ return -ETIMEDOUT;
+ }
+
+ timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+
+ /* exit stream reset and wait to read a zero before reading any other register */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0);
+
+ /* wait for hardware to report that stream is out of reset */
+ udelay(3);
+ do {
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
+ if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0)
+ break;
+ } while (--timeout);
+ if (timeout == 0) {
+ dev_err(sdev->dev, "timeout waiting for stream to exit reset\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream, int cmd)
{
@@ -290,7 +329,6 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
/* cmd must be for audio stream */
switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_START:
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
@@ -433,12 +471,13 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params)
{
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *hstream = &stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+ int ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
- u32 val, mask;
+ u32 mask;
u32 run;
if (!stream) {
@@ -483,36 +522,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
SOF_HDA_CL_DMA_SD_INT_MASK);
/* stream reset */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
- 0x1);
- udelay(3);
- do {
- val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- sd_offset);
- if (val & 0x1)
- break;
- } while (--timeout);
- if (timeout == 0) {
- dev_err(sdev->dev, "error: stream reset failed\n");
- return -ETIMEDOUT;
- }
-
- timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
- 0x0);
-
- /* wait for hardware to report that stream is out of reset */
- udelay(3);
- do {
- val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
- sd_offset);
- if ((val & 0x1) == 0)
- break;
- } while (--timeout);
- if (timeout == 0) {
- dev_err(sdev->dev, "error: timeout waiting for stream reset\n");
- return -ETIMEDOUT;
- }
+ ret = hda_dsp_stream_reset(sdev, hstream);
+ if (ret < 0)
+ return ret;
if (hstream->posbuf)
*hstream->posbuf = 0;
@@ -572,6 +584,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
/*
* Recommended hardware programming sequence for HDAudio DMA format
+ * on earlier platforms - this is not needed on newer platforms
*
* 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit
* for corresponding stream index before the time of writing
@@ -581,9 +594,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
* enable decoupled mode
*/
- /* couple host and link DMA, disable DSP features */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- mask, 0);
+ if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ /* couple host and link DMA, disable DSP features */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ mask, 0);
+ }
/* program stream format */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -591,9 +606,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
SOF_HDA_ADSP_REG_CL_SD_FORMAT,
0xffff, hstream->format_val);
- /* decouple host and link DMA, enable DSP features */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- mask, mask);
+ if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ /* decouple host and link DMA, enable DSP features */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ mask, mask);
+ }
/* program last valid index */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -608,9 +625,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU,
upper_32_bits(hstream->bdl.addr));
- /* enable position buffer */
- if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE)
- & SOF_HDA_ADSP_DPLBASE_ENABLE)) {
+ /* enable position buffer, if needed */
+ if (bus->use_posbuf && bus->posbuf.addr &&
+ !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE)
+ & SOF_HDA_ADSP_DPLBASE_ENABLE)) {
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE,
upper_32_bits(bus->posbuf.addr));
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE,
@@ -647,6 +665,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
hstream);
struct hdac_bus *bus = sof_to_bus(sdev);
u32 mask = 0x1 << stream->index;
+ int ret;
+
+ ret = hda_dsp_stream_reset(sdev, stream);
+ if (ret < 0)
+ return ret;
spin_lock_irq(&bus->reg_lock);
/* couple host and link DMA if link DMA channel is idle */
@@ -655,6 +678,8 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
SOF_HDA_REG_PP_PPCTL, mask, 0);
spin_unlock_irq(&bus->reg_lock);
+ hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0);
+
stream->substream = NULL;
return 0;
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 2c0d4d06ab36..c8fb082209ce 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -41,7 +41,7 @@
#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)
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
@@ -60,7 +60,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
/* DAI already configured, reset it before reconfiguring it */
if (sof_dai->configured) {
- ret = hda_ctrl_dai_widget_free(w);
+ ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
if (ret < 0)
return ret;
}
@@ -78,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
return ret;
}
- /* set HW_PARAMS flag */
- config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS);
+ /* set HW_PARAMS flag along with quirks */
+ config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
+ quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+
/* send DAI_CONFIG IPC */
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
@@ -94,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
return 0;
}
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
@@ -117,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
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);
+ /* set HW_FREE flag along with any quirks */
+ config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
+ quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
@@ -134,17 +137,6 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
return sof_widget_free(sdev, swidget);
}
-static const struct sof_intel_dsp_desc
- *get_chip_info(struct snd_sof_pdata *pdata)
-{
- const struct sof_dev_desc *desc = pdata->desc;
- const struct sof_intel_dsp_desc *chip_info;
-
- chip_info = desc->chip_info;
-
- return chip_info;
-}
-
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
/*
@@ -184,23 +176,19 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
config->alh.stream_id = alh_stream_id;
if (setup)
- return hda_ctrl_dai_widget_setup(w);
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
- return hda_ctrl_dai_widget_free(w);
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
}
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 snd_soc_dapm_widget *w;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = d->playback_widget;
- else
- w = d->capture_widget;
+ w = snd_soc_dai_get_widget(d, params_data->stream);
return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
d->id, true);
@@ -209,15 +197,11 @@ static int sdw_params_stream(struct device *dev,
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 snd_soc_dapm_widget *w;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = d->playback_widget;
- else
- w = d->capture_widget;
+ w = snd_soc_dai_get_widget(d, free_data->stream);
/* send invalid stream_id */
return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
@@ -440,6 +424,10 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
#define hda_use_msi (1)
#endif
+int sof_hda_position_quirk = SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS;
+module_param_named(position_quirk, sof_hda_position_quirk, int, 0444);
+MODULE_PARM_DESC(position_quirk, "SOF HDaudio position quirk");
+
static char *hda_model;
module_param(hda_model, charp, 0444);
MODULE_PARM_DESC(hda_model, "Use the given HDA board model.");
@@ -478,7 +466,7 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = {
{HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"},
};
-static void hda_dsp_get_status(struct snd_sof_dev *sdev)
+static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level)
{
u32 status;
int i;
@@ -488,8 +476,8 @@ static void hda_dsp_get_status(struct snd_sof_dev *sdev)
for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) {
if (status == hda_dsp_rom_msg[i].code) {
- dev_err(sdev->dev, "%s - code %8.8x\n",
- hda_dsp_rom_msg[i].msg, status);
+ dev_printk(level, sdev->dev, "%s - code %8.8x\n",
+ hda_dsp_rom_msg[i].msg, status);
return;
}
}
@@ -527,7 +515,8 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
}
/* dump the first 8 dwords representing the extended ROM status */
-static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags)
+static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level,
+ u32 flags)
{
char msg[128];
int len = 0;
@@ -539,18 +528,19 @@ 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);
}
- dev_err(sdev->dev, "extended rom status: %s", msg);
+ dev_printk(level, sdev->dev, "extended rom status: %s", msg);
}
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
{
+ char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR;
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[HDA_DSP_STACK_DUMP_SIZE];
/* print ROM/FW status */
- hda_dsp_get_status(sdev);
+ hda_dsp_get_status(sdev, level);
if (flags & SOF_DBG_DUMP_REGS) {
u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS);
@@ -558,10 +548,10 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
HDA_DSP_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
- stack, HDA_DSP_STACK_DUMP_SIZE);
+ sof_print_oops_and_stack(sdev, level, status, panic, &xoops,
+ &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE);
} else {
- hda_dsp_dump_ext_rom_status(sdev, flags);
+ hda_dsp_dump_ext_rom_status(sdev, level, flags);
}
}
@@ -618,7 +608,10 @@ static int hda_init(struct snd_sof_dev *sdev)
/* HDA bus init */
sof_hda_bus_init(bus, &pci->dev);
- bus->use_posbuf = 1;
+ if (sof_hda_position_quirk == SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS)
+ bus->use_posbuf = 0;
+ else
+ bus->use_posbuf = 1;
bus->bdl_pos_adj = 0;
bus->sync_write = 1;
@@ -915,6 +908,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
goto err;
}
+ sdev->num_cores = chip->cores_num;
+
hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
if (!hdev)
return -ENOMEM;
@@ -1050,9 +1045,9 @@ err:
int hda_dsp_remove(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
struct hdac_bus *bus = sof_to_bus(sdev);
struct pci_dev *pci = to_pci_dev(sdev->dev);
- const struct sof_intel_dsp_desc *chip = hda->desc;
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
@@ -1077,7 +1072,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
/* disable cores */
if (chip)
- snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
+ hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
/* disable DSP */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
@@ -1104,7 +1099,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-static int hda_generic_machine_select(struct snd_sof_dev *sdev)
+static void hda_generic_machine_select(struct snd_sof_dev *sdev,
+ struct snd_soc_acpi_mach **mach)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_soc_acpi_mach_params *mach_params;
@@ -1136,7 +1132,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
* - one HDMI codec, and/or
* - one external HDAudio codec
*/
- if (!pdata->machine && codec_num <= 2) {
+ if (!*mach && codec_num <= 2) {
hda_mach = snd_soc_acpi_intel_hda_machines;
dev_info(bus->dev, "using HDA machine driver %s now\n",
@@ -1151,10 +1147,9 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
tplg_filename = hda_mach->sof_tplg_filename;
ret = dmic_topology_fixup(sdev, &tplg_filename, idisp_str, &dmic_num);
if (ret < 0)
- return ret;
+ return;
hda_mach->mach_params.dmic_num = dmic_num;
- pdata->machine = hda_mach;
pdata->tplg_filename = tplg_filename;
if (codec_num == 2) {
@@ -1164,23 +1159,22 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
*/
hda_mach->mach_params.link_mask = 0;
}
+
+ *mach = hda_mach;
}
}
/* used by hda machine driver to create dai links */
- if (pdata->machine) {
- mach_params = (struct snd_soc_acpi_mach_params *)
- &pdata->machine->mach_params;
+ if (*mach) {
+ mach_params = &(*mach)->mach_params;
mach_params->codec_mask = bus->codec_mask;
mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi;
}
-
- return 0;
}
#else
-static int hda_generic_machine_select(struct snd_sof_dev *sdev)
+static void hda_generic_machine_select(struct snd_sof_dev *sdev,
+ struct snd_soc_acpi_mach **mach)
{
- return 0;
}
#endif
@@ -1263,7 +1257,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev,
return true;
}
-static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct snd_soc_acpi_link_adr *link;
@@ -1281,7 +1275,7 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
* machines, for mixed cases with I2C/I2S the detection relies
* on the HID list.
*/
- if (link_mask && !pdata->machine) {
+ if (link_mask) {
for (mach = pdata->desc->alt_machines;
mach && mach->link_mask; mach++) {
/*
@@ -1316,7 +1310,6 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
if (mach && mach->link_mask) {
int dmic_num = 0;
- pdata->machine = mach;
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
mach->mach_params.platform = dev_name(sdev->dev);
@@ -1338,9 +1331,8 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
int ret;
ret = dmic_topology_fixup(sdev, &tplg_filename, "", &dmic_num);
-
if (ret < 0)
- return ret;
+ return NULL;
pdata->tplg_filename = tplg_filename;
}
@@ -1350,35 +1342,36 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
"SoundWire machine driver %s topology %s\n",
mach->drv_name,
pdata->tplg_filename);
- } else {
- dev_info(sdev->dev,
- "No SoundWire machine driver found\n");
+
+ return mach;
}
+
+ dev_info(sdev->dev, "No SoundWire machine driver found\n");
}
- return 0;
+ return NULL;
}
#else
-static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
+static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev)
{
- return 0;
+ return NULL;
}
#endif
-void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
+void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
struct snd_soc_acpi_mach_params *mach_params;
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
+ mach_params = &mach->mach_params;
mach_params->platform = dev_name(sdev->dev);
mach_params->num_dai_drivers = desc->ops->num_drv;
mach_params->dai_drivers = desc->ops->drv;
}
-void hda_machine_select(struct snd_sof_dev *sdev)
+struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
@@ -1393,8 +1386,6 @@ void hda_machine_select(struct snd_sof_dev *sdev)
if (!sof_pdata->tplg_filename)
sof_pdata->tplg_filename = mach->sof_tplg_filename;
- sof_pdata->machine = mach;
-
if (mach->link_mask) {
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
@@ -1404,16 +1395,18 @@ void hda_machine_select(struct snd_sof_dev *sdev)
/*
* If I2S fails, try SoundWire
*/
- hda_sdw_machine_select(sdev);
+ if (!mach)
+ mach = hda_sdw_machine_select(sdev);
/*
* Choose HDA generic machine driver if mach is NULL.
* Otherwise, set certain mach params.
*/
- hda_generic_machine_select(sdev);
-
- if (!sof_pdata->machine)
+ hda_generic_machine_select(sdev, &mach);
+ if (!mach)
dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
+
+ return mach;
}
int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 1195018a1f4f..03a6bb7a165c 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -273,7 +273,7 @@
#define BXT_D0I3_DELAY 5000
#define FW_CL_STREAM_NUMBER 0x1
-#define HDA_FW_BOOT_ATTEMPTS 3
+#define HDA_FW_BOOT_ATTEMPTS 3
/* ADSPCS - Audio DSP Control & Status */
@@ -487,6 +487,8 @@ struct sof_intel_hda_stream {
(SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \
+ SOF_HDA_ADSP_LOADER_BASE)
+#define SOF_STREAM_SD_OFFSET_CRST 0x1
+
/*
* DSP Core services.
*/
@@ -496,6 +498,7 @@ int hda_dsp_core_run(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_reset_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask);
+int hda_dsp_core_get(struct snd_sof_dev *sdev, int core);
void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
@@ -533,6 +536,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
+int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
/*
* DSP Stream Operations.
@@ -614,8 +618,6 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev);
/* pre and post fw run ops */
int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev);
int hda_dsp_post_fw_run(struct snd_sof_dev *sdev);
-int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev);
-int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask);
/* parse platform specific ext manifest ops */
int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
@@ -726,8 +728,8 @@ extern const struct sof_intel_dsp_desc jsl_chip_info;
extern const struct sof_intel_dsp_desc adls_chip_info;
/* machine driver select */
-void hda_machine_select(struct snd_sof_dev *sdev);
-void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
+struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev);
+void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev);
/* PCI driver selection and probe */
@@ -735,7 +737,13 @@ 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);
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
+
+#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
+#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
+#define SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE (2) /* recommended with VC0 or VC1 */
+
+extern int sof_hda_position_quirk;
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 0b2cc331d55b..f75e3983969f 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -18,12 +18,75 @@
#include "hda-ipc.h"
#include "../sof-audio.h"
+#define ICL_DSP_HPRO_CORE_ID 3
+
static const struct snd_sof_debugfs_map icl_dsp_debugfs[] = {
{"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
{"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
{"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
};
+static int icl_dsp_core_stall(struct snd_sof_dev *sdev, unsigned int core_mask)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+
+ /* make sure core_mask in host managed cores */
+ core_mask &= chip->host_managed_cores_mask;
+ if (!core_mask) {
+ dev_err(sdev->dev, "error: core_mask is not in host managed cores\n");
+ return -EINVAL;
+ }
+
+ /* stall core */
+ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS,
+ HDA_DSP_ADSPCS_CSTALL_MASK(core_mask),
+ HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
+
+ return 0;
+}
+
+/*
+ * post fw run operation for ICL.
+ * Core 3 will be powered up and in stall when HPRO is enabled
+ */
+static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ int ret;
+
+ if (sdev->first_boot) {
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: could not startup SoundWire links\n");
+ return ret;
+ }
+ }
+
+ hda_sdw_int_enable(sdev, true);
+
+ /*
+ * The recommended HW programming sequence for ICL is to
+ * power up core 3 and keep it in stall if HPRO is enabled.
+ */
+ if (!hda->clk_config_lpro) {
+ ret = hda_dsp_enable_core(sdev, BIT(ICL_DSP_HPRO_CORE_ID));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: dsp core power up failed on core %d\n",
+ ICL_DSP_HPRO_CORE_ID);
+ return ret;
+ }
+
+ sdev->enabled_cores_mask |= BIT(ICL_DSP_HPRO_CORE_ID);
+ sdev->dsp_core_ref_count[ICL_DSP_HPRO_CORE_ID]++;
+
+ snd_sof_dsp_stall(sdev, BIT(ICL_DSP_HPRO_CORE_ID));
+ }
+
+ /* re-enable clock gating and power gating */
+ return hda_dsp_ctrl_clock_power_gating(sdev, true);
+}
+
/* Icelake ops */
const struct snd_sof_dsp_ops sof_icl_ops = {
/* probe/remove/shutdown */
@@ -77,6 +140,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
@@ -92,18 +156,17 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
/* pre/post fw run */
.pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run_icl,
+ .post_fw_run = icl_dsp_post_fw_run,
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
+ /* dsp core get/put */
+ .core_get = hda_dsp_core_get,
/* firmware run */
.run = hda_dsp_cl_boot_firmware_iccmax,
- .stall = hda_dsp_core_stall_icl,
+ .stall = icl_dsp_core_stall,
/* trace callback */
.trace_init = hda_dsp_trace_init,
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
index 18eb41b8a8f4..f8c841caa362 100644
--- a/sound/soc/sof/intel/pci-tng.c
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -55,9 +55,18 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev)
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
struct pci_dev *pci = to_pci_dev(sdev->dev);
+ const struct sof_intel_dsp_desc *chip;
u32 base, size;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "error: no such device supported\n");
+ return -EIO;
+ }
+
+ sdev->num_cores = chip->cores_num;
+
/* DSP DMA can only access low 31 bits of host memory */
ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
if (ret < 0) {
diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h
index e9f7d4d7fcce..f36cd9d5eb94 100644
--- a/sound/soc/sof/intel/shim.h
+++ b/sound/soc/sof/intel/shim.h
@@ -151,6 +151,9 @@
#define PCI_PMCS 0x84
#define PCI_PMCS_PS_MASK 0x3
+/* Intel quirks */
+#define SOF_INTEL_PROCEN_FMT_QUIRK BIT(0)
+
/* DSP hardware descriptor */
struct sof_intel_dsp_desc {
int cores_num;
@@ -166,6 +169,7 @@ struct sof_intel_dsp_desc {
int ssp_base_offset; /* base address of the SSPs */
u32 sdw_shim_base;
u32 sdw_alh_base;
+ u32 quirks;
bool (*check_sdw_irq)(struct snd_sof_dev *sdev);
};
@@ -177,4 +181,11 @@ struct sof_intel_stream {
size_t posn_offset;
};
+static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata)
+{
+ const struct sof_dev_desc *desc = pdata->desc;
+
+ return desc->chip_info;
+}
+
#endif
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 48da8e7a67bc..7f7929c5cb88 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -20,6 +20,46 @@ static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = {
{"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
};
+static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ struct sof_ipc_pm_core_config pm_core_config = {
+ .hdr = {
+ .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
+ .size = sizeof(pm_core_config),
+ },
+ .enable_mask = sdev->enabled_cores_mask | BIT(core),
+ };
+
+ /* power up primary core if not already powered up and return */
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return hda_dsp_enable_core(sdev, BIT(core));
+
+ /* notify DSP for secondary cores */
+ return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
+ &pm_core_config, sizeof(pm_core_config),
+ &pm_core_config, sizeof(pm_core_config));
+}
+
+static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+{
+ struct sof_ipc_pm_core_config pm_core_config = {
+ .hdr = {
+ .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
+ .size = sizeof(pm_core_config),
+ },
+ .enable_mask = sdev->enabled_cores_mask & ~BIT(core),
+ };
+
+ /* power down primary core and return */
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return hda_dsp_core_reset_power_down(sdev, BIT(core));
+
+ /* notify DSP for secondary cores */
+ return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
+ &pm_core_config, sizeof(pm_core_config),
+ &pm_core_config, sizeof(pm_core_config));
+}
+
/* Tigerlake ops */
const struct snd_sof_dsp_ops sof_tgl_ops = {
/* probe/remove/shutdown */
@@ -73,6 +113,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
@@ -93,9 +134,9 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
- /* dsp core power up/down */
- .core_power_up = hda_dsp_enable_core,
- .core_power_down = hda_dsp_core_reset_power_down,
+ /* dsp core get/put */
+ .core_get = tgl_dsp_core_get,
+ .core_put = tgl_dsp_core_put,
/* firmware run */
.run = hda_dsp_cl_boot_firmware_iccmax,
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index e6c53c6c470e..5bcf906d90af 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -173,7 +173,22 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
}
break;
case SOF_IPC_GLB_TRACE_MSG:
- str = "GLB_TRACE_MSG"; break;
+ str = "GLB_TRACE_MSG";
+ switch (type) {
+ case SOF_IPC_TRACE_DMA_PARAMS:
+ str2 = "DMA_PARAMS"; break;
+ case SOF_IPC_TRACE_DMA_POSITION:
+ str2 = "DMA_POSITION"; break;
+ case SOF_IPC_TRACE_DMA_PARAMS_EXT:
+ str2 = "DMA_PARAMS_EXT"; break;
+ case SOF_IPC_TRACE_FILTER_UPDATE:
+ str2 = "FILTER_UPDATE"; break;
+ case SOF_IPC_TRACE_DMA_FREE:
+ str2 = "DMA_FREE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
case SOF_IPC_GLB_TEST_MSG:
str = "GLB_TEST_MSG";
switch (type) {
@@ -287,7 +302,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
struct snd_sof_ipc_msg *msg;
int ret;
- if (ipc->disable_ipc_tx)
+ if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
return -ENODEV;
/*
@@ -379,6 +394,67 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
}
EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
+/* Generic helper function to retrieve the reply */
+void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ /*
+ * Sometimes, there is unexpected reply ipc arriving. The reply
+ * ipc belongs to none of the ipcs sent from driver.
+ * In this case, the driver must ignore the ipc.
+ */
+ if (!msg) {
+ dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
+ return;
+ }
+
+ /* get the generic reply */
+ snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply,
+ sizeof(reply));
+
+ if (reply.error < 0) {
+ memcpy(msg->reply_data, &reply, sizeof(reply));
+ ret = reply.error;
+ } else if (!reply.hdr.size) {
+ /* Reply should always be >= sizeof(struct sof_ipc_reply) */
+ if (msg->reply_size)
+ dev_err(sdev->dev,
+ "empty reply received, expected %zu bytes\n",
+ msg->reply_size);
+ else
+ dev_err(sdev->dev, "empty reply received\n");
+
+ ret = -EINVAL;
+ } else if (msg->reply_size > 0) {
+ if (reply.hdr.size == msg->reply_size) {
+ ret = 0;
+ } else if (reply.hdr.size < msg->reply_size) {
+ dev_dbg(sdev->dev,
+ "reply size (%u) is less than expected (%zu)\n",
+ reply.hdr.size, msg->reply_size);
+
+ msg->reply_size = reply.hdr.size;
+ ret = 0;
+ } else {
+ dev_err(sdev->dev,
+ "reply size (%u) exceeds the buffer size (%zu)\n",
+ reply.hdr.size, msg->reply_size);
+ ret = -EINVAL;
+ }
+
+ /* get the full message if reply.hdr.size <= msg->reply_size */
+ if (!ret)
+ snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset,
+ msg->reply_data, msg->reply_size);
+ }
+
+ msg->reply_error = ret;
+}
+EXPORT_SYMBOL(snd_sof_ipc_get_reply);
+
/* handle reply message from DSP */
void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
@@ -460,7 +536,7 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
if (err < 0)
sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
else
- sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
/* wake up firmware loader */
wake_up(&sdev->boot_wait);
@@ -547,7 +623,8 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
if (spcm->pcm.compress)
snd_sof_compr_fragment_elapsed(stream->cstream);
- else if (!stream->substream->runtime->no_period_wakeup)
+ else if (stream->substream->runtime &&
+ !stream->substream->runtime->no_period_wakeup)
/* only inform ALSA for period_wakeup mode */
snd_sof_pcm_period_elapsed(stream->substream);
}
@@ -645,11 +722,6 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
sparams->src = (u8 *)src->chanv;
sparams->dst = (u8 *)dst->chanv;
break;
- case SOF_CTRL_TYPE_VALUE_COMP_GET:
- case SOF_CTRL_TYPE_VALUE_COMP_SET:
- sparams->src = (u8 *)src->compv;
- sparams->dst = (u8 *)dst->compv;
- break;
case SOF_CTRL_TYPE_DATA_GET:
case SOF_CTRL_TYPE_DATA_SET:
sparams->src = (u8 *)src->data->data;
@@ -669,7 +741,7 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
struct sof_ipc_ctrl_data *cdata,
struct sof_ipc_ctrl_data_params *sparams,
- bool send)
+ bool set)
{
struct sof_ipc_ctrl_data *partdata;
size_t send_bytes;
@@ -684,7 +756,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
if (!partdata)
return -ENOMEM;
- if (send)
+ if (set)
err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata,
sparams);
else
@@ -713,7 +785,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
msg_bytes -= send_bytes;
partdata->elems_remaining = msg_bytes;
- if (send)
+ if (set)
memcpy(sparams->dst, sparams->src + offset, send_bytes);
err = sof_ipc_tx_message_unlocked(sdev->ipc,
@@ -725,7 +797,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
if (err < 0)
break;
- if (!send)
+ if (!set)
memcpy(sparams->dst + offset, sparams->src, send_bytes);
offset += pl_size;
@@ -740,11 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
/*
* IPC get()/set() for kcontrols.
*/
-int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
- u32 ipc_cmd,
- enum sof_ipc_ctrl_type ctrl_type,
- enum sof_ipc_ctrl_cmd ctrl_cmd,
- bool send)
+int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set)
{
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
@@ -752,9 +820,11 @@ 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;
+ enum sof_ipc_ctrl_type ctrl_type;
struct snd_sof_widget *swidget;
bool widget_found = false;
size_t send_bytes;
+ u32 ipc_cmd;
int err;
list_for_each_entry(swidget, &sdev->widget_list, list) {
@@ -782,7 +852,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
/* write/read value header via mmaped region */
send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
cdata->num_elems;
- if (send)
+ if (set)
err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM,
scontrol->readback_offset,
cdata->chanv, send_bytes);
@@ -794,12 +864,25 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
if (err)
dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n",
- send ? "write to" : "read from");
+ set ? "write to" : "read from");
return err;
}
+ /*
+ * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
+ * direction
+ * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently
+ * for ctrl_type
+ */
+ if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
+ ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;
+ ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;
+ } else {
+ ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;
+ ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;
+ }
+
cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
- cdata->cmd = ctrl_cmd;
cdata->type = ctrl_type;
cdata->comp_id = scontrol->comp_id;
cdata->msg_index = 0;
@@ -813,13 +896,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
sparams.elems = scontrol->num_channels;
break;
- case SOF_CTRL_TYPE_VALUE_COMP_GET:
- case SOF_CTRL_TYPE_VALUE_COMP_SET:
- sparams.msg_bytes = scontrol->num_channels *
- sizeof(struct sof_ipc_ctrl_value_comp);
- sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
- sparams.elems = scontrol->num_channels;
- break;
case SOF_CTRL_TYPE_DATA_GET:
case SOF_CTRL_TYPE_DATA_SET:
sparams.msg_bytes = cdata->data->size;
@@ -858,7 +934,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
return -EINVAL;
}
- err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send);
+ err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set);
if (err < 0)
dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n",
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index c04646647637..697f03565a70 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -820,8 +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 start DSP\n");
- snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
+ snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
+ SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
return ret;
}
@@ -835,16 +835,13 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
msecs_to_jiffies(sdev->boot_timeout));
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 |
+ snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
+ SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
- sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return -EIO;
}
- if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
- dev_dbg(sdev->dev, "firmware boot complete\n");
- else
+ if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
return -EIO; /* FW boots but fw_ready op failed */
/* perform post fw run operations */
@@ -854,6 +851,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
return ret;
}
+ dev_dbg(sdev->dev, "firmware boot complete\n");
+ sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
+
return 0;
}
EXPORT_SYMBOL(snd_sof_run_firmware);
diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig
new file mode 100644
index 000000000000..aeacf0e5bfbb
--- /dev/null
+++ b/sound/soc/sof/mediatek/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+
+config SND_SOC_SOF_MTK_TOPLEVEL
+ bool "SOF support for MTK audio DSPs"
+ depends on ARM64 || COMPILE_TEST
+ depends on SND_SOC_SOF_OF
+ help
+ This adds support for Sound Open Firmware for Mediatek platforms.
+ It is top level for all mediatek platforms.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_SOF_MTK_TOPLEVEL
+config SND_SOC_SOF_MTK_COMMON
+ tristate
+ select SND_SOC_SOF_OF_DEV
+ select SND_SOC_SOF
+ 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
+
+config SND_SOC_SOF_MT8195
+ tristate "SOF support for MT8195 audio DSP"
+ select SND_SOC_SOF_MTK_COMMON
+ help
+ This adds support for Sound Open Firmware for Mediatek platforms
+ using the mt8195 processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_MTK_TOPLEVEL
diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile
new file mode 100644
index 000000000000..e8ec6da981de
--- /dev/null
+++ b/sound/soc/sof/mediatek/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/
diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h
new file mode 100644
index 000000000000..6734e2c0c6b1
--- /dev/null
+++ b/sound/soc/sof/mediatek/adsp_helper.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2021 MediaTek Corporation. All rights reserved.
+ */
+
+#ifndef __MTK_ADSP_HELPER_H__
+#define __MTK_ADSP_HELPER_H__
+
+/*
+ * Global important adsp data structure.
+ */
+#define DSP_MBOX_NUM 3
+
+struct mtk_adsp_chip_info {
+ phys_addr_t pa_sram;
+ phys_addr_t pa_dram; /* adsp dram physical base */
+ phys_addr_t pa_shared_dram; /* adsp dram physical base */
+ phys_addr_t pa_cfgreg;
+ phys_addr_t pa_mboxreg[DSP_MBOX_NUM];
+ u32 sramsize;
+ u32 dramsize;
+ u32 cfgregsize;
+ void __iomem *va_sram; /* corresponding to pa_sram */
+ void __iomem *va_dram; /* corresponding to pa_dram */
+ void __iomem *va_cfgreg;
+ void __iomem *va_mboxreg[DSP_MBOX_NUM];
+ void __iomem *shared_sram; /* part of va_sram */
+ void __iomem *shared_dram; /* part of va_dram */
+ phys_addr_t adsp_bootup_addr;
+ int dram_offset; /*dram offset between system and dsp view*/
+};
+
+struct adsp_priv {
+ struct device *dev;
+ struct snd_sof_dev *sdev;
+
+ /* DSP IPC handler */
+ struct mbox_controller *adsp_mbox;
+
+ struct mtk_adsp_chip_info *adsp;
+ struct clk **clk;
+ u32 (*ap2adsp_addr)(u32 addr, void *data);
+ u32 (*adsp2ap_addr)(u32 addr, void *data);
+
+ void *private_data;
+};
+
+#endif
diff --git a/sound/soc/sof/mediatek/mt8195/Makefile b/sound/soc/sof/mediatek/mt8195/Makefile
new file mode 100644
index 000000000000..afc4f21fccc5
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+snd-sof-mt8195-objs := mt8195.o mt8195-clk.o mt8195-loader.o
+obj-$(CONFIG_SND_SOC_SOF_MT8195) += snd-sof-mt8195.o
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
new file mode 100644
index 000000000000..6bcb4b9b00fb
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2021 Mediatek Corporation. All rights reserved.
+//
+// Author: YC Hung <yc.hung@mediatek.com>
+//
+// Hardware interface for mt8195 DSP clock
+
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include "mt8195.h"
+#include "mt8195-clk.h"
+#include "../adsp_helper.h"
+#include "../../sof-audio.h"
+
+static const char *adsp_clks[ADSP_CLK_MAX] = {
+ [CLK_TOP_ADSP] = "adsp_sel",
+ [CLK_TOP_CLK26M] = "clk26m_ck",
+ [CLK_TOP_AUDIO_LOCAL_BUS] = "audio_local_bus",
+ [CLK_TOP_MAINPLL_D7_D2] = "mainpll_d7_d2",
+ [CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp",
+ [CLK_TOP_AUDIO_H] = "audio_h",
+};
+
+int mt8195_adsp_init_clock(struct snd_sof_dev *sdev)
+{
+ struct device *dev = sdev->dev;
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ int i;
+
+ priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL);
+
+ if (!priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < ADSP_CLK_MAX; i++) {
+ priv->clk[i] = devm_clk_get(dev, adsp_clks[i]);
+ if (IS_ERR(priv->clk[i]))
+ return PTR_ERR(priv->clk[i]);
+ }
+
+ return 0;
+}
+
+static int adsp_enable_all_clock(struct snd_sof_dev *sdev)
+{
+ struct device *dev = sdev->dev;
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(mainpll_d7_d2) fail %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(adsp_sel) fail %d\n",
+ __func__, ret);
+ goto disable_mainpll_d7_d2_clk;
+ }
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(audio_local_bus) fail %d\n",
+ __func__, ret);
+ goto disable_dsp_sel_clk;
+ }
+
+ ret = clk_prepare_enable(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(scp_adsp_audiodsp) fail %d\n",
+ __func__, ret);
+ goto disable_audio_local_bus_clk;
+ }
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_H]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(audio_h) fail %d\n",
+ __func__, ret);
+ goto disable_scp_adsp_audiodsp_clk;
+ }
+
+ return 0;
+
+disable_scp_adsp_audiodsp_clk:
+ clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
+disable_audio_local_bus_clk:
+ clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
+disable_dsp_sel_clk:
+ clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]);
+disable_mainpll_d7_d2_clk:
+ clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
+
+ return ret;
+}
+
+static void adsp_disable_all_clock(struct snd_sof_dev *sdev)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+ clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_H]);
+ clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
+ clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
+ clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]);
+ clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
+}
+
+static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable)
+{
+ struct device *dev = sdev->dev;
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ int ret;
+
+ dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off");
+
+ if (enable) {
+ ret = clk_set_parent(priv->clk[CLK_TOP_ADSP],
+ priv->clk[CLK_TOP_CLK26M]);
+ if (ret) {
+ dev_err(dev, "failed to set dsp_sel to clk26m: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_set_parent(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS],
+ priv->clk[CLK_TOP_MAINPLL_D7_D2]);
+ if (ret) {
+ dev_err(dev, "set audio_local_bus failed %d\n", ret);
+ return ret;
+ }
+
+ ret = adsp_enable_all_clock(sdev);
+ if (ret) {
+ dev_err(dev, "failed to adsp_enable_clock: %d\n", ret);
+ return ret;
+ }
+ } else {
+ adsp_disable_all_clock(sdev);
+ }
+
+ return 0;
+}
+
+int adsp_clock_on(struct snd_sof_dev *sdev)
+{
+ /* Open ADSP clock */
+ return adsp_default_clk_init(sdev, 1);
+}
+
+int adsp_clock_off(struct snd_sof_dev *sdev)
+{
+ /* Close ADSP clock */
+ return adsp_default_clk_init(sdev, 0);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.h b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h
new file mode 100644
index 000000000000..9cc0573d5cd2
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2021 MediaTek Corporation. All rights reserved.
+ *
+ * Header file for the mt8195 DSP clock definition
+ */
+
+#ifndef __MT8195_CLK_H
+#define __MT8195_CLK_H
+
+struct snd_sof_dev;
+
+/*DSP clock*/
+enum adsp_clk_id {
+ CLK_TOP_ADSP,
+ CLK_TOP_CLK26M,
+ CLK_TOP_AUDIO_LOCAL_BUS,
+ CLK_TOP_MAINPLL_D7_D2,
+ CLK_SCP_ADSP_AUDIODSP,
+ CLK_TOP_AUDIO_H,
+ ADSP_CLK_MAX
+};
+
+int mt8195_adsp_init_clock(struct snd_sof_dev *sdev);
+int adsp_clock_on(struct snd_sof_dev *sdev);
+int adsp_clock_off(struct snd_sof_dev *sdev);
+#endif
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-loader.c b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c
new file mode 100644
index 000000000000..ed18d6379e92
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright (c) 2021 Mediatek Corporation. All rights reserved.
+//
+// Author: YC Hung <yc.hung@mediatek.com>
+//
+// Hardware interface for mt8195 DSP code loader
+
+#include <sound/sof.h>
+#include "mt8195.h"
+#include "../../ops.h"
+
+void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr)
+{
+ /* ADSP bootup base */
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_ALTRESETVEC, boot_addr);
+
+ /* pull high RunStall (set bit3 to 1) */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ ADSP_RUNSTALL, ADSP_RUNSTALL);
+
+ /* pull high StatVectorSel to use AltResetVec (set bit4 to 1) */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ DSP_RESET_SW, DSP_RESET_SW);
+
+ /* toggle DReset & BReset */
+ /* pull high DReset & BReset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ ADSP_BRESET_SW | ADSP_DRESET_SW,
+ ADSP_BRESET_SW | ADSP_DRESET_SW);
+
+ /* pull low DReset & BReset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ ADSP_BRESET_SW | ADSP_DRESET_SW,
+ 0);
+
+ /* Enable PDebug */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0,
+ PDEBUG_ENABLE,
+ PDEBUG_ENABLE);
+
+ /* release RunStall (set bit3 to 0) */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ ADSP_RUNSTALL, 0);
+}
+
+void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev)
+{
+ /* Clear to 0 firstly */
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_RESET_SW, 0x0);
+
+ /* RUN_STALL pull high again to reset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW,
+ ADSP_RUNSTALL, ADSP_RUNSTALL);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
new file mode 100644
index 000000000000..3ab12f352935
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2021 Mediatek Inc. All rights reserved.
+//
+// Author: YC Hung <yc.hung@mediatek.com>
+//
+
+/*
+ * Hardware interface for audio DSP on mt8195
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/module.h>
+
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include "../../ops.h"
+#include "../../sof-of-dev.h"
+#include "../../sof-audio.h"
+#include "../adsp_helper.h"
+#include "mt8195.h"
+#include "mt8195-clk.h"
+
+static int platform_parse_resource(struct platform_device *pdev, void *data)
+{
+ struct resource *mmio;
+ struct resource res;
+ struct device_node *mem_region;
+ struct device *dev = &pdev->dev;
+ struct mtk_adsp_chip_info *adsp = data;
+ int ret;
+
+ mem_region = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!mem_region) {
+ dev_err(dev, "no dma memory-region phandle\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(mem_region, 0, &res);
+ of_node_put(mem_region);
+ if (ret) {
+ dev_err(dev, "of_address_to_resource dma failed\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "DMA %pR\n", &res);
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_err(dev, "of_reserved_mem_device_init failed\n");
+ return ret;
+ }
+
+ mem_region = of_parse_phandle(dev->of_node, "memory-region", 1);
+ if (!mem_region) {
+ dev_err(dev, "no memory-region sysmem phandle\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(mem_region, 0, &res);
+ of_node_put(mem_region);
+ if (ret) {
+ dev_err(dev, "of_address_to_resource sysmem failed\n");
+ return ret;
+ }
+
+ adsp->pa_dram = (phys_addr_t)res.start;
+ adsp->dramsize = resource_size(&res);
+ if (adsp->pa_dram & DRAM_REMAP_MASK) {
+ dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n",
+ (u32)adsp->pa_dram);
+ return -EINVAL;
+ }
+
+ if (adsp->dramsize < TOTAL_SIZE_SHARED_DRAM_FROM_TAIL) {
+ dev_err(dev, "adsp memory(%#x) is not enough for share\n",
+ adsp->dramsize);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "dram pbase=%pa, dramsize=%#x\n",
+ &adsp->pa_dram, adsp->dramsize);
+
+ /* Parse CFG base */
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ if (!mmio) {
+ dev_err(dev, "no ADSP-CFG register resource\n");
+ return -ENXIO;
+ }
+ /* remap for DSP register accessing */
+ adsp->va_cfgreg = devm_ioremap_resource(dev, mmio);
+ if (IS_ERR(adsp->va_cfgreg))
+ return PTR_ERR(adsp->va_cfgreg);
+
+ adsp->pa_cfgreg = (phys_addr_t)mmio->start;
+ adsp->cfgregsize = resource_size(mmio);
+
+ dev_dbg(dev, "cfgreg-vbase=%p, cfgregsize=%#x\n",
+ adsp->va_cfgreg, adsp->cfgregsize);
+
+ /* Parse SRAM */
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+ if (!mmio) {
+ dev_err(dev, "no SRAM resource\n");
+ return -ENXIO;
+ }
+
+ adsp->pa_sram = (phys_addr_t)mmio->start;
+ adsp->sramsize = resource_size(mmio);
+ if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) {
+ dev_err(dev, "adsp SRAM(%#x) is not enough for share\n",
+ adsp->sramsize);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize);
+
+ return ret;
+}
+
+static int adsp_sram_power_on(struct device *dev, bool on)
+{
+ void __iomem *va_dspsysreg;
+ u32 srampool_con;
+
+ va_dspsysreg = ioremap(ADSP_SRAM_POOL_CON, 0x4);
+ if (!va_dspsysreg) {
+ dev_err(dev, "failed to ioremap sram pool base %#x\n",
+ ADSP_SRAM_POOL_CON);
+ return -ENOMEM;
+ }
+
+ srampool_con = readl(va_dspsysreg);
+ if (on)
+ writel(srampool_con & ~DSP_SRAM_POOL_PD_MASK, va_dspsysreg);
+ else
+ writel(srampool_con | DSP_SRAM_POOL_PD_MASK, va_dspsysreg);
+
+ iounmap(va_dspsysreg);
+ return 0;
+}
+
+/* Init the basic DSP DRAM address */
+static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info *adsp)
+{
+ void __iomem *vaddr_emi_map;
+ int offset;
+
+ if (!adsp)
+ return -ENXIO;
+
+ vaddr_emi_map = devm_ioremap(dev, DSP_EMI_MAP_ADDR, 0x4);
+ if (!vaddr_emi_map) {
+ dev_err(dev, "failed to ioremap emi map base %#x\n",
+ DSP_EMI_MAP_ADDR);
+ return -ENOMEM;
+ }
+
+ offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW;
+ adsp->dram_offset = offset;
+ offset >>= DRAM_REMAP_SHIFT;
+ dev_dbg(dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset);
+ writel(offset, vaddr_emi_map);
+ if (offset != readl(vaddr_emi_map)) {
+ dev_err(dev, "write emi map fail : %#x\n", readl(vaddr_emi_map));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_adsp_chip_info *adsp = data;
+ u32 shared_size;
+
+ /* remap shared-dram base to be non-cachable */
+ shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL;
+ adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size;
+ if (adsp->va_dram) {
+ adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size;
+ } else {
+ adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram,
+ shared_size);
+ if (!adsp->shared_dram) {
+ dev_err(dev, "ioremap failed for shared DRAM\n");
+ return -ENOMEM;
+ }
+ }
+ dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n",
+ adsp->shared_dram, &adsp->pa_shared_dram, shared_size);
+
+ return 0;
+}
+
+static int mt8195_run(struct snd_sof_dev *sdev)
+{
+ u32 adsp_bootup_addr;
+
+ adsp_bootup_addr = SRAM_PHYS_BASE_FROM_DSP_VIEW;
+ dev_dbg(sdev->dev, "HIFIxDSP boot from base : 0x%08X\n", adsp_bootup_addr);
+ sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr);
+
+ return 0;
+}
+
+static int mt8195_dsp_probe(struct snd_sof_dev *sdev)
+{
+ struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+ struct adsp_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->pdata->hw_pdata = priv;
+ priv->dev = sdev->dev;
+ priv->sdev = sdev;
+
+ priv->adsp = devm_kzalloc(&pdev->dev, sizeof(struct mtk_adsp_chip_info), GFP_KERNEL);
+ if (!priv->adsp)
+ return -ENOMEM;
+
+ ret = platform_parse_resource(pdev, priv->adsp);
+ if (ret)
+ return ret;
+
+ ret = mt8195_adsp_init_clock(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "mt8195_adsp_init_clock failed\n");
+ return -EINVAL;
+ }
+
+ ret = adsp_clock_on(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_clock_on fail!\n");
+ return -EINVAL;
+ }
+
+ ret = adsp_sram_power_on(sdev->dev, true);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_sram_power_on fail!\n");
+ goto exit_clk_disable;
+ }
+
+ ret = adsp_memory_remap_init(&pdev->dev, priv->adsp);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_memory_remap_init fail!\n");
+ goto err_adsp_sram_power_off;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev,
+ priv->adsp->pa_sram,
+ priv->adsp->sramsize);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n",
+ &priv->adsp->pa_sram, priv->adsp->sramsize);
+ ret = -EINVAL;
+ goto err_adsp_sram_power_off;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev,
+ priv->adsp->pa_dram,
+ priv->adsp->dramsize);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n",
+ &priv->adsp->pa_dram, priv->adsp->dramsize);
+ ret = -EINVAL;
+ goto err_adsp_sram_power_off;
+ }
+ priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM];
+
+ ret = adsp_shared_base_ioremap(pdev, priv->adsp);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n");
+ goto err_adsp_sram_power_off;
+ }
+
+ sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
+ sdev->bar[DSP_MBOX0_BAR] = priv->adsp->va_mboxreg[0];
+ sdev->bar[DSP_MBOX1_BAR] = priv->adsp->va_mboxreg[1];
+ sdev->bar[DSP_MBOX2_BAR] = priv->adsp->va_mboxreg[2];
+
+ sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM;
+ sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+
+ return 0;
+
+err_adsp_sram_power_off:
+ adsp_sram_power_on(&pdev->dev, false);
+exit_clk_disable:
+ adsp_clock_off(sdev);
+
+ return ret;
+}
+
+static int mt8195_dsp_remove(struct snd_sof_dev *sdev)
+{
+ struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+
+ adsp_sram_power_on(&pdev->dev, false);
+ adsp_clock_off(sdev);
+
+ return 0;
+}
+
+static int mt8195_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
+{
+ struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+ int ret;
+
+ /* stall and reset dsp */
+ sof_hifixdsp_shutdown(sdev);
+
+ /* power down adsp sram */
+ ret = adsp_sram_power_on(&pdev->dev, false);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_sram_power_off fail!\n");
+ return ret;
+ }
+
+ /* turn off adsp clock */
+ return adsp_clock_off(sdev);
+}
+
+static int mt8195_dsp_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* turn on adsp clock */
+ ret = adsp_clock_on(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_clock_on fail!\n");
+ return ret;
+ }
+
+ /* power on adsp sram */
+ ret = adsp_sram_power_on(sdev->dev, true);
+ if (ret)
+ dev_err(sdev->dev, "adsp_sram_power_on fail!\n");
+
+ return ret;
+}
+
+/* on mt8195 there is 1 to 1 match between type and BAR idx */
+static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ return type;
+}
+
+static struct snd_soc_dai_driver mt8195_dai[] = {
+{
+ .name = "SOF_DL2",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_DL3",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL4",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL5",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+};
+
+/* mt8195 ops */
+static const struct snd_sof_dsp_ops sof_mt8195_ops = {
+ /* probe and remove */
+ .probe = mt8195_dsp_probe,
+ .remove = mt8195_dsp_remove,
+
+ /* DSP core boot */
+ .run = mt8195_run,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Register IO */
+ .write = sof_io_write,
+ .read = sof_io_read,
+ .write64 = sof_io_write64,
+ .read64 = sof_io_read64,
+
+ /* misc */
+ .get_bar_index = mt8195_get_bar_index,
+
+ /* module loading */
+ .load_module = snd_sof_parse_module_memcpy,
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* Firmware ops */
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+
+ /* DAI drivers */
+ .drv = mt8195_dai,
+ .num_drv = ARRAY_SIZE(mt8195_dai),
+
+ /* PM */
+ .suspend = mt8195_dsp_suspend,
+ .resume = mt8195_dsp_resume,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+};
+
+static const struct sof_dev_desc sof_of_mt8195_desc = {
+ .default_fw_path = "mediatek/sof",
+ .default_tplg_path = "mediatek/sof-tplg",
+ .default_fw_filename = "sof-mt8195.ri",
+ .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg",
+ .ops = &sof_mt8195_ops,
+};
+
+static const struct of_device_id sof_of_mt8195_ids[] = {
+ { .compatible = "mediatek,mt8195-dsp", .data = &sof_of_mt8195_desc},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_mt8195_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-mt8195",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_mt8195_ids,
+ },
+};
+module_platform_driver(snd_sof_of_mt8195_driver);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h
new file mode 100644
index 000000000000..929424182357
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2021 MediaTek Corporation. All rights reserved.
+ *
+ * Header file for the mt8195 DSP register definition
+ */
+
+#ifndef __MT8195_H
+#define __MT8195_H
+
+struct mtk_adsp_chip_info;
+struct snd_sof_dev;
+
+#define DSP_REG_BASE 0x10803000
+#define SCP_CFGREG_BASE 0x10724000
+#define DSP_SYSAO_BASE 0x1080C000
+
+/*****************************************************************************
+ * R E G I S T E R TABLE
+ *****************************************************************************/
+#define DSP_JTAGMUX 0x0000
+#define DSP_ALTRESETVEC 0x0004
+#define DSP_PDEBUGDATA 0x0008
+#define DSP_PDEBUGBUS0 0x000c
+#define PDEBUG_ENABLE BIT(0)
+#define DSP_PDEBUGBUS1 0x0010
+#define DSP_PDEBUGINST 0x0014
+#define DSP_PDEBUGLS0STAT 0x0018
+#define DSP_PDEBUGLS1STAT 0x001c
+#define DSP_PDEBUGPC 0x0020
+#define DSP_RESET_SW 0x0024 /*reset sw*/
+#define ADSP_BRESET_SW BIT(0)
+#define ADSP_DRESET_SW BIT(1)
+#define ADSP_RUNSTALL BIT(3)
+#define STATVECTOR_SEL BIT(4)
+#define DSP_PFAULTBUS 0x0028
+#define DSP_PFAULTINFO 0x002c
+#define DSP_GPR00 0x0030
+#define DSP_GPR01 0x0034
+#define DSP_GPR02 0x0038
+#define DSP_GPR03 0x003c
+#define DSP_GPR04 0x0040
+#define DSP_GPR05 0x0044
+#define DSP_GPR06 0x0048
+#define DSP_GPR07 0x004c
+#define DSP_GPR08 0x0050
+#define DSP_GPR09 0x0054
+#define DSP_GPR0A 0x0058
+#define DSP_GPR0B 0x005c
+#define DSP_GPR0C 0x0060
+#define DSP_GPR0D 0x0064
+#define DSP_GPR0E 0x0068
+#define DSP_GPR0F 0x006c
+#define DSP_GPR10 0x0070
+#define DSP_GPR11 0x0074
+#define DSP_GPR12 0x0078
+#define DSP_GPR13 0x007c
+#define DSP_GPR14 0x0080
+#define DSP_GPR15 0x0084
+#define DSP_GPR16 0x0088
+#define DSP_GPR17 0x008c
+#define DSP_GPR18 0x0090
+#define DSP_GPR19 0x0094
+#define DSP_GPR1A 0x0098
+#define DSP_GPR1B 0x009c
+#define DSP_GPR1C 0x00a0
+#define DSP_GPR1D 0x00a4
+#define DSP_GPR1E 0x00a8
+#define DSP_GPR1F 0x00ac
+#define DSP_TCM_OFFSET 0x00b0 /* not used */
+#define DSP_DDR_OFFSET 0x00b4 /* not used */
+#define DSP_INTFDSP 0x00d0
+#define DSP_INTFDSP_CLR 0x00d4
+#define DSP_SRAM_PD_SW1 0x00d8
+#define DSP_SRAM_PD_SW2 0x00dc
+#define DSP_OCD 0x00e0
+#define DSP_RG_DSP_IRQ_POL 0x00f0 /* not used */
+#define DSP_DSP_IRQ_EN 0x00f4 /* not used */
+#define DSP_DSP_IRQ_LEVEL 0x00f8 /* not used */
+#define DSP_DSP_IRQ_STATUS 0x00fc /* not used */
+#define DSP_RG_INT2CIRQ 0x0114
+#define DSP_RG_INT_POL_CTL0 0x0120
+#define DSP_RG_INT_EN_CTL0 0x0130
+#define DSP_RG_INT_LV_CTL0 0x0140
+#define DSP_RG_INT_STATUS0 0x0150
+#define DSP_PDEBUGSTATUS0 0x0200
+#define DSP_PDEBUGSTATUS1 0x0204
+#define DSP_PDEBUGSTATUS2 0x0208
+#define DSP_PDEBUGSTATUS3 0x020c
+#define DSP_PDEBUGSTATUS4 0x0210
+#define DSP_PDEBUGSTATUS5 0x0214
+#define DSP_PDEBUGSTATUS6 0x0218
+#define DSP_PDEBUGSTATUS7 0x021c
+#define DSP_DSP2PSRAM_PRIORITY 0x0220 /* not used */
+#define DSP_AUDIO_DSP2SPM_INT 0x0224
+#define DSP_AUDIO_DSP2SPM_INT_ACK 0x0228
+#define DSP_AUDIO_DSP_DEBUG_SEL 0x022C
+#define DSP_AUDIO_DSP_EMI_BASE_ADDR 0x02E0 /* not used */
+#define DSP_AUDIO_DSP_SHARED_IRAM 0x02E4
+#define DSP_AUDIO_DSP_CKCTRL_P2P_CK_CON 0x02F0
+#define DSP_RG_SEMAPHORE00 0x0300
+#define DSP_RG_SEMAPHORE01 0x0304
+#define DSP_RG_SEMAPHORE02 0x0308
+#define DSP_RG_SEMAPHORE03 0x030C
+#define DSP_RG_SEMAPHORE04 0x0310
+#define DSP_RG_SEMAPHORE05 0x0314
+#define DSP_RG_SEMAPHORE06 0x0318
+#define DSP_RG_SEMAPHORE07 0x031C
+#define DSP_RESERVED_0 0x03F0
+#define DSP_RESERVED_1 0x03F4
+
+/* dsp wdt */
+#define DSP_WDT_MODE 0x0400
+
+/* dsp mbox */
+#define DSP_MBOX_IN_CMD 0x00
+#define DSP_MBOX_IN_CMD_CLR 0x04
+#define DSP_MBOX_OUT_CMD 0x1c
+#define DSP_MBOX_OUT_CMD_CLR 0x20
+#define DSP_MBOX_IN_MSG0 0x08
+#define DSP_MBOX_IN_MSG1 0x0C
+#define DSP_MBOX_OUT_MSG0 0x24
+#define DSP_MBOX_OUT_MSG1 0x28
+
+/*dsp sys ao*/
+#define ADSP_SRAM_POOL_CON (DSP_SYSAO_BASE + 0x30)
+#define DSP_SRAM_POOL_PD_MASK 0xf
+#define DSP_EMI_MAP_ADDR (DSP_SYSAO_BASE + 0x81c)
+
+/* DSP memories */
+#define MBOX_OFFSET 0x800000 /* DRAM */
+#define MBOX_SIZE 0x1000 /* consistent with which in memory.h of sof fw */
+#define DSP_DRAM_SIZE 0x1000000 /* 16M */
+
+#define DSP_REG_BAR 4
+#define DSP_MBOX0_BAR 5
+#define DSP_MBOX1_BAR 6
+#define DSP_MBOX2_BAR 7
+
+#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0
+
+#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/
+#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/
+
+#define TOTAL_SIZE_SHARED_DRAM_FROM_TAIL \
+ (SIZE_SHARED_DRAM_DL + SIZE_SHARED_DRAM_UL)
+
+#define SRAM_PHYS_BASE_FROM_DSP_VIEW 0x40000000 /* MT8195 DSP view */
+#define DRAM_PHYS_BASE_FROM_DSP_VIEW 0x60000000 /* MT8195 DSP view */
+
+/*remap dram between AP and DSP view, 4KB aligned*/
+#define DRAM_REMAP_SHIFT 12
+#define DRAM_REMAP_MASK (BIT(DRAM_REMAP_SHIFT) - 1)
+
+void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr);
+void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev);
+#endif
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index 160b88a2d59f..235e2ef72178 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -142,25 +142,46 @@ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar,
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced);
-void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset)
+/**
+ * snd_sof_dsp_panic - handle a received DSP panic message
+ * @sdev: Pointer to the device's sdev
+ * @offset: offset of panic information
+ * @non_recoverable: the panic is fatal, no recovery will be done by the caller
+ */
+void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable)
{
- dev_err(sdev->dev, "error : DSP panic!\n");
-
/*
- * check if DSP is not ready and did not set the dsp_oops_offset.
- * if the dsp_oops_offset is not set, set it from the panic message.
- * Also add a check to memory window setting with panic message.
+ * if DSP is not ready and the dsp_oops_offset is not yet set, use the
+ * offset from the panic message.
*/
if (!sdev->dsp_oops_offset)
sdev->dsp_oops_offset = offset;
- else
- 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;
+ /*
+ * Print warning if the offset from the panic message differs from
+ * dsp_oops_offset
+ */
+ if (sdev->dsp_oops_offset != offset)
+ dev_warn(sdev->dev,
+ "%s: dsp_oops_offset %zu differs from panic offset %u\n",
+ __func__, sdev->dsp_oops_offset, offset);
- snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
- snd_sof_trace_notify_for_error(sdev);
+ /*
+ * Set the fw_state to crashed only in case of non recoverable DSP panic
+ * event.
+ * Use different message within the snd_sof_dsp_dbg_dump() depending on
+ * the non_recoverable flag.
+ */
+ sdev->dbg_dump_printed = false;
+ if (non_recoverable) {
+ snd_sof_dsp_dbg_dump(sdev, "DSP panic!",
+ SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
+ sof_set_fw_state(sdev, SOF_FW_CRASHED);
+ snd_sof_trace_notify_for_error(sdev);
+ } else {
+ snd_sof_dsp_dbg_dump(sdev,
+ "DSP panic (recovery will be attempted)",
+ SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
+ }
}
EXPORT_SYMBOL(snd_sof_dsp_panic);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 09bf38fdfb8a..ffe7456e7713 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -72,35 +72,68 @@ static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev)
return 0;
}
-/* dsp core power up/power down */
-static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev,
- unsigned int core_mask)
+/* dsp core get/put */
+static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
- int ret = 0;
+ if (core > sdev->num_cores - 1) {
+ dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core,
+ sdev->num_cores);
+ return -EINVAL;
+ }
+
+ if (sof_ops(sdev)->core_get) {
+ int ret;
+
+ /* if current ref_count is > 0, increment it and return */
+ if (sdev->dsp_core_ref_count[core] > 0) {
+ sdev->dsp_core_ref_count[core]++;
+ return 0;
+ }
+
+ /* power up the core */
+ ret = sof_ops(sdev)->core_get(sdev, core);
+ if (ret < 0)
+ return ret;
+
+ /* increment ref_count */
+ sdev->dsp_core_ref_count[core]++;
- core_mask &= ~sdev->enabled_cores_mask;
- if (sof_ops(sdev)->core_power_up && core_mask) {
- ret = sof_ops(sdev)->core_power_up(sdev, core_mask);
- if (!ret)
- sdev->enabled_cores_mask |= core_mask;
+ /* and update enabled_cores_mask */
+ sdev->enabled_cores_mask |= BIT(core);
+
+ dev_dbg(sdev->dev, "Core %d powered up\n", core);
}
- return ret;
+ return 0;
}
-static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev,
- unsigned int core_mask)
+static inline int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
- int ret = 0;
+ if (core > sdev->num_cores - 1) {
+ dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core,
+ sdev->num_cores);
+ return -EINVAL;
+ }
+
+ if (sof_ops(sdev)->core_put) {
+ int ret;
- core_mask &= sdev->enabled_cores_mask;
- if (sof_ops(sdev)->core_power_down && core_mask) {
- ret = sof_ops(sdev)->core_power_down(sdev, core_mask);
- if (!ret)
- sdev->enabled_cores_mask &= ~core_mask;
+ /* decrement ref_count and return if it is > 0 */
+ if (--(sdev->dsp_core_ref_count[core]) > 0)
+ return 0;
+
+ /* power down the core */
+ ret = sof_ops(sdev)->core_put(sdev, core);
+ if (ret < 0)
+ return ret;
+
+ /* and update enabled_cores_mask */
+ sdev->enabled_cores_mask &= ~BIT(core);
+
+ dev_dbg(sdev->dev, "Core %d powered down\n", core);
}
- return ret;
+ return 0;
}
/* pre/post fw load */
@@ -241,7 +274,7 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev,
}
/* debug */
-void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags);
+void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags);
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,
@@ -454,6 +487,16 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev,
return 0;
}
+/* pcm ack */
+static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack)
+ return sof_ops(sdev)->pcm_ack(sdev, substream);
+
+ return 0;
+}
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
static inline int
snd_sof_probe_compr_assign(struct snd_sof_dev *sdev,
@@ -514,15 +557,17 @@ snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
sof_ops(sdev)->machine_unregister(sdev, pdata);
}
-static inline void
+static inline struct snd_soc_acpi_mach *
snd_sof_machine_select(struct snd_sof_dev *sdev)
{
if (sof_ops(sdev) && sof_ops(sdev)->machine_select)
- sof_ops(sdev)->machine_select(sdev);
+ return sof_ops(sdev)->machine_select(sdev);
+
+ return NULL;
}
static inline void
-snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach,
+snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev)
{
if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params)
@@ -598,5 +643,5 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset,
u32 mask, u32 target, u32 timeout_ms,
u32 interval_us);
-void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset);
+void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable);
#endif
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index fa0bfcd2474e..37fb8e6cd493 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -100,14 +100,16 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
-static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
- struct snd_sof_dev *sdev,
- struct snd_sof_pcm *spcm)
+int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
+ struct snd_sof_pcm *spcm)
{
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
int ret;
+ if (!spcm->prepared[substream->stream])
+ return 0;
+
stream.hdr.size = sizeof(stream);
stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
stream.comp_id = spcm->stream[substream->stream].comp_id;
@@ -179,11 +181,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
*/
- if (spcm->prepared[substream->stream]) {
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
- if (ret < 0)
- return ret;
- }
+ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
+ if (ret < 0)
+ return ret;
dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
@@ -299,24 +299,26 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
- if (spcm->prepared[substream->stream]) {
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
- if (ret < 0)
- err = ret;
- }
-
- ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ /* free PCM in the DSP */
+ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
if (ret < 0)
err = ret;
- cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
+ /* stop DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0) {
dev_err(component->dev, "error: platform hw free failed\n");
err = ret;
}
+ /* free the DAPM widget list */
+ ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ if (ret < 0)
+ err = ret;
+
+ cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
+
return err;
}
@@ -393,26 +395,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
break;
- case SNDRV_PCM_TRIGGER_RESUME:
- if (spcm->stream[substream->stream].suspend_ignored) {
- /*
- * this case will be triggered when INFO_RESUME is
- * supported, no need to resume streams that remained
- * enabled in D0ix.
- */
- spcm->stream[substream->stream].suspend_ignored = false;
- return 0;
- }
-
- /* set up hw_params */
- ret = sof_pcm_prepare(component, substream);
- if (ret < 0) {
- dev_err(component->dev,
- "error: failed to set up hw_params upon resume\n");
- return ret;
- }
-
- fallthrough;
case SNDRV_PCM_TRIGGER_START:
if (spcm->stream[substream->stream].suspend_ignored) {
/*
@@ -467,13 +449,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
if (!ret && reset_hw_params) {
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
+ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
+ free_widget_list);
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;
@@ -814,6 +793,18 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
"channels_min: %d channels_max: %d\n",
channels->min, channels->max);
break;
+ case SOF_DAI_MEDIATEK_AFE:
+ rate->min = dai->dai_config->afe.rate;
+ rate->max = dai->dai_config->afe.rate;
+ channels->min = dai->dai_config->afe.channels;
+ channels->max = dai->dai_config->afe.channels;
+
+ dev_dbg(component->dev,
+ "rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev,
+ "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
case SOF_DAI_IMX_SAI:
rate->min = dai->dai_config->sai.fsync_rate;
rate->max = dai->dai_config->sai.fsync_rate;
@@ -826,6 +817,42 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
"channels_min: %d channels_max: %d\n",
channels->min, channels->max);
break;
+ case SOF_DAI_AMD_BT:
+ rate->min = dai->dai_config->acpbt.fsync_rate;
+ rate->max = dai->dai_config->acpbt.fsync_rate;
+ channels->min = dai->dai_config->acpbt.tdm_slots;
+ channels->max = dai->dai_config->acpbt.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev,
+ "AMD_BT channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_AMD_SP:
+ rate->min = dai->dai_config->acpsp.fsync_rate;
+ rate->max = dai->dai_config->acpsp.fsync_rate;
+ channels->min = dai->dai_config->acpsp.tdm_slots;
+ channels->max = dai->dai_config->acpsp.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev,
+ "AMD_SP channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_AMD_DMIC:
+ rate->min = dai->dai_config->acpdmic.fsync_rate;
+ rate->max = dai->dai_config->acpdmic.fsync_rate;
+ channels->min = dai->dai_config->acpdmic.tdm_slots;
+ channels->max = dai->dai_config->acpdmic.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev,
+ "AMD_DMIC channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
default:
dev_err(component->dev, "error: invalid DAI type %d\n",
dai->dai_config->type);
@@ -869,6 +896,14 @@ static void sof_pcm_remove(struct snd_soc_component *component)
snd_soc_tplg_component_remove(component);
}
+static int sof_pcm_ack(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+
+ return snd_sof_pcm_platform_ack(sdev, substream);
+}
+
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
{
struct snd_soc_component_driver *pd = &sdev->plat_drv;
@@ -887,6 +922,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->hw_free = sof_pcm_hw_free;
pd->trigger = sof_pcm_trigger;
pd->pointer = sof_pcm_pointer;
+ pd->ack = sof_pcm_ack;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
pd->compress_ops = &sof_probe_compressed_ops;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index ac8ae6e422a7..197a88695fef 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -130,6 +130,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
dev_err(sdev->dev,
"error: failed to load DSP firmware after resume %d\n",
ret);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -144,6 +145,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
dev_err(sdev->dev,
"error: failed to boot DSP firmware after resume %d\n",
ret);
+ sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -312,6 +314,14 @@ int snd_sof_prepare(struct device *dev)
/* will suspend to S3 by default */
sdev->system_suspend_target = SOF_SUSPEND_S3;
+ /*
+ * if the firmware is crashed or boot failed then we try to aim for S3
+ * to reboot the firmware
+ */
+ if (sdev->fw_state == SOF_FW_CRASHED ||
+ sdev->fw_state == SOF_FW_BOOT_FAILED)
+ return 0;
+
if (!desc->use_acpi_target_states)
return 0;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 7cbe757c1fe2..9e76b796502f 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -14,29 +14,12 @@
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);
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
if (ret < 0)
dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n",
scontrol->comp_id);
@@ -57,7 +40,7 @@ static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *da
}
/* set NONE flag to clear all previous settings */
- config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE);
+ config->flags = SOF_DAI_CONFIG_FLAGS_NONE;
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
@@ -76,12 +59,26 @@ static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_wi
/* set up all controls for the widget */
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
+ /* set kcontrol data in DSP */
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;
}
+
+ /*
+ * Read back the data from the DSP for static widgets. This is particularly
+ * useful for binary kcontrols associated with static pipeline widgets to
+ * initialize the data size to match that in the DSP.
+ */
+ if (swidget->dynamic_pipeline_widget)
+ continue;
+
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+ if (ret < 0)
+ dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n",
+ swidget->widget->name);
}
return 0;
@@ -106,7 +103,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
.id = swidget->comp_id,
};
struct sof_ipc_reply reply;
- int ret;
+ int ret, ret1, core;
if (!swidget->private)
return 0;
@@ -115,32 +112,59 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
+ core = swidget->core;
+
switch (swidget->id) {
case snd_soc_dapm_scheduler:
+ {
+ const struct sof_ipc_pipe_new *pipeline = swidget->private;
+
+ core = pipeline->core;
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;
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_dai_out:
+ {
+ struct snd_sof_dai *dai = swidget->private;
+
+ dai->configured = false;
+ fallthrough;
+ }
default:
ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
break;
}
+ /* continue to disable core even if IPC fails */
ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
&reply, sizeof(reply));
- if (ret < 0) {
+ if (ret < 0)
dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
- swidget->use_count++;
- return ret;
+
+ /*
+ * disable widget core. continue to route setup status and complete flag
+ * even if this fails and return the appropriate error
+ */
+ ret1 = snd_sof_dsp_core_put(sdev, core);
+ if (ret1 < 0) {
+ dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
+ core, swidget->widget->name);
+ if (!ret)
+ ret = ret1;
}
/* 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;
+ if (!ret)
+ dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
+
+ return ret;
}
EXPORT_SYMBOL(sof_widget_free);
@@ -153,6 +177,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
struct snd_sof_dai *dai;
size_t ipc_size;
int ret;
+ int core;
/* skip if there is no private data */
if (!swidget->private)
@@ -162,10 +187,18 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (++swidget->use_count > 1)
return 0;
- ret = sof_pipeline_core_enable(sdev, swidget);
+ /* set core ID */
+ core = swidget->core;
+ if (swidget->id == snd_soc_dapm_scheduler) {
+ pipeline = swidget->private;
+ core = pipeline->core;
+ }
+
+ /* enable widget core */
+ ret = snd_sof_dsp_core_get(sdev, core);
if (ret < 0) {
- dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
- ret, swidget->widget->name);
+ dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
+ swidget->widget->name);
goto use_count_dec;
}
@@ -174,8 +207,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
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;
+ if (!comp) {
+ ret = -ENOMEM;
+ goto core_put;
+ }
dai = swidget->private;
dai->configured = false;
@@ -190,20 +225,26 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n",
swidget->widget->name);
- goto use_count_dec;
+ goto core_put;
}
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);
+
+ /*
+ * widget use_count and core ref_count will both be decremented by
+ * sof_widget_free()
+ */
sof_widget_free(sdev, swidget);
return ret;
}
break;
case snd_soc_dapm_scheduler:
pipeline = swidget->private;
- ret = sof_load_pipeline_ipc(sdev, pipeline, &r);
+ ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
+ sizeof(*pipeline), &r, sizeof(r));
break;
default:
hdr = swidget->private;
@@ -213,7 +254,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
- goto use_count_dec;
+ goto core_put;
}
/* restore kcontrols for widget */
@@ -221,6 +262,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
swidget->widget->name);
+ /*
+ * widget use_count and core ref_count will both be decremented by
+ * sof_widget_free()
+ */
sof_widget_free(sdev, swidget);
return ret;
}
@@ -229,6 +274,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
return 0;
+core_put:
+ snd_sof_dsp_core_put(sdev, core);
use_count_dec:
swidget->use_count--;
return ret;
@@ -595,16 +642,25 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
int ret;
/* restore pipeline components */
- list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
/* only set up the widgets belonging to static pipelines */
if (!verify && swidget->dynamic_pipeline_widget)
continue;
+ /*
+ * For older firmware, skip scheduler widgets in this loop,
+ * sof_widget_setup() will be called in the 'complete pipeline' loop
+ */
+ if (v->abi_version < SOF_ABI_VER(3, 19, 0) &&
+ swidget->id == snd_soc_dapm_scheduler)
+ continue;
+
/* 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;
@@ -652,6 +708,12 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
if (!verify && swidget->dynamic_pipeline_widget)
continue;
+ if (v->abi_version < SOF_ABI_VER(3, 19, 0)) {
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
swidget->complete =
snd_sof_complete_pipeline(sdev, swidget);
break;
@@ -663,12 +725,81 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
return 0;
}
+int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
+{
+ int ret;
+
+ /* Send PCM_FREE IPC to reset pipeline */
+ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
+ if (ret < 0)
+ return ret;
+
+ /* stop the DMA */
+ ret = snd_sof_pcm_platform_hw_free(sdev, substream);
+ if (ret < 0)
+ return ret;
+
+ /* free widget list */
+ if (free_widget_list) {
+ ret = sof_widget_list_free(sdev, spcm, dir);
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to free widgets during suspend\n");
+ }
+
+ return ret;
+}
+
+/*
+ * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that
+ * did not get suspended(ex: paused streams) so the widgets can be set up again during resume.
+ */
+static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_widget *swidget;
+ struct snd_sof_pcm *spcm;
+ int dir, ret;
+
+ /*
+ * free all PCMs and their associated DAPM widgets if their connected DAPM widget
+ * list is not NULL. This should only be true for paused streams at this point.
+ * This is equivalent to the handling of FE DAI suspend trigger for running streams.
+ */
+ list_for_each_entry(spcm, &sdev->pcm_list, list)
+ for_each_pcm_streams(dir) {
+ struct snd_pcm_substream *substream = spcm->stream[dir].substream;
+
+ if (!substream || !substream->runtime)
+ continue;
+
+ if (spcm->stream[dir].list) {
+ ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ /*
+ * free any left over DAI widgets. This is equivalent to the handling of suspend trigger
+ * for the BE DAI for running streams.
+ */
+ list_for_each_entry(swidget, &sdev->widget_list, list)
+ if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) {
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
/*
- * This function doesn't free widgets during suspend. It only resets the set up status for all
- * routes and use_count for all widgets.
+ * For older firmware, this function doesn't free widgets for static pipelines during suspend.
+ * It only resets use_count for all widgets.
*/
int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
int ret;
@@ -676,12 +807,18 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
/*
* 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.
+ * sroute->setup because during suspend all running 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) {
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->dynamic_pipeline_widget)
+ continue;
+
+ /* Do not free widgets for static pipelines with FW ABI older than 3.19 */
+ if (!verify && !swidget->dynamic_pipeline_widget &&
+ v->abi_version < SOF_ABI_VER(3, 19, 0)) {
swidget->use_count = 0;
+ swidget->complete = 0;
continue;
}
@@ -690,6 +827,19 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
return ret;
}
+ /*
+ * Tear down all pipelines associated with PCMs that did not get suspended
+ * and unset the prepare flag so that they can be set up again during resume.
+ * Skip this step for older firmware.
+ */
+ if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) {
+ ret = sof_tear_down_left_over_pipelines(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to tear down paused pipelines\n");
+ return ret;
+ }
+ }
+
list_for_each_entry(sroute, &sdev->route_list, list)
sroute->setup = false;
@@ -877,9 +1027,10 @@ int sof_machine_check(struct snd_sof_dev *sdev)
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
/* find machine */
- snd_sof_machine_select(sdev);
- if (sof_pdata->machine) {
- snd_sof_set_mach_params(sof_pdata->machine, sdev);
+ mach = snd_sof_machine_select(sdev);
+ if (mach) {
+ sof_pdata->machine = mach;
+ snd_sof_set_mach_params(mach, sdev);
return 0;
}
@@ -901,7 +1052,7 @@ int sof_machine_check(struct snd_sof_dev *sdev)
sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
sof_pdata->machine = mach;
- snd_sof_set_mach_params(sof_pdata->machine, sdev);
+ snd_sof_set_mach_params(mach, sdev);
return 0;
}
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 05e98e231b85..f3009e6b91a1 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -74,7 +74,6 @@ struct snd_sof_control {
u32 readback_offset; /* offset to mmapped data if used */
struct sof_ipc_ctrl_data *control_data;
u32 size; /* cdata size */
- enum sof_ipc_ctrl_cmd cmd;
u32 *volume_table; /* volume table computed from tlv data*/
struct list_head list; /* list in sdev control list */
@@ -185,12 +184,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file);
int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget);
-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,
- const struct snd_sof_widget *swidget);
-
/*
* Stream IPC
*/
@@ -245,11 +238,7 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { }
/*
* Mixer IPC
*/
-int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
- u32 ipc_cmd,
- enum sof_ipc_ctrl_type ctrl_type,
- enum sof_ipc_ctrl_cmd ctrl_cmd,
- bool send);
+int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set);
/* DAI link fixup */
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
@@ -271,4 +260,8 @@ 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);
+int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
+ struct snd_sof_pcm *spcm);
+int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+ struct snd_sof_pcm *spcm, int dir, bool free_widget_list);
#endif
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index 885430a42226..e3718638f9ce 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -11,8 +11,8 @@
#include <linux/pm_runtime.h>
#include <sound/sof.h>
+#include "sof-of-dev.h"
#include "ops.h"
-#include "imx/imx-ops.h"
static char *fw_path;
module_param(fw_path, charp, 0444);
@@ -22,56 +22,26 @@ 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)
-static struct sof_dev_desc sof_of_imx8qxp_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8x.ri",
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8x_ops,
-};
-
-static struct sof_dev_desc sof_of_imx8qm_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8.ri",
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8_ops,
-};
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M)
-static struct sof_dev_desc sof_of_imx8mp_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8m.ri",
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8m_ops,
-};
-#endif
-
-static const struct dev_pm_ops sof_of_pm = {
+const struct dev_pm_ops sof_of_pm = {
.prepare = snd_sof_prepare,
.complete = snd_sof_complete,
SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
NULL)
};
+EXPORT_SYMBOL(sof_of_pm);
static void sof_of_probe_complete(struct device *dev)
{
/* allow runtime_pm */
pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
}
-static int sof_of_probe(struct platform_device *pdev)
+int sof_of_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct sof_dev_desc *desc;
@@ -112,8 +82,9 @@ static int sof_of_probe(struct platform_device *pdev)
/* call sof helper for DSP hardware probe */
return snd_sof_device_probe(dev, sof_pdata);
}
+EXPORT_SYMBOL(sof_of_probe);
-static int sof_of_remove(struct platform_device *pdev)
+int sof_of_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
@@ -122,29 +93,6 @@ static int sof_of_remove(struct platform_device *pdev)
return 0;
}
-
-static const struct of_device_id sof_of_ids[] = {
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8)
- { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
- { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
-#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M)
- { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc},
-#endif
- { }
-};
-MODULE_DEVICE_TABLE(of, sof_of_ids);
-
-/* DT driver definition */
-static struct platform_driver snd_sof_of_driver = {
- .probe = sof_of_probe,
- .remove = sof_of_remove,
- .driver = {
- .name = "sof-audio-of",
- .pm = &sof_of_pm,
- .of_match_table = sof_of_ids,
- },
-};
-module_platform_driver(snd_sof_of_driver);
+EXPORT_SYMBOL(sof_of_remove);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h
new file mode 100644
index 000000000000..4e0f6588dad9
--- /dev/null
+++ b/sound/soc/sof/sof-of-dev.h
@@ -0,0 +1,17 @@
+/* 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 2021 NXP
+ */
+
+#ifndef __SOUND_SOC_SOF_OF_H
+#define __SOUND_SOC_SOF_OF_H
+
+extern const struct dev_pm_ops sof_of_pm;
+
+int sof_of_probe(struct platform_device *pdev);
+int sof_of_remove(struct platform_device *pdev);
+
+#endif
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index bc9e70765678..20c6ca37dbc4 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -59,22 +59,23 @@ static const struct dmi_system_id sof_tplg_table[] = {
},
.driver_data = "sof-adl-rt5682-ssp0-max98373-ssp2.tplg",
},
+ {
+ .callback = sof_tplg_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"),
+ },
+ .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg",
+ },
+
{}
};
static const struct dmi_system_id community_key_platforms[] = {
{
- .ident = "Up Squared",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
- DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
- }
- },
- {
- .ident = "Up Extreme",
+ .ident = "Up boards",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
- DMI_MATCH(DMI_BOARD_NAME, "UP-WHL01"),
}
},
{
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index ba341b1bda0c..087935192ce8 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -20,7 +20,7 @@
#include <uapi/sound/sof/fw.h>
#include <sound/sof/ext_manifest.h>
-/* debug flags */
+/* Flag definitions used in sof_core_debug (sof_debug module parameter) */
#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 */
@@ -35,14 +35,16 @@
*/
#define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */
+/* Flag definitions used for controlling the DSP dump behavior */
#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_OPTIONAL BIT(4) /* only dump if SOF_DBG_PRINT_ALL_DUMPS is set */
+/* Output this dump (at the DEBUG level) only when SOF_DBG_PRINT_ALL_DUMPS is set */
+#define SOF_DBG_DUMP_OPTIONAL BIT(4)
/* global debug state set by SOF_DBG_ flags */
-extern int sof_core_debug;
+bool sof_debug_check_flag(int mask);
/* max BARs mmaped devices can use */
#define SND_SOF_BARS 8
@@ -71,6 +73,9 @@ extern int sof_core_debug;
/* So far the primary core on all DSPs has ID 0 */
#define SOF_DSP_PRIMARY_CORE 0
+/* max number of DSP cores */
+#define SOF_MAX_DSP_NUM_CORES 8
+
/* DSP power state */
enum sof_dsp_power_states {
SOF_DSP_PM_D0,
@@ -127,10 +132,8 @@ struct snd_sof_dsp_ops {
int (*run)(struct snd_sof_dev *sof_dev); /* mandatory */
int (*stall)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */
int (*reset)(struct snd_sof_dev *sof_dev); /* optional */
- int (*core_power_up)(struct snd_sof_dev *sof_dev,
- unsigned int core_mask); /* optional */
- int (*core_power_down)(struct snd_sof_dev *sof_dev,
- unsigned int core_mask); /* optional */
+ int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */
+ int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */
/*
* Register IO: only used by respective drivers themselves,
@@ -206,6 +209,9 @@ struct snd_sof_dsp_ops {
snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream); /* optional */
+ /* pcm ack */
+ int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
/* Except for probe_pointer, all probe ops are mandatory */
int (*probe_assign)(struct snd_sof_dev *sdev,
@@ -289,8 +295,8 @@ struct snd_sof_dsp_ops {
void *pdata); /* optional */
void (*machine_unregister)(struct snd_sof_dev *sdev,
void *pdata); /* optional */
- void (*machine_select)(struct snd_sof_dev *sdev); /* optional */
- void (*set_mach_params)(const struct snd_soc_acpi_mach *mach,
+ struct snd_soc_acpi_mach * (*machine_select)(struct snd_sof_dev *sdev); /* optional */
+ void (*set_mach_params)(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev); /* optional */
/* DAI ops */
@@ -305,8 +311,8 @@ struct snd_sof_dsp_ops {
/* DSP architecture specific callbacks for oops and stack dumps */
struct dsp_arch_ops {
- void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops);
- void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops,
+ void (*dsp_oops)(struct snd_sof_dev *sdev, const char *level, void *oops);
+ void (*dsp_stack)(struct snd_sof_dev *sdev, const char *level, void *oops,
u32 *stack, u32 stack_words);
};
@@ -326,6 +332,10 @@ struct snd_sof_dfsentry {
#if ENABLE_DEBUGFS_CACHEBUF
char *cache_buf; /* buffer to cache the contents of debugfs memory */
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+ void *msg_inject_tx;
+ void *msg_inject_rx;
+#endif
struct snd_sof_dev *sdev;
struct list_head list; /* list in sdev dfsentry list */
union {
@@ -367,15 +377,6 @@ struct snd_sof_ipc_msg {
bool ipc_complete;
};
-enum snd_sof_fw_state {
- SOF_FW_BOOT_NOT_STARTED = 0,
- SOF_FW_BOOT_PREPARE,
- SOF_FW_BOOT_IN_PROGRESS,
- SOF_FW_BOOT_FAILED,
- SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */
- SOF_FW_BOOT_COMPLETE,
-};
-
/*
* SOF Device Level.
*/
@@ -400,7 +401,7 @@ struct snd_sof_dev {
/* DSP firmware boot */
wait_queue_head_t boot_wait;
- enum snd_sof_fw_state fw_state;
+ enum sof_fw_state fw_state;
bool first_boot;
/* work queue in case the probe is implemented in two steps */
@@ -473,6 +474,18 @@ struct snd_sof_dev {
bool msi_enabled;
+ /* DSP core context */
+ u32 num_cores;
+
+ /*
+ * ref count per core that will be modified during system suspend/resume and during pcm
+ * hw_params/hw_free. This doesn't need to be protected with a mutex because pcm
+ * hw_params/hw_free are already protected by the PCM mutex in the ALSA framework in
+ * sound/core/ when streams are active and during system suspend/resume, streams are
+ * already suspended.
+ */
+ int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES];
+
void *private; /* core does not touch this */
};
@@ -515,6 +528,7 @@ void snd_sof_fw_unload(struct snd_sof_dev *sdev);
*/
struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev);
void snd_sof_ipc_free(struct snd_sof_dev *sdev);
+void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev);
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,
@@ -527,6 +541,11 @@ 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);
+static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id)
+{
+ snd_sof_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, msg_id);
+}
/*
* Trace/debug
@@ -542,10 +561,10 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
struct sof_ipc_dma_trace_posn *posn);
void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev);
-void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
- u32 tracep_code, void *oops,
- struct sof_ipc_panic_info *panic_info,
- void *stack, size_t stack_words);
+void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
+ u32 panic_code, u32 tracep_code, void *oops,
+ struct sof_ipc_panic_info *panic_info,
+ void *stack, size_t stack_words);
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);
@@ -556,16 +575,17 @@ int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
/*
* DSP Architectures.
*/
-static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
- u32 stack_words)
+static inline void sof_stack(struct snd_sof_dev *sdev, const char *level,
+ void *oops, u32 *stack, u32 stack_words)
{
- sof_dsp_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words);
+ sof_dsp_arch_ops(sdev)->dsp_stack(sdev, level, oops, stack,
+ stack_words);
}
-static inline void sof_oops(struct snd_sof_dev *sdev, void *oops)
+static inline void sof_oops(struct snd_sof_dev *sdev, const char *level, void *oops)
{
if (sof_dsp_arch_ops(sdev)->dsp_oops)
- sof_dsp_arch_ops(sdev)->dsp_oops(sdev, oops);
+ sof_dsp_arch_ops(sdev)->dsp_oops(sdev, level, oops);
}
extern const struct dsp_arch_ops sof_xtensa_arch_ops;
@@ -574,7 +594,7 @@ 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)
+ enum sof_fw_state new_state)
{
if (sdev->fw_state == new_state)
return;
diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c
index 5586af9f1a25..c79026cdb8c7 100644
--- a/sound/soc/sof/sof-probes.c
+++ b/sound/soc/sof/sof-probes.c
@@ -321,7 +321,7 @@ static int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai);
}
-struct snd_soc_cdai_ops sof_probe_compr_ops = {
+const 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,
diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h
index 35e1dd8d9e03..4a1ed2942d28 100644
--- a/sound/soc/sof/sof-probes.h
+++ b/sound/soc/sof/sof-probes.h
@@ -32,7 +32,7 @@ int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
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_soc_cdai_ops sof_probe_compr_ops;
extern const struct snd_compress_ops sof_probe_compressed_ops;
#endif
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index bb9e62bbe5db..e72dcae5e7ee 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -376,6 +376,10 @@ static const struct sof_dai_types sof_dais[] = {
{"ALH", SOF_DAI_INTEL_ALH},
{"SAI", SOF_DAI_IMX_SAI},
{"ESAI", SOF_DAI_IMX_ESAI},
+ {"ACP", SOF_DAI_AMD_BT},
+ {"ACPSP", SOF_DAI_AMD_SP},
+ {"ACPDMIC", SOF_DAI_AMD_DMIC},
+ {"AFE", SOF_DAI_MEDIATEK_AFE},
};
static enum sof_ipc_dai_type find_dai(const char *name)
@@ -803,6 +807,19 @@ static const struct sof_topology_token led_tokens[] = {
get_token_u32, offsetof(struct snd_sof_led_control, direction), 0},
};
+/* AFE */
+static const struct sof_topology_token afe_tokens[] = {
+ {SOF_TKN_MEDIATEK_AFE_RATE,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0},
+ {SOF_TKN_MEDIATEK_AFE_CH,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0},
+ {SOF_TKN_MEDIATEK_AFE_FORMAT,
+ SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0},
+};
+
static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
void *object,
const struct sof_topology_token *tokens,
@@ -1073,11 +1090,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
/* set cmd for mixer control */
if (le32_to_cpu(mc->max) == 1) {
- scontrol->cmd = SOF_CTRL_CMD_SWITCH;
+ scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH;
goto skip;
}
- scontrol->cmd = SOF_CTRL_CMD_VOLUME;
+ scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME;
/* extract tlv data */
if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) {
@@ -1148,7 +1165,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp,
scontrol->comp_id = sdev->next_comp_id;
scontrol->num_channels = le32_to_cpu(ec->num_channels);
scontrol->control_data->index = kc->index;
- scontrol->cmd = SOF_CTRL_CMD_ENUM;
+ scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM;
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
@@ -1194,7 +1211,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
}
scontrol->comp_id = sdev->next_comp_id;
- scontrol->cmd = SOF_CTRL_CMD_BINARY;
+ scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY;
scontrol->control_data->index = kc->index;
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
@@ -1329,69 +1346,6 @@ static int sof_control_unload(struct snd_soc_component *scomp,
* DAI Topology
*/
-/* Static DSP core power management so far, should be extended in the future */
-static int sof_core_enable(struct snd_sof_dev *sdev, int core)
-{
- struct sof_ipc_pm_core_config pm_core_config = {
- .hdr = {
- .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
- .size = sizeof(pm_core_config),
- },
- .enable_mask = sdev->enabled_cores_mask | BIT(core),
- };
- int ret;
-
- if (sdev->enabled_cores_mask & BIT(core))
- return 0;
-
- /* power up the core if it is host managed */
- ret = snd_sof_dsp_core_power_up(sdev, BIT(core));
- if (ret < 0) {
- dev_err(sdev->dev, "error: %d powering up core %d\n",
- ret, core);
- return ret;
- }
-
- /* Now notify DSP */
- ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
- &pm_core_config, sizeof(pm_core_config),
- &pm_core_config, sizeof(pm_core_config));
- if (ret < 0) {
- dev_err(sdev->dev, "error: core %d enable ipc failure %d\n",
- core, ret);
- goto err;
- }
- return ret;
-err:
- /* power down core if it is host managed and return the original error if this fails too */
- if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0)
- dev_err(sdev->dev, "error: powering down core %d\n", core);
-
- return ret;
-}
-
-int sof_pipeline_core_enable(struct snd_sof_dev *sdev,
- const struct snd_sof_widget *swidget)
-{
- const struct sof_ipc_pipe_new *pipeline;
- int ret;
-
- if (swidget->id == snd_soc_dapm_scheduler) {
- pipeline = swidget->private;
- } else {
- pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id);
- if (!pipeline)
- return -ENOENT;
- }
-
- /* First enable the pipeline core */
- ret = sof_core_enable(sdev, pipeline->core);
- if (ret < 0)
- return ret;
-
- return sof_core_enable(sdev, swidget->core);
-}
-
static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_dapm_widget *w,
struct snd_soc_tplg_dapm_widget *tw,
@@ -1690,23 +1644,6 @@ err:
/*
* Pipeline Topology
*/
-int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
- struct sof_ipc_pipe_new *pipeline,
- struct sof_ipc_comp_reply *r)
-{
- int ret = sof_core_enable(sdev, pipeline->core);
-
- if (ret < 0)
- return ret;
-
- ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
- sizeof(*pipeline), r, sizeof(*r));
- if (ret < 0)
- 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)
@@ -1758,12 +1695,12 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
goto err;
}
- if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE)
+ if (sof_debug_check_flag(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;
+ if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))
+ swidget->dynamic_pipeline_widget =
+ sof_debug_check_flag(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,
@@ -2139,7 +2076,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
*size += wdata[i].pdata->size;
/* get data type */
- switch (wdata[i].control->cmd) {
+ switch (wdata[i].control->control_data->cmd) {
case SOF_CTRL_CMD_VOLUME:
case SOF_CTRL_CMD_ENUM:
case SOF_CTRL_CMD_SWITCH:
@@ -2358,7 +2295,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
return ret;
}
- if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE)
+ if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
comp.core = SOF_DSP_PRIMARY_CORE;
swidget->core = comp.core;
@@ -2485,10 +2422,8 @@ static int sof_route_unload(struct snd_soc_component *scomp,
static int sof_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct snd_kcontrol_new *kc;
struct snd_soc_dapm_widget *widget;
- struct sof_ipc_pipe_new *pipeline;
struct snd_sof_control *scontrol;
struct snd_sof_widget *swidget;
struct soc_mixer_control *sm;
@@ -2515,24 +2450,6 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
list_del(&dai->list);
}
break;
- case snd_soc_dapm_scheduler:
-
- /* 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",
- pipeline->core);
- break;
default:
break;
}
@@ -2992,6 +2909,144 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
return ret;
}
+static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg,
+ struct snd_soc_tplg_hw_config *hw_config,
+ struct sof_ipc_dai_config *config)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params));
+ config->hdr.size = size;
+
+ config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpdmic.tdm_slots,
+ config->acpdmic.fsync_rate);
+
+ /* set config for all DAI's with name matching the link name */
+ ret = sof_set_dai_config(sdev, size, link, config);
+ if (ret < 0)
+ dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n",
+ config->dai_index);
+ return ret;
+}
+
+static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg,
+ struct snd_soc_tplg_hw_config *hw_config,
+ struct sof_ipc_dai_config *config)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params));
+ config->hdr.size = size;
+
+ config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpbt.tdm_slots,
+ config->acpbt.fsync_rate);
+
+ /* set config for all DAI's with name matching the link name */
+ ret = sof_set_dai_config(sdev, size, link, config);
+ if (ret < 0)
+ dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n",
+ config->dai_index);
+ return ret;
+}
+
+static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg,
+ struct snd_soc_tplg_hw_config *hw_config,
+ struct sof_ipc_dai_config *config)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params));
+ config->hdr.size = size;
+
+ config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpsp.tdm_slots,
+ config->acpsp.fsync_rate);
+
+ /* set config for all DAI's with name matching the link name */
+ ret = sof_set_dai_config(sdev, size, link, config);
+ if (ret < 0)
+ dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n",
+ config->dai_index);
+ return ret;
+}
+
+static int sof_link_afe_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg,
+ struct snd_soc_tplg_hw_config *hw_config,
+ struct sof_ipc_dai_config *config)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_soc_tplg_private *private = &cfg->priv;
+ struct snd_soc_dai *dai;
+ u32 size = sizeof(*config);
+ int ret;
+
+ config->hdr.size = size;
+
+ /* get any bespoke DAI tokens */
+ ret = sof_parse_tokens(scomp, &config->afe, afe_tokens,
+ ARRAY_SIZE(afe_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(scomp->dev, "parse afe tokens failed %d\n",
+ le32_to_cpu(private->size));
+ return ret;
+ }
+
+ dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n",
+ config->afe.rate, config->afe.channels, config->afe.format);
+
+ dai = snd_soc_find_dai(link->cpus);
+ if (!dai) {
+ dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name);
+ return -EINVAL;
+ }
+
+ config->afe.stream_id = DMA_CHAN_INVALID;
+
+ ret = sof_set_dai_config(sdev, size, link, config);
+ if (ret < 0)
+ dev_err(scomp->dev, "failed to process afe dai link %s", link->name);
+
+ return ret;
+}
+
static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dai_link *link,
struct snd_soc_tplg_link_config *cfg,
@@ -3277,6 +3332,19 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
case SOF_DAI_IMX_ESAI:
ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
+ case SOF_DAI_AMD_BT:
+ ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ break;
+ case SOF_DAI_AMD_SP:
+ ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ break;
+ case SOF_DAI_AMD_DMIC:
+ ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf,
+ config);
+ break;
+ case SOF_DAI_MEDIATEK_AFE:
+ ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ break;
default:
dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type);
ret = -EINVAL;
@@ -3461,7 +3529,7 @@ static int sof_complete(struct snd_soc_component *scomp)
* 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)
+ list_for_each_entry(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)
@@ -3474,7 +3542,7 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* verify topology components loading including dynamic pipelines */
- if (sof_core_debug & SOF_DBG_VERIFY_TPLG) {
+ if (sof_debug_check_flag(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);
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index e3afc3dac7d1..f13024c8ebf2 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -539,6 +539,10 @@ EXPORT_SYMBOL(snd_sof_trace_notify_for_error);
void snd_sof_release_trace(struct snd_sof_dev *sdev)
{
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+ struct sof_ipc_cmd_hdr hdr;
+ struct sof_ipc_reply ipc_reply;
int ret;
if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled)
@@ -549,6 +553,20 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev)
dev_err(sdev->dev,
"error: snd_sof_dma_trace_trigger: stop: %d\n", ret);
+ /*
+ * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from
+ * ABI 3.20.0 onwards
+ */
+ if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
+ hdr.size = sizeof(hdr);
+ hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
+
+ ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size,
+ &ipc_reply, sizeof(ipc_reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
+ }
+
ret = snd_sof_dma_trace_release(sdev);
if (ret < 0)
dev_err(sdev->dev,
diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c
index bd09c3825caf..bebbe3a2865c 100644
--- a/sound/soc/sof/xtensa/core.c
+++ b/sound/soc/sof/xtensa/core.c
@@ -81,33 +81,39 @@ static const struct xtensa_exception_cause xtensa_exception_causes[] = {
};
/* only need xtensa atm */
-static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops)
+static void xtensa_dsp_oops(struct snd_sof_dev *sdev, const char *level, void *oops)
{
struct sof_ipc_dsp_oops_xtensa *xoops = oops;
int i;
- dev_err(sdev->dev, "error: DSP Firmware Oops\n");
+ dev_printk(level, sdev->dev, "error: DSP Firmware Oops\n");
for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) {
if (xtensa_exception_causes[i].id == xoops->exccause) {
- dev_err(sdev->dev, "error: Exception Cause: %s, %s\n",
- xtensa_exception_causes[i].msg,
- xtensa_exception_causes[i].description);
+ dev_printk(level, sdev->dev,
+ "error: Exception Cause: %s, %s\n",
+ xtensa_exception_causes[i].msg,
+ xtensa_exception_causes[i].description);
}
}
- dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n",
- xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar);
- dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x",
- xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4);
- dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x",
- xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc);
- dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x",
- xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5);
- dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x",
- xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt);
+ dev_printk(level, sdev->dev,
+ "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n",
+ xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar);
+ dev_printk(level, sdev->dev,
+ "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x",
+ xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4);
+ dev_printk(level, sdev->dev,
+ "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x",
+ xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc);
+ dev_printk(level, sdev->dev,
+ "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x",
+ xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5);
+ dev_printk(level, sdev->dev,
+ "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x",
+ xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt);
}
-static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
- u32 stack_words)
+static void xtensa_stack(struct snd_sof_dev *sdev, const char *level, void *oops,
+ u32 *stack, u32 stack_words)
{
struct sof_ipc_dsp_oops_xtensa *xoops = oops;
u32 stack_ptr = xoops->plat_hdr.stackptr;
@@ -115,7 +121,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
unsigned char buf[4 * 8 + 3 + 1];
int i;
- dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr);
+ dev_printk(level, sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr);
/*
* example output:
@@ -124,7 +130,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack,
for (i = 0; i < stack_words; i += 4) {
hex_dump_to_buffer(stack + i, 16, 16, 4,
buf, sizeof(buf), false);
- dev_err(sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf);
+ dev_printk(level, sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf);
}
}
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
index e6078f50e508..6ee714542b84 100644
--- a/sound/soc/stm/stm32_adfsdm.c
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -12,7 +12,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-
+#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/consumer.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
@@ -334,6 +334,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, priv);
+ pm_runtime_enable(&pdev->dev);
+
ret = devm_snd_soc_register_component(&pdev->dev,
&stm32_adfsdm_dai_component,
&priv->dai_drv, 1);
@@ -373,6 +375,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
static int stm32_adfsdm_remove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 717f45a83445..ac5dff4d1677 100644
--- a/sound/soc/stm/stm32_i2s.c
+++ b/sound/soc/stm/stm32_i2s.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
@@ -1044,36 +1045,24 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
/* Get clocks */
i2s->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(i2s->pclk)) {
- if (PTR_ERR(i2s->pclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get pclk: %ld\n",
- PTR_ERR(i2s->pclk));
- return PTR_ERR(i2s->pclk);
- }
+ if (IS_ERR(i2s->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->pclk),
+ "Could not get pclk\n");
i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk");
- if (IS_ERR(i2s->i2sclk)) {
- if (PTR_ERR(i2s->i2sclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get i2sclk: %ld\n",
- PTR_ERR(i2s->i2sclk));
- return PTR_ERR(i2s->i2sclk);
- }
+ if (IS_ERR(i2s->i2sclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->i2sclk),
+ "Could not get i2sclk\n");
i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k");
- if (IS_ERR(i2s->x8kclk)) {
- if (PTR_ERR(i2s->x8kclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get x8k parent clock: %ld\n",
- PTR_ERR(i2s->x8kclk));
- return PTR_ERR(i2s->x8kclk);
- }
+ if (IS_ERR(i2s->x8kclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x8kclk),
+ "Could not get x8k parent clock\n");
i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k");
- if (IS_ERR(i2s->x11kclk)) {
- if (PTR_ERR(i2s->x11kclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get x11k parent clock: %ld\n",
- PTR_ERR(i2s->x11kclk));
- return PTR_ERR(i2s->x11kclk);
- }
+ if (IS_ERR(i2s->x11kclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x11kclk),
+ "Could not get x11k parent clock\n");
/* Register mclk provider if requested */
if (of_find_property(np, "#clock-cells", NULL)) {
@@ -1096,12 +1085,10 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
/* Reset */
rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rst)) {
- if (PTR_ERR(rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Reset controller error %ld\n",
- PTR_ERR(rst));
- return PTR_ERR(rst);
- }
+ if (IS_ERR(rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+ "Reset controller error\n");
+
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
@@ -1113,6 +1100,7 @@ static int stm32_i2s_remove(struct platform_device *pdev)
{
snd_dmaengine_pcm_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -1143,19 +1131,15 @@ static int stm32_i2s_probe(struct platform_device *pdev)
i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk",
i2s->base, i2s->regmap_conf);
- if (IS_ERR(i2s->regmap)) {
- if (PTR_ERR(i2s->regmap) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Regmap init error %ld\n",
- PTR_ERR(i2s->regmap));
- return PTR_ERR(i2s->regmap);
- }
+ if (IS_ERR(i2s->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap),
+ "Regmap init error\n");
+
+ pm_runtime_enable(&pdev->dev);
ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "PCM DMA register error %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n");
ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component,
i2s->dai_drv, 1);
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index 058757c721f0..8e21e6f886fc 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -173,29 +173,20 @@ static int stm32_sai_probe(struct platform_device *pdev)
if (!STM_SAI_IS_F4(sai)) {
sai->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(sai->pclk)) {
- if (PTR_ERR(sai->pclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "missing bus clock pclk: %ld\n",
- PTR_ERR(sai->pclk));
- return PTR_ERR(sai->pclk);
- }
+ if (IS_ERR(sai->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk),
+ "missing bus clock pclk\n");
}
sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
- if (IS_ERR(sai->clk_x8k)) {
- if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "missing x8k parent clock: %ld\n",
- PTR_ERR(sai->clk_x8k));
- return PTR_ERR(sai->clk_x8k);
- }
+ if (IS_ERR(sai->clk_x8k))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k),
+ "missing x8k parent clock\n");
sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
- if (IS_ERR(sai->clk_x11k)) {
- if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "missing x11k parent clock: %ld\n",
- PTR_ERR(sai->clk_x11k));
- return PTR_ERR(sai->clk_x11k);
- }
+ if (IS_ERR(sai->clk_x11k))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k),
+ "missing x11k parent clock\n");
/* init irqs */
sai->irq = platform_get_irq(pdev, 0);
@@ -204,12 +195,10 @@ static int stm32_sai_probe(struct platform_device *pdev)
/* reset */
rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rst)) {
- if (PTR_ERR(rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Reset controller error %ld\n",
- PTR_ERR(rst));
- return PTR_ERR(rst);
- }
+ if (IS_ERR(rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+ "Reset controller error\n");
+
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index 9c3b8e209656..dd636af81c9b 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -1294,7 +1294,7 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = {
.id = 1, /* avoid call to fmt_single_name() */
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 16,
.rate_min = 8000,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -1312,7 +1312,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = {
.id = 1, /* avoid call to fmt_single_name() */
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 16,
.rate_min = 8000,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -1379,12 +1379,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
*/
sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
sai->regmap_config);
- if (IS_ERR(sai->regmap)) {
- if (PTR_ERR(sai->regmap) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Regmap init error %ld\n",
- PTR_ERR(sai->regmap));
- return PTR_ERR(sai->regmap);
- }
+ if (IS_ERR(sai->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap),
+ "Regmap init error\n");
/* Get direction property */
if (of_property_match_string(np, "dma-names", "tx") >= 0) {
@@ -1472,12 +1469,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
of_node_put(args.np);
sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
- if (IS_ERR(sai->sai_ck)) {
- if (PTR_ERR(sai->sai_ck) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Missing kernel clock sai_ck: %ld\n",
- PTR_ERR(sai->sai_ck));
- return PTR_ERR(sai->sai_ck);
- }
+ if (IS_ERR(sai->sai_ck))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck),
+ "Missing kernel clock sai_ck\n");
ret = clk_prepare(sai->pdata->pclk);
if (ret < 0)
@@ -1551,11 +1545,8 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
conf = &stm32_sai_pcm_config_spdif;
ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not register pcm dma\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n");
ret = snd_soc_register_component(&pdev->dev, &stm32_component,
&sai->cpu_dai_drv, 1);
diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c
index 48145f553588..6f7882c4fe6a 100644
--- a/sound/soc/stm/stm32_spdifrx.c
+++ b/sound/soc/stm/stm32_spdifrx.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -405,12 +406,9 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev,
int ret;
spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl");
- if (IS_ERR(spdifrx->ctrl_chan)) {
- if (PTR_ERR(spdifrx->ctrl_chan) != -EPROBE_DEFER)
- dev_err(dev, "dma_request_slave_channel error %ld\n",
- PTR_ERR(spdifrx->ctrl_chan));
- return PTR_ERR(spdifrx->ctrl_chan);
- }
+ if (IS_ERR(spdifrx->ctrl_chan))
+ return dev_err_probe(dev, PTR_ERR(spdifrx->ctrl_chan),
+ "dma_request_slave_channel error\n");
spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer),
GFP_KERNEL);
@@ -929,12 +927,9 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev,
spdifrx->phys_addr = res->start;
spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk");
- if (IS_ERR(spdifrx->kclk)) {
- if (PTR_ERR(spdifrx->kclk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get kclk: %ld\n",
- PTR_ERR(spdifrx->kclk));
- return PTR_ERR(spdifrx->kclk);
- }
+ if (IS_ERR(spdifrx->kclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->kclk),
+ "Could not get kclk\n");
spdifrx->irq = platform_get_irq(pdev, 0);
if (spdifrx->irq < 0)
@@ -955,6 +950,7 @@ static int stm32_spdifrx_remove(struct platform_device *pdev)
snd_dmaengine_pcm_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -985,12 +981,9 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk",
spdifrx->base,
spdifrx->regmap_conf);
- if (IS_ERR(spdifrx->regmap)) {
- if (PTR_ERR(spdifrx->regmap) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Regmap init error %ld\n",
- PTR_ERR(spdifrx->regmap));
- return PTR_ERR(spdifrx->regmap);
- }
+ if (IS_ERR(spdifrx->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->regmap),
+ "Regmap init error\n");
ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0,
dev_name(&pdev->dev), spdifrx);
@@ -1000,23 +993,20 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
}
rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rst)) {
- if (PTR_ERR(rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Reset controller error %ld\n",
- PTR_ERR(rst));
- return PTR_ERR(rst);
- }
+ if (IS_ERR(rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+ "Reset controller error\n");
+
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
+ pm_runtime_enable(&pdev->dev);
+
pcm_config = &stm32_spdifrx_pcm_config;
ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "PCM DMA register error %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n");
ret = snd_soc_register_component(&pdev->dev,
&stm32_spdifrx_component,
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index da597e456beb..60712f24ade5 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -1752,8 +1752,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
GPIOD_OUT_LOW);
if (IS_ERR(scodec->gpio_pa)) {
ret = PTR_ERR(scodec->gpio_pa);
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
+ dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n");
return ret;
}
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index a10949bf0ca1..17090f43150e 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -21,6 +21,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -186,6 +188,7 @@ struct sun4i_spdif_dev {
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data dma_params_tx;
const struct sun4i_spdif_quirks *quirks;
+ spinlock_t lock;
};
static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
@@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
+static int sun4i_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 *status = ucontrol->value.iec958.status;
+
+ status[0] = 0xff;
+ status[1] = 0xff;
+ status[2] = 0xff;
+ status[3] = 0xff;
+ status[4] = 0xff;
+ status[5] = 0x03;
+
+ return 0;
+}
+
+static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+ u8 *status = ucontrol->value.iec958.status;
+ unsigned long flags;
+ unsigned int reg;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, &reg);
+
+ status[0] = reg & 0xff;
+ status[1] = (reg >> 8) & 0xff;
+ status[2] = (reg >> 16) & 0xff;
+ status[3] = (reg >> 24) & 0xff;
+
+ regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, &reg);
+
+ status[4] = reg & 0xff;
+ status[5] = (reg >> 8) & 0x3;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+ u8 *status = ucontrol->value.iec958.status;
+ unsigned long flags;
+ unsigned int reg;
+ bool chg0, chg1;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ reg = (u32)status[3] << 24;
+ reg |= (u32)status[2] << 16;
+ reg |= (u32)status[1] << 8;
+ reg |= (u32)status[0];
+
+ regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0,
+ GENMASK(31,0), reg, &chg0);
+
+ reg = (u32)status[5] << 8;
+ reg |= (u32)status[4];
+
+ regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1,
+ GENMASK(9,0), reg, &chg1);
+
+ reg = SUN4I_SPDIF_TXCFG_CHSTMODE;
+ if (status[0] & IEC958_AES0_NONAUDIO)
+ reg |= SUN4I_SPDIF_TXCFG_NONAUDIO;
+
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+ SUN4I_SPDIF_TXCFG_CHSTMODE |
+ SUN4I_SPDIF_TXCFG_NONAUDIO, reg);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return chg0 || chg1;
+}
+
+static struct snd_kcontrol_new sun4i_spdif_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .info = sun4i_spdif_info,
+ .get = sun4i_spdif_get_status_mask
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = sun4i_spdif_info,
+ .get = sun4i_spdif_get_status,
+ .put = sun4i_spdif_set_status
+ }
+};
+
static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
{
struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
+ snd_soc_add_dai_controls(dai, sun4i_spdif_controls,
+ ARRAY_SIZE(sun4i_spdif_controls));
+
return 0;
}
@@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
return -ENOMEM;
host->pdev = pdev;
+ spin_lock_init(&host->lock);
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 518bfb724a5b..0bea2162f68d 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -21,6 +21,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
#define SUN8I_SYSCLK_CTL 0x00c
#define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11
@@ -72,6 +73,12 @@
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8
+#define SUN8I_AIF1_VOL_CTRL1 0x050
+#define SUN8I_AIF1_VOL_CTRL1_AD0L_VOL 8
+#define SUN8I_AIF1_VOL_CTRL1_AD0R_VOL 0
+#define SUN8I_AIF1_VOL_CTRL3 0x058
+#define SUN8I_AIF1_VOL_CTRL3_DA0L_VOL 8
+#define SUN8I_AIF1_VOL_CTRL3_DA0R_VOL 0
#define SUN8I_AIF2_ADCDAT_CTRL 0x084
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14
@@ -91,6 +98,12 @@
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8
+#define SUN8I_AIF2_VOL_CTRL1 0x090
+#define SUN8I_AIF2_VOL_CTRL1_ADCL_VOL 8
+#define SUN8I_AIF2_VOL_CTRL1_ADCR_VOL 0
+#define SUN8I_AIF2_VOL_CTRL2 0x098
+#define SUN8I_AIF2_VOL_CTRL2_DACL_VOL 8
+#define SUN8I_AIF2_VOL_CTRL2_DACR_VOL 0
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0)
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0)
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0)
@@ -102,8 +115,14 @@
#define SUN8I_ADC_DIG_CTRL_ENAD 15
#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2
#define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1
+#define SUN8I_ADC_VOL_CTRL 0x104
+#define SUN8I_ADC_VOL_CTRL_ADCL_VOL 8
+#define SUN8I_ADC_VOL_CTRL_ADCR_VOL 0
#define SUN8I_DAC_DIG_CTRL 0x120
#define SUN8I_DAC_DIG_CTRL_ENDA 15
+#define SUN8I_DAC_VOL_CTRL 0x124
+#define SUN8I_DAC_VOL_CTRL_DACL_VOL 8
+#define SUN8I_DAC_VOL_CTRL_DACR_VOL 0
#define SUN8I_DAC_MXR_SRC 0x130
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14
@@ -696,6 +715,41 @@ static struct snd_soc_dai_driver sun8i_codec_dais[] = {
},
};
+static const DECLARE_TLV_DB_SCALE(sun8i_codec_vol_scale, -12000, 75, 1);
+
+static const struct snd_kcontrol_new sun8i_codec_controls[] = {
+ SOC_DOUBLE_TLV("AIF1 AD0 Capture Volume",
+ SUN8I_AIF1_VOL_CTRL1,
+ SUN8I_AIF1_VOL_CTRL1_AD0L_VOL,
+ SUN8I_AIF1_VOL_CTRL1_AD0R_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+ SOC_DOUBLE_TLV("AIF1 DA0 Playback Volume",
+ SUN8I_AIF1_VOL_CTRL3,
+ SUN8I_AIF1_VOL_CTRL3_DA0L_VOL,
+ SUN8I_AIF1_VOL_CTRL3_DA0R_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+ SOC_DOUBLE_TLV("AIF2 ADC Capture Volume",
+ SUN8I_AIF2_VOL_CTRL1,
+ SUN8I_AIF2_VOL_CTRL1_ADCL_VOL,
+ SUN8I_AIF2_VOL_CTRL1_ADCR_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+ SOC_DOUBLE_TLV("AIF2 DAC Playback Volume",
+ SUN8I_AIF2_VOL_CTRL2,
+ SUN8I_AIF2_VOL_CTRL2_DACL_VOL,
+ SUN8I_AIF2_VOL_CTRL2_DACR_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+ SOC_DOUBLE_TLV("ADC Capture Volume",
+ SUN8I_ADC_VOL_CTRL,
+ SUN8I_ADC_VOL_CTRL_ADCL_VOL,
+ SUN8I_ADC_VOL_CTRL_ADCR_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+ SOC_DOUBLE_TLV("DAC Playback Volume",
+ SUN8I_DAC_VOL_CTRL,
+ SUN8I_DAC_VOL_CTRL_DACL_VOL,
+ SUN8I_DAC_VOL_CTRL_DACR_VOL,
+ 0xc0, 0, sun8i_codec_vol_scale),
+};
+
static int sun8i_codec_aif_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1215,6 +1269,8 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component)
}
static const struct snd_soc_component_driver sun8i_soc_component = {
+ .controls = sun8i_codec_controls,
+ .num_controls = ARRAY_SIZE(sun8i_codec_controls),
.dapm_widgets = sun8i_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets),
.dapm_routes = sun8i_codec_dapm_routes,
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 266d2cab9f49..27365a877e47 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -262,10 +262,59 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai)
return 0;
}
+static const unsigned int tegra20_i2s_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000
+};
+
+static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r = hw_param_interval(params, rule->var);
+ struct snd_soc_dai *dai = rule->private;
+ struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev);
+ struct clk *parent = clk_get_parent(i2s->clk_i2s);
+ long i, parent_rate, valid_rates = 0;
+
+ parent_rate = clk_get_rate(parent);
+ if (parent_rate <= 0) {
+ dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
+ parent_rate);
+ return parent_rate ?: -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) {
+ if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0)
+ valid_rates |= BIT(i);
+ }
+
+ /*
+ * At least one rate must be valid, otherwise the parent clock isn't
+ * audio PLL. Nothing should be filtered in this case.
+ */
+ if (!valid_rates)
+ valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1;
+
+ return snd_interval_list(r, ARRAY_SIZE(tegra20_i2s_rates),
+ tegra20_i2s_rates, valid_rates);
+}
+
+static int tegra20_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate"))
+ return 0;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ tegra20_i2s_filter_rates, dai,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+
static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
.set_fmt = tegra20_i2s_set_fmt,
.hw_params = tegra20_i2s_hw_params,
.trigger = tegra20_i2s_trigger,
+ .startup = tegra20_i2s_startup,
};
static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index 7751575cd6d6..d09cd7ee6879 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -7,12 +7,15 @@
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -22,12 +25,12 @@
#include "tegra20_spdif.h"
-#define DRV_NAME "tegra20-spdif"
-
static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev)
{
struct tegra20_spdif *spdif = dev_get_drvdata(dev);
+ regcache_cache_only(spdif->regmap, true);
+
clk_disable_unprepare(spdif->clk_spdif_out);
return 0;
@@ -38,23 +41,45 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev)
struct tegra20_spdif *spdif = dev_get_drvdata(dev);
int ret;
+ ret = reset_control_assert(spdif->reset);
+ if (ret)
+ return ret;
+
ret = clk_prepare_enable(spdif->clk_spdif_out);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
return ret;
}
+ usleep_range(10, 100);
+
+ ret = reset_control_deassert(spdif->reset);
+ if (ret)
+ goto disable_clocks;
+
+ regcache_cache_only(spdif->regmap, false);
+ regcache_mark_dirty(spdif->regmap);
+
+ ret = regcache_sync(spdif->regmap);
+ if (ret)
+ goto disable_clocks;
+
return 0;
+
+disable_clocks:
+ clk_disable_unprepare(spdif->clk_spdif_out);
+
+ return ret;
}
static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct device *dev = dai->dev;
- struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
unsigned int mask = 0, val = 0;
int ret, spdifclock;
+ long rate;
mask |= TEGRA20_SPDIF_CTRL_PACK |
TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
@@ -69,6 +94,14 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
+ /*
+ * FIFO trigger level must be bigger than DMA burst or equal to it,
+ * otherwise data is discarded on overflow.
+ */
+ regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR,
+ TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK,
+ TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL);
+
switch (params_rate(params)) {
case 32000:
spdifclock = 4096000;
@@ -97,10 +130,16 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
if (ret) {
- dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
+ dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret);
return ret;
}
+ rate = clk_get_rate(spdif->clk_spdif_out);
+ if (rate != spdifclock)
+ dev_warn_once(dai->dev,
+ "SPDIF clock rate %d doesn't match requested rate %lu\n",
+ spdifclock, rate);
+
return 0;
}
@@ -118,9 +157,9 @@ static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
}
static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *dai)
{
- struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -140,9 +179,62 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
+static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r = hw_param_interval(params, rule->var);
+ struct snd_soc_dai *dai = rule->private;
+ struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
+ struct clk *parent = clk_get_parent(spdif->clk_spdif_out);
+ const unsigned int rates[] = { 32000, 44100, 48000 };
+ long i, parent_rate, valid_rates = 0;
+
+ parent_rate = clk_get_rate(parent);
+ if (parent_rate <= 0) {
+ dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
+ parent_rate);
+ return parent_rate ?: -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ if (parent_rate % (rates[i] * 128) == 0)
+ valid_rates |= BIT(i);
+ }
+
+ /*
+ * At least one rate must be valid, otherwise the parent clock isn't
+ * audio PLL. Nothing should be filtered in this case.
+ */
+ if (!valid_rates)
+ valid_rates = BIT(ARRAY_SIZE(rates)) - 1;
+
+ return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates);
+}
+
+static int tegra20_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate"))
+ return 0;
+
+ /*
+ * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF
+ * and audio may not work on some TVs if clock rate isn't precise.
+ *
+ * PLL rate is controlled by I2S side. Filter out audio rates that
+ * don't match PLL rate at the start of stream to allow both SPDIF
+ * and I2S work simultaneously, assuming that PLL rate won't be
+ * changed later on.
+ */
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ tegra20_spdif_filter_rates, dai,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+
static int tegra20_spdif_probe(struct snd_soc_dai *dai)
{
- struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
dai->capture_dma_data = NULL;
dai->playback_dma_data = &spdif->playback_dma_data;
@@ -151,26 +243,27 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai)
}
static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
- .hw_params = tegra20_spdif_hw_params,
- .trigger = tegra20_spdif_trigger,
+ .hw_params = tegra20_spdif_hw_params,
+ .trigger = tegra20_spdif_trigger,
+ .startup = tegra20_spdif_startup,
};
static struct snd_soc_dai_driver tegra20_spdif_dai = {
- .name = DRV_NAME,
+ .name = "tegra20-spdif",
.probe = tegra20_spdif_probe,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000,
+ SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &tegra20_spdif_dai_ops,
};
static const struct snd_soc_component_driver tegra20_spdif_component = {
- .name = DRV_NAME,
+ .name = "tegra20-spdif",
};
static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
@@ -251,7 +344,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = {
static int tegra20_spdif_platform_probe(struct platform_device *pdev)
{
struct tegra20_spdif *spdif;
- struct resource *mem, *dmareq;
+ struct resource *mem;
void __iomem *regs;
int ret;
@@ -262,89 +355,77 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, spdif);
- spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
+ spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(spdif->reset)) {
+ dev_err(&pdev->dev, "Can't retrieve spdif reset\n");
+ return PTR_ERR(spdif->reset);
+ }
+
+ spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out");
if (IS_ERR(spdif->clk_spdif_out)) {
- pr_err("Can't retrieve spdif clock\n");
- ret = PTR_ERR(spdif->clk_spdif_out);
- return ret;
+ dev_err(&pdev->dev, "Could not retrieve spdif clock\n");
+ return PTR_ERR(spdif->clk_spdif_out);
}
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(regs))
return PTR_ERR(regs);
- dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmareq) {
- dev_err(&pdev->dev, "No DMA resource\n");
- return -ENODEV;
- }
-
spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
- &tegra20_spdif_regmap_config);
+ &tegra20_spdif_regmap_config);
if (IS_ERR(spdif->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
- ret = PTR_ERR(spdif->regmap);
- return ret;
+ return PTR_ERR(spdif->regmap);
}
spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
spdif->playback_dma_data.maxburst = 4;
- spdif->playback_dma_data.slave_id = dmareq->start;
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
- ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
- &tegra20_spdif_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &tegra20_spdif_component,
+ &tegra20_spdif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
- ret = -ENOMEM;
- goto err_pm_disable;
+ return ret;
}
- ret = tegra_pcm_platform_register(&pdev->dev);
+ ret = devm_tegra_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_component;
+ return ret;
}
return 0;
-
-err_unregister_component:
- snd_soc_unregister_component(&pdev->dev);
-err_pm_disable:
- pm_runtime_disable(&pdev->dev);
-
- return ret;
-}
-
-static int tegra20_spdif_platform_remove(struct platform_device *pdev)
-{
- tegra_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
-
- pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct dev_pm_ops tegra20_spdif_pm_ops = {
SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
tegra20_spdif_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
+static const struct of_device_id tegra20_spdif_of_match[] = {
+ { .compatible = "nvidia,tegra20-spdif", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match);
+
static struct platform_driver tegra20_spdif_driver = {
.driver = {
- .name = DRV_NAME,
+ .name = "tegra20-spdif",
.pm = &tegra20_spdif_pm_ops,
+ .of_match_table = tegra20_spdif_of_match,
},
.probe = tegra20_spdif_platform_probe,
- .remove = tegra20_spdif_platform_remove,
};
-
module_platform_driver(tegra20_spdif_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h
index 1973ffc2d5c7..ff4b79e2052f 100644
--- a/sound/soc/tegra/tegra20_spdif.h
+++ b/sound/soc/tegra/tegra20_spdif.h
@@ -451,6 +451,7 @@ struct tegra20_spdif {
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
+ struct reset_control *reset;
};
#endif
diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c
index acf59328dcb6..725385e17d84 100644
--- a/sound/soc/tegra/tegra210_mvc.c
+++ b/sound/soc/tegra/tegra210_mvc.c
@@ -108,67 +108,152 @@ static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
}
}
-static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol)
{
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;
+ return val;
+}
+
+static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
+ u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
- ucontrol->value.integer.value[0] = mute_mask;
+ /*
+ * If per channel control is enabled, then return
+ * exact mute/unmute setting of all channels.
+ *
+ * Else report setting based on CH0 bit to reflect
+ * the correct HW state.
+ */
+ if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
+ ucontrol->value.integer.value[0] = mute_mask;
+ } else {
+ if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
+ ucontrol->value.integer.value[0] =
+ TEGRA210_MUTE_MASK_EN;
+ else
+ ucontrol->value.integer.value[0] = 0;
+ }
return 0;
}
-static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
+ u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
+
+ /*
+ * If per channel control is disabled, then return
+ * master mute/unmute setting based on CH0 bit.
+ *
+ * Else report settings based on state of all
+ * channels.
+ */
+ if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
+ ucontrol->value.integer.value[0] =
+ mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
+ } else {
+ if (mute_mask == TEGRA210_MUTE_MASK_EN)
+ ucontrol->value.integer.value[0] =
+ TEGRA210_MVC_CH0_MUTE_EN;
+ else
+ ucontrol->value.integer.value[0] = 0;
+ }
+
+ return 0;
+}
+
+static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt)
{
- 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 new_mask, old_mask;
+ u32 value;
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;
+ dev_err(cmpnt->dev,
+ "Volume switch trigger is still active, err = %d\n",
+ err);
- regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &value);
+ return err;
+}
- old_mask = (value >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN;
- new_mask = ucontrol->value.integer.value[0];
+static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ bool per_chan_ctrl)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
+ u32 mute_val = ucontrol->value.integer.value[0];
+ u32 per_ch_ctrl_val;
+ bool change = false;
+ int err;
- if (new_mask == old_mask) {
- err = 0;
+ pm_runtime_get_sync(cmpnt->dev);
+
+ err = tegra210_mvc_volume_switch_timeout(cmpnt);
+ if (err < 0)
goto end;
+
+ if (per_chan_ctrl) {
+ per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN;
+ } else {
+ per_ch_ctrl_val = 0;
+
+ if (mute_val)
+ mute_val = TEGRA210_MUTE_MASK_EN;
}
- err = regmap_update_bits(mvc->regmap, mc->reg,
+ regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_MUTE_MASK,
- new_mask << TEGRA210_MVC_MUTE_SHIFT);
- if (err < 0)
- goto end;
+ mute_val << TEGRA210_MVC_MUTE_SHIFT,
+ &change);
- err = 1;
+ if (change) {
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+ per_ch_ctrl_val);
+
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
+ TEGRA210_MVC_VOLUME_SWITCH_MASK,
+ TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
+ }
end:
pm_runtime_put(cmpnt->dev);
- return err;
+
+ if (err < 0)
+ return err;
+
+ if (change)
+ return 1;
+
+ return 0;
+}
+
+static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return tegra210_mvc_update_mute(kcontrol, ucontrol, true);
+}
+
+static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return tegra210_mvc_update_mute(kcontrol, ucontrol, false);
}
static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
@@ -178,7 +263,7 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
(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;
+ u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
s32 val = mvc->volume[chan];
if (mvc->curve_type == CURVE_POLY) {
@@ -193,44 +278,55 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
return 0;
}
-static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return tegra210_mvc_get_vol(kcontrol, ucontrol);
+}
+
+static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ bool per_ch_enable)
{
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, old_volume;
+ u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
+ int old_volume = mvc->volume[chan];
+ int err, i;
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);
+ err = tegra210_mvc_volume_switch_timeout(cmpnt);
if (err < 0)
goto end;
- chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
- old_volume = mvc->volume[chan];
-
- tegra210_mvc_conv_vol(mvc, chan,
- ucontrol->value.integer.value[0]);
+ tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]);
if (mvc->volume[chan] == old_volume) {
err = 0;
goto end;
}
+ if (per_ch_enable) {
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+ TEGRA210_MVC_PER_CHAN_CTRL_EN);
+ } else {
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0);
+
+ for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
+ mvc->volume[i] = mvc->volume[chan];
+ }
+
/* 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_write(mvc->regmap, mc->reg, mvc->volume[chan]);
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK,
@@ -240,9 +336,22 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
end:
pm_runtime_put(cmpnt->dev);
+
return err;
}
+static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return tegra210_mvc_update_vol(kcontrol, ucontrol, true);
+}
+
+static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return tegra210_mvc_update_vol(kcontrol, ucontrol, false);
+}
+
static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
struct device *dev)
{
@@ -436,6 +545,16 @@ static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
tegra210_mvc_get_mute, tegra210_mvc_put_mute),
+ /* Master volume */
+ SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
+ tegra210_mvc_get_master_vol,
+ tegra210_mvc_put_master_vol),
+
+ /* Master mute */
+ SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
+ tegra210_mvc_get_master_mute,
+ tegra210_mvc_put_master_mute),
+
SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
};
diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h
index def29c4c7257..d775335dc60b 100644
--- a/sound/soc/tegra/tegra210_mvc.h
+++ b/sound/soc/tegra/tegra210_mvc.h
@@ -59,6 +59,7 @@
#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_CH0_MUTE_EN 1
#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)
@@ -92,6 +93,10 @@
#define TEGRA210_MVC_MAX_CHAN_COUNT 8
#define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i))
+#define TEGRA210_MVC_GET_CHAN(reg, base) (((reg) - (base)) / REG_SIZE)
+
+#define TEGRA210_GET_MUTE_VAL(val) (((val) >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN)
+
#define NUM_GAIN_POLY_COEFFS 9
enum {
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index ef1e74d95236..468c8e77de21 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -48,6 +48,12 @@ int tegra_pcm_platform_register(struct device *dev)
}
EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
+int devm_tegra_pcm_platform_register(struct device *dev)
+{
+ return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
+}
+EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register);
+
int tegra_pcm_platform_register_with_chan_names(struct device *dev,
struct snd_dmaengine_pcm_config *config,
char *txdmachan, char *rxdmachan)
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index d602126c65b7..2a36eea1740d 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -32,6 +32,7 @@ int tegra_pcm_hw_params(struct snd_soc_component *component,
snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
int tegra_pcm_platform_register(struct device *dev);
+int devm_tegra_pcm_platform_register(struct device *dev);
int tegra_pcm_platform_register_with_chan_names(struct device *dev,
struct snd_dmaengine_pcm_config *config,
char *txdmachan, char *rxdmachan);
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 56a19eeec5c7..2c146b91fca3 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1870,12 +1870,10 @@ err1:
static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp)
{
#ifdef CONFIG_OF_GPIO
- if (mcasp->dev->of_node &&
- of_property_read_bool(mcasp->dev->of_node, "gpio-controller"))
- return true;
-#endif
-
+ return of_property_read_bool(mcasp->dev->of_node, "gpio-controller");
+#else
return false;
+#endif
}
static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
@@ -2026,13 +2024,9 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
chan = dma_request_chan(mcasp->dev, tmp);
- if (IS_ERR(chan)) {
- if (PTR_ERR(chan) != -EPROBE_DEFER)
- dev_err(mcasp->dev,
- "Can't verify DMA configuration (%ld)\n",
- PTR_ERR(chan));
- return PTR_ERR(chan);
- }
+ if (IS_ERR(chan))
+ return dev_err_probe(mcasp->dev, PTR_ERR(chan),
+ "Can't verify DMA configuration\n");
if (WARN_ON(!chan->device || !chan->device->dev)) {
dma_release_channel(chan);
return -EINVAL;
@@ -2230,9 +2224,6 @@ static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
mcasp->gpio_chip = davinci_mcasp_template_chip;
mcasp->gpio_chip.label = dev_name(mcasp->dev);
mcasp->gpio_chip.parent = mcasp->dev;
-#ifdef CONFIG_OF_GPIO
- mcasp->gpio_chip.of_node = mcasp->dev->of_node;
-#endif
return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp);
}
diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c
index 9347f982c3e1..4077e15ec48b 100644
--- a/sound/soc/ti/j721e-evm.c
+++ b/sound/soc/ti/j721e-evm.c
@@ -464,13 +464,9 @@ static int j721e_get_clocks(struct device *dev,
int ret;
clocks->target = devm_clk_get(dev, prefix);
- if (IS_ERR(clocks->target)) {
- ret = PTR_ERR(clocks->target);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire %s: %d\n",
- prefix, ret);
- return ret;
- }
+ if (IS_ERR(clocks->target))
+ return dev_err_probe(dev, PTR_ERR(clocks->target),
+ "failed to acquire %s\n", prefix);
clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix);
if (clk_name) {
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
index aa3592ee1358..ddfa6424c656 100644
--- a/sound/soc/uniphier/Kconfig
+++ b/sound/soc/uniphier/Kconfig
@@ -23,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11
tristate "UniPhier LD11/LD20 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
- select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier LD11/LD20
input and output that can be used with other codecs.
@@ -34,7 +33,6 @@ config SND_SOC_UNIPHIER_PXS2
tristate "UniPhier PXs2 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
- select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier PXs2
input and output that can be used with other codecs.
diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c
index e2ca087adee6..cba0e868a7d7 100644
--- a/sound/soc/xilinx/xlnx_spdif.c
+++ b/sound/soc/xilinx/xlnx_spdif.c
@@ -237,7 +237,6 @@ MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match);
static int xlnx_spdif_probe(struct platform_device *pdev)
{
int ret;
- struct resource *res;
struct snd_soc_dai_driver *dai_drv;
struct spdif_dev_data *ctx;
@@ -273,13 +272,10 @@ static int xlnx_spdif_probe(struct platform_device *pdev)
if (ctx->mode) {
dai_drv = &xlnx_spdif_tx_dai;
} else {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "No IRQ resource found\n");
- ret = -ENODEV;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto clk_err;
- }
- ret = devm_request_irq(dev, res->start,
+ ret = devm_request_irq(dev, ret,
xlnx_spdifrx_irq_handler,
0, "XLNX_SPDIF_RX", ctx);
if (ret) {
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 6b84f66e4af4..3881e1c1b08a 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -688,7 +688,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
{
u32 dvma_addr = (u32)dbri->dma_dvma;
s32 tmp, addr;
- static int wait_id = 0;
+ static int wait_id;
wait_id++;
wait_id &= 0xffff; /* restrict it to a 16 bit counter. */
@@ -1926,7 +1926,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri *dbri)
static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
{
struct snd_dbri *dbri = dev_id;
- static int errcnt = 0;
+ static int errcnt;
int x;
if (dbri == NULL)
@@ -2591,7 +2591,7 @@ static int dbri_probe(struct platform_device *op)
struct snd_dbri *dbri;
struct resource *rp;
struct snd_card *card;
- static int dev = 0;
+ static int dev;
int irq;
int err;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 1764b9302d46..376962291c4d 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -987,8 +987,6 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
wake_up(&chip->shutdown_wait);
}
-#ifdef CONFIG_PM
-
int snd_usb_autoresume(struct snd_usb_audio *chip)
{
int i, err;
@@ -1100,11 +1098,6 @@ err_out:
atomic_dec(&chip->active); /* allow autopm after this point */
return err;
}
-#else
-#define usb_audio_suspend NULL
-#define usb_audio_resume NULL
-#define usb_audio_resume NULL
-#endif /* CONFIG_PM */
static const struct usb_device_id usb_audio_ids [] = {
#include "quirks-table.h"
diff --git a/sound/usb/format.c b/sound/usb/format.c
index f5e676a51b30..405dc0bf6678 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -375,7 +375,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
for (rate = min; rate <= max; rate += res) {
/* Filter out invalid rates on Presonus Studio 1810c */
- if (chip->usb_id == USB_ID(0x0194f, 0x010c) &&
+ if (chip->usb_id == USB_ID(0x194f, 0x010c) &&
!s1810c_valid_sample_rate(fp, rate))
goto skip_rate;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 6e7bac8203ba..e8f3f8d622ec 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -145,6 +145,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p,
if (p && p->dB) {
cval->dBmin = p->dB->min;
cval->dBmax = p->dB->max;
+ cval->min_mute = p->dB->min_mute;
cval->initialized = 1;
}
}
@@ -3628,7 +3629,6 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
mixer->disconnected = true;
}
-#ifdef CONFIG_PM
/* stop any bus activity of a mixer */
static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
{
@@ -3710,7 +3710,6 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer)
return snd_usb_mixer_activate(mixer);
}
-#endif
void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
struct usb_mixer_interface *mixer,
@@ -3719,7 +3718,5 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
list->mixer = mixer;
list->id = unitid;
list->dump = snd_usb_mixer_dump_cval;
-#ifdef CONFIG_PM
list->resume = restore_mixer_value;
-#endif
}
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 98ea24d91d80..d43895c1ae5c 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -118,10 +118,8 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv);
-#ifdef CONFIG_PM
int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer);
int snd_usb_mixer_resume(struct usb_mixer_interface *mixer);
-#endif
int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
int index, int value);
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 55eea90ee993..5d391f62351b 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -6,8 +6,9 @@
*/
struct usbmix_dB_map {
- u32 min;
- u32 max;
+ int min;
+ int max;
+ bool min_mute;
};
struct usbmix_name_map {
@@ -336,6 +337,13 @@ static const struct usbmix_name_map bose_companion5_map[] = {
{ 0 } /* terminator */
};
+/* Bose Revolve+ SoundLink, correction of dB maps */
+static const struct usbmix_dB_map bose_soundlink_dB = {-8283, -0, true};
+static const struct usbmix_name_map bose_soundlink_map[] = {
+ { 2, NULL, .dB = &bose_soundlink_dB },
+ { 0 } /* terminator */
+};
+
/* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */
static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0};
static const struct usbmix_name_map sennheiser_pc8_map[] = {
@@ -522,6 +530,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = bose_companion5_map,
},
{
+ /* Bose Revolve+ SoundLink */
+ .id = USB_ID(0x05a7, 0x40fa),
+ .map = bose_soundlink_map,
+ },
+ {
/* Corsair Virtuoso SE (wired mode) */
.id = USB_ID(0x1b1c, 0x0a3d),
.map = corsair_virtuoso_map,
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 823b6b8de942..e447ddd6854c 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3254,7 +3254,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_rme_controls_create(mixer);
break;
- case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
+ case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */
err = snd_sc1810_init_mixer(mixer);
break;
case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
@@ -3280,7 +3280,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
return err;
}
-#ifdef CONFIG_PM
void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
{
switch (mixer->chip->usb_id) {
@@ -3289,7 +3288,6 @@ void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
break;
}
}
-#endif
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
int unitid)
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
index 52be26db558f..4ba01ba3fe8b 100644
--- a/sound/usb/mixer_quirks.h
+++ b/sound/usb/mixer_quirks.h
@@ -14,9 +14,7 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval, int unitid,
struct snd_kcontrol *kctl);
-#ifdef CONFIG_PM
void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer);
-#endif
#endif /* SND_USB_MIXER_QUIRKS_H */
diff --git a/sound/usb/power.h b/sound/usb/power.h
index 6004231a7c75..396e3e51440a 100644
--- a/sound/usb/power.h
+++ b/sound/usb/power.h
@@ -21,17 +21,7 @@ struct snd_usb_power_domain *
snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
unsigned char id);
-#ifdef CONFIG_PM
int snd_usb_autoresume(struct snd_usb_audio *chip);
void snd_usb_autosuspend(struct snd_usb_audio *chip);
-#else
-static inline int snd_usb_autoresume(struct snd_usb_audio *chip)
-{
- return 0;
-}
-static inline void snd_usb_autosuspend(struct snd_usb_audio *chip)
-{
-}
-#endif
#endif /* __USBAUDIO_POWER_H */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 64e1c20311ed..ab9f3da49941 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1290,7 +1290,7 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
if (chip->usb_id == USB_ID(0x0763, 0x2012))
return fasttrackpro_skip_setting_quirk(chip, iface, altno);
/* presonus studio 1810c: skip altsets incompatible with device_setup */
- if (chip->usb_id == USB_ID(0x0194f, 0x010c))
+ if (chip->usb_id == USB_ID(0x194f, 0x010c))
return s1810c_skip_setting_quirk(chip, iface, altno);
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 099bee662af6..52f4e6652407 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * usbusy2y.c - ALSA USB US-428 Driver
+ * usbusx2y.c - ALSA USB US-428 Driver
*
2005-04-14 Karsten Wiese
Version 0.8.7.2:
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
index 378826312abe..1c94eaff1931 100644
--- a/sound/x86/intel_hdmi_audio.c
+++ b/sound/x86/intel_hdmi_audio.c
@@ -1750,7 +1750,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
card_ctx->irq = irq;
/* only 32bit addressable */
- dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
init_channel_allocations();