Merge tag 'asoc-v5.19' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Mon, 23 May 2022 14:03:04 +0000 (16:03 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 23 May 2022 14:03:04 +0000 (16:03 +0200)
ASoC: Updates for v5.19

This is quite a big update, partly due to the addition of some larger
drivers (more of which is to follow since at least the AVS driver is
still a work in progress) and partly due to Charles' work sorting out
our handling of endianness.  As has been the case recently it's much
more about drivers than the core.

 - Overhaul of endianness specification for data formats, avoiding
   needless restrictions due to CODECs.
 - Initial stages of Intel AVS driver merge.
 - Introduction of v4 IPC mechanism for SOF.
 - TDM mode support for AK4613.
 - Support for Analog Devices ADAU1361, Cirrus Logic CS35L45, Maxim
   MAX98396, MediaTek MT8186, NXP i.MX8 micfil and SAI interfaces,
   nVidia Tegra186 ASRC, and Texas Instruments TAS2764 and TAS2780

521 files changed:
Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/adi,max98396.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl,micfil.txt
Documentation/devicetree/bindings/sound/maxim,max98390.yaml
Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml [deleted file]
Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml [moved from Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml with 84% similarity]
Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml
Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml
Documentation/devicetree/bindings/sound/rt5682.txt
Documentation/devicetree/bindings/sound/tas27xx.yaml [moved from Documentation/devicetree/bindings/sound/tas2764.yaml with 73% similarity]
Documentation/devicetree/bindings/sound/wlf,wm8731.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wlf,wm8940.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8731.txt [deleted file]
MAINTAINERS
arch/arm/configs/multi_v5_defconfig
drivers/dma/imx-dma.c
drivers/dma/imx-sdma.c
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/mtk-adsp-ipc.c [new file with mode: 0644]
drivers/mmc/host/mxcmmc.c
drivers/spi/spi-fsl-lpspi.c
drivers/spi/spi-imx.c
drivers/staging/greybus/audio_codec.c
drivers/tty/serial/imx.c
drivers/video/fbdev/mx3fb.c
include/dt-bindings/sound/cs35l45.h [new file with mode: 0644]
include/linux/dma/imx-dma.h [moved from include/linux/platform_data/dma-imx.h with 67% similarity]
include/linux/firmware/cirrus/cs_dsp.h
include/linux/firmware/mediatek/mtk-adsp-ipc.h [new file with mode: 0644]
include/sound/cs35l41.h
include/sound/intel-dsp-config.h
include/sound/intel-nhlt.h
include/sound/soc-acpi.h
include/sound/soc-card.h
include/sound/soc-component.h
include/sound/soc-dpcm.h
include/sound/soc.h
include/sound/sof.h
include/sound/sof/ext_manifest4.h [new file with mode: 0644]
include/sound/sof/ipc4/header.h [new file with mode: 0644]
include/sound/sof/stream.h
include/uapi/sound/intel/avs/tokens.h [new file with mode: 0644]
include/uapi/sound/sof/abi.h
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/amd/acp-da7219-max98357a.c
sound/soc/amd/acp-rt5645.c
sound/soc/amd/acp/Kconfig
sound/soc/amd/acp/acp-legacy-mach.c
sound/soc/amd/acp/acp-mach-common.c
sound/soc/amd/acp/acp-mach.h
sound/soc/amd/acp/acp-sof-mach.c
sound/soc/amd/acp3x-rt5682-max9836.c
sound/soc/amd/vangogh/acp5x-mach.c
sound/soc/amd/yc/acp6x-mach.c
sound/soc/amd/yc/pci-acp6x.c
sound/soc/atmel/Kconfig
sound/soc/atmel/atmel-classd.c
sound/soc/atmel/atmel-pdmic.c
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/au1x/Kconfig
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad193x-i2c.c
sound/soc/codecs/adau1372-i2c.c
sound/soc/codecs/adau1372.c
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1701.c
sound/soc/codecs/adau1761-i2c.c
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau1781-i2c.c
sound/soc/codecs/adau17x1.c
sound/soc/codecs/adau17x1.h
sound/soc/codecs/adau1977-i2c.c
sound/soc/codecs/adau7118-i2c.c
sound/soc/codecs/adav803.c
sound/soc/codecs/ak4118.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ak4613.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/ak4671.c
sound/soc/codecs/alc5623.c
sound/soc/codecs/alc5632.c
sound/soc/codecs/cros_ec_codec.c
sound/soc/codecs/cs35l32.c
sound/soc/codecs/cs35l33.c
sound/soc/codecs/cs35l34.c
sound/soc/codecs/cs35l35.c
sound/soc/codecs/cs35l36.c
sound/soc/codecs/cs35l41-i2c.c
sound/soc/codecs/cs35l41-lib.c
sound/soc/codecs/cs35l41.c
sound/soc/codecs/cs35l45-i2c.c [new file with mode: 0644]
sound/soc/codecs/cs35l45-spi.c [new file with mode: 0644]
sound/soc/codecs/cs35l45-tables.c [new file with mode: 0644]
sound/soc/codecs/cs35l45.c [new file with mode: 0644]
sound/soc/codecs/cs35l45.h [new file with mode: 0644]
sound/soc/codecs/cs4234.c
sound/soc/codecs/cs4265.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/cs4271-i2c.c
sound/soc/codecs/cs42l42.c
sound/soc/codecs/cs42l51-i2c.c
sound/soc/codecs/cs42l51.c
sound/soc/codecs/cs42l52.c
sound/soc/codecs/cs42l56.c
sound/soc/codecs/cs42l73.c
sound/soc/codecs/cs42xx8-i2c.c
sound/soc/codecs/cs43130.c
sound/soc/codecs/cs43130.h
sound/soc/codecs/cs4341.c
sound/soc/codecs/cs4349.c
sound/soc/codecs/cs53l30.c
sound/soc/codecs/cx2072x.c
sound/soc/codecs/da7210.c
sound/soc/codecs/da7213.c
sound/soc/codecs/da7218.c
sound/soc/codecs/da7219.c
sound/soc/codecs/da732x.c
sound/soc/codecs/da9055.c
sound/soc/codecs/dmic.c
sound/soc/codecs/es8316.c
sound/soc/codecs/es8328-i2c.c
sound/soc/codecs/hdac_hda.c
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/isabelle.c
sound/soc/codecs/lm4857.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/lochnagar-sc.c
sound/soc/codecs/lpass-macro-common.c
sound/soc/codecs/max9768.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98095.c
sound/soc/codecs/max98371.c
sound/soc/codecs/max98373-i2c.c
sound/soc/codecs/max98390.c
sound/soc/codecs/max98396.c [new file with mode: 0644]
sound/soc/codecs/max98396.h [new file with mode: 0644]
sound/soc/codecs/max9850.c
sound/soc/codecs/max98504.c
sound/soc/codecs/max98520.c
sound/soc/codecs/max9867.c
sound/soc/codecs/max9877.c
sound/soc/codecs/max98925.c
sound/soc/codecs/max98926.c
sound/soc/codecs/max98927.c
sound/soc/codecs/ml26124.c
sound/soc/codecs/mt6351.c
sound/soc/codecs/mt6358.c
sound/soc/codecs/mt6359.c
sound/soc/codecs/mt6660.c
sound/soc/codecs/nau8540.c
sound/soc/codecs/nau8810.c
sound/soc/codecs/nau8821.c
sound/soc/codecs/nau8822.c
sound/soc/codecs/nau8824.c
sound/soc/codecs/nau8825.c
sound/soc/codecs/pcm1681.c
sound/soc/codecs/pcm1789-i2c.c
sound/soc/codecs/pcm1789.c
sound/soc/codecs/pcm1789.h
sound/soc/codecs/pcm179x-i2c.c
sound/soc/codecs/pcm186x-i2c.c
sound/soc/codecs/pcm186x.c
sound/soc/codecs/pcm3060-i2c.c
sound/soc/codecs/pcm3060.c
sound/soc/codecs/pcm3168a-i2c.c
sound/soc/codecs/pcm512x-i2c.c
sound/soc/codecs/rk3328_codec.c
sound/soc/codecs/rt1011.c
sound/soc/codecs/rt1015.c
sound/soc/codecs/rt1016.c
sound/soc/codecs/rt1019.c
sound/soc/codecs/rt1305.c
sound/soc/codecs/rt1308-sdw.c
sound/soc/codecs/rt1308-sdw.h
sound/soc/codecs/rt1308.c
sound/soc/codecs/rt1316-sdw.c
sound/soc/codecs/rt274.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5514.c
sound/soc/codecs/rt5616.c
sound/soc/codecs/rt5631.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5651.c
sound/soc/codecs/rt5659.c
sound/soc/codecs/rt5660.c
sound/soc/codecs/rt5663.c
sound/soc/codecs/rt5665.c
sound/soc/codecs/rt5668.c
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5682-i2c.c
sound/soc/codecs/rt5682s.c
sound/soc/codecs/rt5682s.h
sound/soc/codecs/rt700.c
sound/soc/codecs/rt711-sdca.c
sound/soc/codecs/rt711.c
sound/soc/codecs/rt715-sdca-sdw.c
sound/soc/codecs/rt715-sdca.c
sound/soc/codecs/rt715.c
sound/soc/codecs/rt9120.c
sound/soc/codecs/sdw-mockup.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/ssm2518.c
sound/soc/codecs/ssm2602-i2c.c
sound/soc/codecs/ssm4567.c
sound/soc/codecs/sta32x.c
sound/soc/codecs/sta350.c
sound/soc/codecs/sta529.c
sound/soc/codecs/tas2552.c
sound/soc/codecs/tas2562.c
sound/soc/codecs/tas2764.c
sound/soc/codecs/tas2770.c
sound/soc/codecs/tas5086.c
sound/soc/codecs/tas571x.c
sound/soc/codecs/tas5720.c
sound/soc/codecs/tas6424.c
sound/soc/codecs/tda7419.c
sound/soc/codecs/tlv320adc3xxx.c
sound/soc/codecs/tlv320adcx140.c
sound/soc/codecs/tlv320aic23-i2c.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic32x4-i2c.c
sound/soc/codecs/tlv320aic3x-i2c.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/ts3a227e.c
sound/soc/codecs/tscs42xx.c
sound/soc/codecs/tscs454.c
sound/soc/codecs/uda1380.c
sound/soc/codecs/wcd9335.c
sound/soc/codecs/wcd934x.c
sound/soc/codecs/wcd938x.c
sound/soc/codecs/wm1250-ev1.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8711.c
sound/soc/codecs/wm8728.c
sound/soc/codecs/wm8731-i2c.c [new file with mode: 0644]
sound/soc/codecs/wm8731-spi.c [new file with mode: 0644]
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8731.h
sound/soc/codecs/wm8737.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8804-i2c.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8978.c
sound/soc/codecs/wm8983.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm8988.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8991.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8996.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9090.c
sound/soc/codecs/wsa881x.c
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_asrc_dma.c
sound/soc/fsl/fsl_easrc.h
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_micfil.c
sound/soc/fsl/fsl_micfil.h
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_sai.h
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-es8328.c
sound/soc/fsl/imx-hdmi.c
sound/soc/fsl/imx-pcm.h
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-ssi.h
sound/soc/generic/audio-graph-card2.c
sound/soc/generic/simple-card-utils.c
sound/soc/img/img-i2s-in.c
sound/soc/img/img-parallel-out.c
sound/soc/img/img-spdif-in.c
sound/soc/img/img-spdif-out.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst/sst.c
sound/soc/intel/atom/sst/sst_drv_interface.c
sound/soc/intel/avs/Makefile
sound/soc/intel/avs/apl.c [new file with mode: 0644]
sound/soc/intel/avs/avs.h
sound/soc/intel/avs/board_selection.c [new file with mode: 0644]
sound/soc/intel/avs/core.c
sound/soc/intel/avs/dsp.c
sound/soc/intel/avs/ipc.c
sound/soc/intel/avs/loader.c
sound/soc/intel/avs/messages.c
sound/soc/intel/avs/messages.h
sound/soc/intel/avs/path.c [new file with mode: 0644]
sound/soc/intel/avs/path.h [new file with mode: 0644]
sound/soc/intel/avs/pcm.c [new file with mode: 0644]
sound/soc/intel/avs/registers.h
sound/soc/intel/avs/skl.c [new file with mode: 0644]
sound/soc/intel/avs/topology.c [new file with mode: 0644]
sound/soc/intel/avs/topology.h [new file with mode: 0644]
sound/soc/intel/avs/trace.c [new file with mode: 0644]
sound/soc/intel/avs/trace.h [new file with mode: 0644]
sound/soc/intel/avs/utils.c
sound/soc/intel/boards/bdw-rt5650.c
sound/soc/intel/boards/bdw-rt5677.c
sound/soc/intel/boards/broadwell.c
sound/soc/intel/boards/bxt_da7219_max98357a.c
sound/soc/intel/boards/bxt_rt298.c
sound/soc/intel/boards/bytcht_cx2072x.c
sound/soc/intel/boards/bytcht_es8316.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/bytcr_rt5651.c
sound/soc/intel/boards/bytcr_wm5102.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_nau8824.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cht_bsw_rt5672.c
sound/soc/intel/boards/cml_rt1011_rt5682.c
sound/soc/intel/boards/glk_rt5682_max98357a.c
sound/soc/intel/boards/kbl_da7219_max98357a.c
sound/soc/intel/boards/kbl_da7219_max98927.c
sound/soc/intel/boards/kbl_rt5660.c
sound/soc/intel/boards/kbl_rt5663_max98927.c
sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
sound/soc/intel/boards/skl_hda_dsp_common.c
sound/soc/intel/boards/skl_nau88l25_max98357a.c
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
sound/soc/intel/boards/skl_rt286.c
sound/soc/intel/boards/sof_cs42l42.c
sound/soc/intel/boards/sof_da7219_max98373.c
sound/soc/intel/boards/sof_es8336.c
sound/soc/intel/boards/sof_nau8825.c
sound/soc/intel/boards/sof_realtek_common.c
sound/soc/intel/boards/sof_realtek_common.h
sound/soc/intel/boards/sof_rt5682.c
sound/soc/intel/boards/sof_sdw_rt5682.c
sound/soc/intel/boards/sof_sdw_rt700.c
sound/soc/intel/boards/sof_sdw_rt711.c
sound/soc/intel/boards/sof_sdw_rt711_sdca.c
sound/soc/intel/boards/sof_ssp_amp.c
sound/soc/intel/catpt/messages.h
sound/soc/intel/common/soc-acpi-intel-adl-match.c
sound/soc/mediatek/Kconfig
sound/soc/mediatek/mt2701/mt2701-wm8960.c
sound/soc/mediatek/mt8173/mt8173-max98090.c
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
sound/soc/mediatek/mt8173/mt8173-rt5650.c
sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
sound/soc/mediatek/mt8195/Makefile
sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c [deleted file]
sound/soc/mediatek/mt8195/mt8195-mt6359.c [moved from sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c with 78% similarity]
sound/soc/mxs/mxs-saif.c
sound/soc/pxa/Kconfig
sound/soc/pxa/hx4700.c
sound/soc/pxa/palm27x.c
sound/soc/pxa/ttc-dkb.c
sound/soc/pxa/z2.c
sound/soc/qcom/Kconfig
sound/soc/qcom/apq8016_sbc.c
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/sc7180.c
sound/soc/qcom/sc7280.c
sound/soc/qcom/sdm845.c
sound/soc/qcom/sm8250.c
sound/soc/rockchip/rk3288_hdmi_analog.c
sound/soc/rockchip/rk3399_gru_sound.c
sound/soc/rockchip/rockchip_max98090.c
sound/soc/rockchip/rockchip_rt5645.c
sound/soc/samsung/aries_wm8994.c
sound/soc/samsung/bells.c
sound/soc/samsung/h1940_uda1380.c
sound/soc/samsung/littlemill.c
sound/soc/samsung/lowland.c
sound/soc/samsung/midas_wm1811.c
sound/soc/samsung/rx1950_uda1380.c
sound/soc/samsung/smartq_wm8987.c
sound/soc/samsung/spdif.c
sound/soc/samsung/speyside.c
sound/soc/samsung/tobermory.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c
sound/soc/sh/rz-ssi.c
sound/soc/soc-card.c
sound/soc/soc-component.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/soc-utils-test.c [new file with mode: 0644]
sound/soc/soc-utils.c
sound/soc/sof/Makefile
sound/soc/sof/amd/acp-trace.c
sound/soc/sof/amd/acp.c
sound/soc/sof/amd/acp.h
sound/soc/sof/amd/pci-rn.c
sound/soc/sof/amd/renoir.c
sound/soc/sof/compress.c
sound/soc/sof/control.c
sound/soc/sof/core.c
sound/soc/sof/debug.c
sound/soc/sof/imx/imx8.c
sound/soc/sof/imx/imx8m.c
sound/soc/sof/intel/Kconfig
sound/soc/sof/intel/Makefile
sound/soc/sof/intel/apl.c
sound/soc/sof/intel/bdw.c
sound/soc/sof/intel/byt.c
sound/soc/sof/intel/cnl.c
sound/soc/sof/intel/hda-common-ops.c [new file with mode: 0644]
sound/soc/sof/intel/hda-dai.c
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda-ipc.c
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda-trace.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/icl.c
sound/soc/sof/intel/pci-apl.c
sound/soc/sof/intel/pci-cnl.c
sound/soc/sof/intel/pci-icl.c
sound/soc/sof/intel/pci-tgl.c
sound/soc/sof/intel/pci-tng.c
sound/soc/sof/intel/shim.h
sound/soc/sof/intel/tgl.c
sound/soc/sof/ipc.c
sound/soc/sof/ipc3-control.c
sound/soc/sof/ipc3-dtrace.c [new file with mode: 0644]
sound/soc/sof/ipc3-loader.c [new file with mode: 0644]
sound/soc/sof/ipc3-ops.h [deleted file]
sound/soc/sof/ipc3-pcm.c
sound/soc/sof/ipc3-priv.h [new file with mode: 0644]
sound/soc/sof/ipc3-topology.c
sound/soc/sof/ipc3.c
sound/soc/sof/ipc4-loader.c [new file with mode: 0644]
sound/soc/sof/ipc4-priv.h [new file with mode: 0644]
sound/soc/sof/ipc4.c [new file with mode: 0644]
sound/soc/sof/loader.c
sound/soc/sof/mediatek/Kconfig
sound/soc/sof/mediatek/Makefile
sound/soc/sof/mediatek/adsp_helper.h
sound/soc/sof/mediatek/mt8186/Makefile [new file with mode: 0644]
sound/soc/sof/mediatek/mt8186/mt8186-clk.c [new file with mode: 0644]
sound/soc/sof/mediatek/mt8186/mt8186-clk.h [new file with mode: 0644]
sound/soc/sof/mediatek/mt8186/mt8186-loader.c [new file with mode: 0644]
sound/soc/sof/mediatek/mt8186/mt8186.c [new file with mode: 0644]
sound/soc/sof/mediatek/mt8186/mt8186.h [new file with mode: 0644]
sound/soc/sof/mediatek/mt8195/mt8195.c
sound/soc/sof/mediatek/mtk-adsp-common.c [new file with mode: 0644]
sound/soc/sof/mediatek/mtk-adsp-common.h [new file with mode: 0644]
sound/soc/sof/ops.c
sound/soc/sof/ops.h
sound/soc/sof/pcm.c
sound/soc/sof/pm.c
sound/soc/sof/sof-acpi-dev.c
sound/soc/sof/sof-audio.c
sound/soc/sof/sof-audio.h
sound/soc/sof/sof-client-ipc-flood-test.c
sound/soc/sof/sof-client-ipc-msg-injector.c
sound/soc/sof/sof-client-probes.c
sound/soc/sof/sof-client.c
sound/soc/sof/sof-client.h
sound/soc/sof/sof-of-dev.c
sound/soc/sof/sof-of-dev.h
sound/soc/sof/sof-pci-dev.c
sound/soc/sof/sof-priv.h
sound/soc/sof/topology.c
sound/soc/sof/trace.c
sound/soc/tegra/Kconfig
sound/soc/tegra/Makefile
sound/soc/tegra/tegra186_asrc.c [new file with mode: 0644]
sound/soc/tegra/tegra186_asrc.h [new file with mode: 0644]
sound/soc/tegra/tegra210_ahub.c
sound/soc/tegra/tegra_asoc_machine.c
sound/soc/tegra/tegra_wm8903.c
sound/soc/ti/ams-delta.c
sound/soc/ti/davinci-mcasp.c
sound/soc/ti/j721e-evm.c
sound/soc/ti/omap-abe-twl6040.c
sound/soc/ti/omap-twl4030.c
sound/soc/ti/osk5912.c
sound/soc/ti/rx51.c
sound/soc/uniphier/aio-compress.c
sound/soc/ux500/mop500_ab8500.c

diff --git a/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml b/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml
new file mode 100644 (file)
index 0000000..b7e68b0
--- /dev/null
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dsp/mediatek,mt8195-dsp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek mt8195 DSP core
+
+maintainers:
+  - YC Hung <yc.hung@mediatek.com>
+
+description: |
+  Some boards from mt8195 contain a DSP core used for
+  advanced pre- and post- audio processing.
+
+properties:
+  compatible:
+    const: mediatek,mt8195-dsp
+
+  reg:
+    items:
+      - description: Address and size of the DSP Cfg registers
+      - description: Address and size of the DSP SRAM
+
+  reg-names:
+    items:
+      - const: cfg
+      - const: sram
+
+  clocks:
+    items:
+      - description: mux for audio dsp clock
+      - description: 26M clock
+      - description: mux for audio dsp local bus
+      - description: default audio dsp local bus clock source
+      - description: clock gate for audio dsp clock
+      - description: mux for audio dsp access external bus
+
+  clock-names:
+    items:
+      - const: adsp_sel
+      - const: clk26m_ck
+      - const: audio_local_bus
+      - const: mainpll_d7_d2
+      - const: scp_adsp_audiodsp
+      - const: audio_h
+
+  power-domains:
+    maxItems: 1
+
+  mboxes:
+    items:
+      - description: ipc reply between host and audio DSP.
+      - description: ipc request between host and audio DSP.
+
+  mbox-names:
+    items:
+      - const: mbox0
+      - const: mbox1
+
+  memory-region:
+    items:
+      - description: dma buffer between host and DSP.
+      - description: DSP system memory.
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - clocks
+  - clock-names
+  - memory-region
+  - power-domains
+  - mbox-names
+  - mboxes
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    dsp@10803000 {
+       compatible =  "mediatek,mt8195-dsp";
+       reg = <0x10803000  0x1000>,
+             <0x10840000  0x40000>;
+       reg-names = "cfg", "sram";
+       clocks = <&topckgen 10>, //CLK_TOP_ADSP
+                <&clk26m>,
+                <&topckgen 107>, //CLK_TOP_AUDIO_LOCAL_BUS
+                <&topckgen 136>, //CLK_TOP_MAINPLL_D7_D2
+                <&scp_adsp 0>, //CLK_SCP_ADSP_AUDIODSP
+                <&topckgen 34>; //CLK_TOP_AUDIO_H
+       clock-names = "adsp_sel",
+                     "clk26m_ck",
+                     "audio_local_bus",
+                     "mainpll_d7_d2",
+                     "scp_adsp_audiodsp",
+                     "audio_h";
+       memory-region = <&adsp_dma_mem_reserved>,
+                       <&adsp_mem_reserved>;
+       power-domains = <&spm 6>; //MT8195_POWER_DOMAIN_ADSP
+       mbox-names = "mbox0", "mbox1";
+       mboxes = <&adsp_mailbox0>, <&adsp_mailbox1>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/adi,max98396.yaml b/Documentation/devicetree/bindings/sound/adi,max98396.yaml
new file mode 100644 (file)
index 0000000..ec4c10c
--- /dev/null
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,max98396.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX98396 Speaker Amplifier Device Tree Bindings
+
+maintainers:
+  - Ryan Lee <ryans.lee@analog.com>
+
+description:
+  The MAX98396 is a mono Class-DG speaker amplifier with I/V sense.
+  The device provides a PCM interface for audio data and a standard
+  I2C interface for control data communication.
+  The MAX98397 is a variant of MAX98396 with wide input supply range.
+
+properties:
+  compatible:
+    enum:
+      - adi,max98396
+      - adi,max98397
+  reg:
+    maxItems: 1
+    description: I2C address of the device.
+
+  adi,vmon-slot-no:
+    description: slot number of the voltage sense monitor
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 0
+    maximum: 15
+    default: 0
+
+  adi,imon-slot-no:
+    description: slot number of the current sense monitor
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 0
+    maximum: 15
+    default: 0
+
+  adi,spkfb-slot-no:
+    description: slot number of speaker DSP monitor
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 0
+    maximum: 15
+    default: 0
+
+  adi,interleave-mode:
+    description:
+      For cases where a single combined channel for the I/V sense data
+      is not sufficient, the device can also be configured to share
+      a single data output channel on alternating frames.
+      In this configuration, the current and voltage data will be frame
+      interleaved on a single output channel.
+    type: boolean
+
+  reset-gpios:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        max98396: amplifier@39 {
+            compatible = "adi,max98396";
+            reg = <0x39>;
+            adi,vmon-slot-no = <0>;
+            adi,imon-slot-no = <1>;
+            reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
new file mode 100644 (file)
index 0000000..184a134
--- /dev/null
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/cirrus,cs35l45.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic CS35L45 Speaker Amplifier
+
+maintainers:
+  - Ricardo Rivera-Matos <rriveram@opensource.cirrus.com>
+  - Richard Fitzgerald <rf@opensource.cirrus.com>
+
+description: |
+  CS35L45 is a Boosted Mono Class D Amplifier with DSP
+  Speaker Protection and Adaptive Battery Management.
+
+properties:
+  compatible:
+    enum:
+      - cirrus,cs35l45
+
+  reg:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vdd-a-supply:
+    description: voltage regulator phandle for the VDD_A supply
+
+  vdd-batt-supply:
+    description: voltage regulator phandle for the VDD_BATT supply
+
+  spi-max-frequency:
+    maximum: 5000000
+
+  cirrus,asp-sdout-hiz-ctrl:
+    description:
+      Audio serial port SDOUT Hi-Z control. Sets the Hi-Z
+      configuration for SDOUT pin of amplifier. Logical OR of
+      CS35L45_ASP_TX_HIZ_xxx values.
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 0
+    maximum: 3
+    default: 2
+
+required:
+  - compatible
+  - reg
+  - "#sound-dai-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/sound/cs35l45.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        cs35l45: cs35l45@2 {
+          #sound-dai-cells = <1>;
+          compatible = "cirrus,cs35l45";
+          reg = <2>;
+          spi-max-frequency = <5000000>;
+          vdd-a-supply = <&dummy_vreg>;
+          vdd-batt-supply = <&dummy_vreg>;
+          reset-gpios = <&gpio 110 0>;
+          cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED |
+                                        CS35L45_ASP_TX_HIZ_DISABLED)>;
+        };
+    };
index 53e227b..1ea05d4 100644 (file)
@@ -6,6 +6,7 @@ microphone bitstream in a configurable output sampling rate.
 Required properties:
 
   - compatible         : Compatible list, contains "fsl,imx8mm-micfil"
+                         or "fsl,imx8mp-micfil"
 
   - reg                        : Offset and length of the register set for the device.
 
index fea9a1b..deaa688 100644 (file)
@@ -29,6 +29,9 @@ properties:
     minimum: 1
     maximum: 8388607
 
+  reset-gpios:
+    maxItems: 1
+
 required:
   - compatible
   - reg
@@ -37,6 +40,7 @@ additionalProperties: false
 
 examples:
   - |
+    #include <dt-bindings/gpio/gpio.h>
     i2c {
       #address-cells = <1>;
       #size-cells = <0>;
@@ -45,5 +49,6 @@ examples:
         reg = <0x38>;
         maxim,temperature_calib = <1024>;
         maxim,r0_calib = <100232>;
+        reset-gpios = <&gpio 9 GPIO_ACTIVE_LOW>;
       };
     };
index 5a5b765..4fa1799 100644 (file)
@@ -18,6 +18,7 @@ properties:
     enum:
       - mediatek,mt8192_mt6359_rt1015_rt5682
       - mediatek,mt8192_mt6359_rt1015p_rt5682
+      - mediatek,mt8192_mt6359_rt1015p_rt5682s
 
   mediatek,platform:
     $ref: "/schemas/types.yaml#/definitions/phandle"
@@ -27,11 +28,33 @@ properties:
     $ref: "/schemas/types.yaml#/definitions/phandle"
     description: The phandle of HDMI codec.
 
+  headset-codec:
+    type: object
+    properties:
+      sound-dai:
+        $ref: /schemas/types.yaml#/definitions/phandle
+    required:
+      - sound-dai
+
+  speaker-codecs:
+    type: object
+    properties:
+      sound-dai:
+        minItems: 1
+        maxItems: 2
+        items:
+          maxItems: 1
+        $ref: /schemas/types.yaml#/definitions/phandle-array
+    required:
+      - sound-dai
+
 additionalProperties: false
 
 required:
   - compatible
   - mediatek,platform
+  - headset-codec
+  - speaker-codecs
 
 examples:
   - |
@@ -44,6 +67,15 @@ examples:
                         "aud_clk_mosi_on";
         pinctrl-0 = <&aud_clk_mosi_off>;
         pinctrl-1 = <&aud_clk_mosi_on>;
+
+        headset-codec {
+            sound-dai = <&rt5682>;
+        };
+
+        speaker-codecs {
+            sound-dai = <&rt1015_l>,
+                        <&rt1015_r>;
+        };
     };
 
 ...
diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml
deleted file mode 100644 (file)
index cf6ad79..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/mt8195-mt6359-rt1011-rt5682.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Mediatek MT8195 with MT6359, RT1011 and RT5682 ASoC sound card driver
-
-maintainers:
-  - Trevor Wu <trevor.wu@mediatek.com>
-
-description:
-  This binding describes the MT8195 sound card with RT1011 and RT5682.
-
-properties:
-  compatible:
-    const: mediatek,mt8195_mt6359_rt1011_rt5682
-
-  model:
-    $ref: /schemas/types.yaml#/definitions/string
-    description: User specified audio sound card name
-
-  mediatek,platform:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
-    description: The phandle of MT8195 ASoC platform.
-
-  mediatek,dptx-codec:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
-    description: The phandle of MT8195 Display Port Tx codec node.
-
-  mediatek,hdmi-codec:
-    $ref: "/schemas/types.yaml#/definitions/phandle"
-    description: The phandle of MT8195 HDMI codec node.
-
-additionalProperties: false
-
-required:
-  - compatible
-  - mediatek,platform
-
-examples:
-  - |
-
-    sound: mt8195-sound {
-        compatible = "mediatek,mt8195_mt6359_rt1011_rt5682";
-        mediatek,platform = <&afe>;
-        pinctrl-names = "default";
-        pinctrl-0 = <&aud_pins_default>;
-    };
-
-...
@@ -1,10 +1,10 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/sound/mt8195-mt6359-rt1019-rt5682.yaml#
+$id: http://devicetree.org/schemas/sound/mt8195-mt6359.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Mediatek MT8195 with MT6359, RT1019 and RT5682 ASoC sound card driver
+title: MediaTek MT8195 ASoC sound card driver
 
 maintainers:
   - Trevor Wu <trevor.wu@mediatek.com>
@@ -14,7 +14,10 @@ description:
 
 properties:
   compatible:
-    const: mediatek,mt8195_mt6359_rt1019_rt5682
+    enum:
+      - mediatek,mt8195_mt6359_rt1019_rt5682
+      - mediatek,mt8195_mt6359_rt1011_rt5682
+      - mediatek,mt8195_mt6359_max98390_rt5682
 
   model:
     $ref: /schemas/types.yaml#/definitions/string
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml
new file mode 100644 (file)
index 0000000..520d0d0
--- /dev/null
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra186-asrc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra186 ASRC Device Tree Bindings
+
+description: |
+  Asynchronous Sample Rate Converter (ASRC) converts the sampling frequency
+  of the input signal from one frequency to another. It can handle over a
+  wide range of sample rate ratios (freq_in/freq_out) from 1:24 to 24:1.
+  ASRC has two modes of operation. One where ratio can be programmed in SW
+  and the other where it gets the information from ratio estimator module.
+
+  It supports  sample rate conversions in the range of 8 to 192 kHz and
+  supports 6 streams upto 12 total channels. The input data size can be
+  16, 24 and 32 bits.
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Mohan Kumar <mkumard@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+allOf:
+  - $ref: name-prefix.yaml#
+
+properties:
+  $nodename:
+    pattern: "^asrc@[0-9a-f]*$"
+
+  compatible:
+    oneOf:
+      - const: nvidia,tegra186-asrc
+      - items:
+          - enum:
+              - nvidia,tegra234-asrc
+              - nvidia,tegra194-asrc
+          - const: nvidia,tegra186-asrc
+
+  reg:
+    maxItems: 1
+
+  sound-name-prefix:
+    pattern: "^ASRC[1-9]$"
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    description: |
+      ASRC has seven input ports and six output ports. Accordingly ACIF
+      (Audio Client Interfaces) port nodes are defined to represent the
+      ASRC inputs (port 0 to 6) and outputs (port 7 to 12). These are
+      connected to corresponding ports on AHUB (Audio Hub). Additional
+      input (port 6) is for receiving ratio information from estimator.
+
+    patternProperties:
+      '^port@[0-6]':
+        $ref: audio-graph-port.yaml#
+        unevaluatedProperties: false
+        description: ASRC ACIF input ports
+      '^port@[7-9]|1[1-2]':
+        $ref: audio-graph-port.yaml#
+        unevaluatedProperties: false
+        description: ASRC ACIF output ports
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+
+    asrc@2910000 {
+        compatible = "nvidia,tegra186-asrc";
+        reg = <0x2910000 0x2000>;
+        sound-name-prefix = "ASRC1";
+    };
+
+...
index 4727f1e..6df6f85 100644 (file)
@@ -106,6 +106,10 @@ patternProperties:
     type: object
     $ref: nvidia,tegra210-mixer.yaml#
 
+  '^asrc@[0-9a-f]+$':
+    type: object
+    $ref: nvidia,tegra186-asrc.yaml#
+
 required:
   - compatible
   - reg
index 2c81efb..e9a5330 100644 (file)
@@ -38,8 +38,8 @@ properties:
     maxItems: 7
 
   clock-names:
-    minItems: 3
-    maxItems: 7
+    minItems: 1
+    maxItems: 10
 
   interrupts:
     minItems: 2
@@ -62,6 +62,9 @@ properties:
   power-domains:
     maxItems: 1
 
+  power-domain-names:
+    maxItems: 1
+
   '#sound-dai-cells':
     const: 1
 
@@ -192,15 +195,19 @@ allOf:
           oneOf:
             - items:   #for I2S
                 - const: aon_cc_audio_hm_h
+                - const: audio_cc_ext_mclk0
                 - const: core_cc_sysnoc_mport_core
+                - const: core_cc_ext_if0_ibit
                 - const: core_cc_ext_if1_ibit
             - items:   #for Soundwire
                 - const: aon_cc_audio_hm_h
+                - const: audio_cc_codec_mem
                 - const: audio_cc_codec_mem0
                 - const: audio_cc_codec_mem1
                 - const: audio_cc_codec_mem2
+                - const: aon_cc_va_mem0
             - items:   #for HDMI
-                - const: aon_cc_audio_hm_h
+                - const: core_cc_sysnoc_mport_core
 
         reg-names:
           anyOf:
@@ -228,6 +235,10 @@ allOf:
                 - const: lpass-irq-hdmi
                 - const: lpass-irq-vaif
                 - const: lpass-irq-rxtxif
+        power-domain-names:
+          allOf:
+            - items:
+                - const: lcx
 
       required:
         - iommus
index 6127df5..a6905bc 100644 (file)
@@ -28,12 +28,17 @@ properties:
     maxItems: 5
 
   clock-names:
-    items:
-      - const: mclk
-      - const: npl
-      - const: macro
-      - const: dcodec
-      - const: fsgen
+    oneOf:
+      - items:   #for ADSP based platforms
+          - const: mclk
+          - const: npl
+          - const: macro
+          - const: dcodec
+          - const: fsgen
+      - items:   #for ADSP bypass based platforms
+          - const: mclk
+          - const: npl
+          - const: fsgen
 
   clock-output-names:
     items:
index 3f0f99c..324595a 100644 (file)
@@ -28,12 +28,17 @@ properties:
     maxItems: 5
 
   clock-names:
-    items:
-      - const: mclk
-      - const: npl
-      - const: macro
-      - const: dcodec
-      - const: fsgen
+    oneOf:
+      - items:   #for ADSP based platforms
+          - const: mclk
+          - const: npl
+          - const: macro
+          - const: dcodec
+          - const: fsgen
+      - items:   #for ADSP bypass based platforms
+          - const: mclk
+          - const: npl
+          - const: fsgen
 
   clock-output-names:
     items:
index 9868a5e..7b4cc84 100644 (file)
@@ -28,10 +28,13 @@ properties:
     maxItems: 3
 
   clock-names:
-    items:
-      - const: mclk
-      - const: core
-      - const: dcodec
+    oneOf:
+      - items:   #for ADSP based platforms
+          - const: mclk
+          - const: core
+          - const: dcodec
+      - items:   #for ADSP bypass based platforms
+          - const: mclk
 
   clock-output-names:
     items:
index 7bf1a5f..5154719 100644 (file)
@@ -36,6 +36,9 @@ properties:
   vdd-io-supply:
     description: A reference to the 1.8V I/O supply
 
+  vdd-mic-bias-supply:
+    description: A reference to the 3.8V mic bias supply
+
   qcom,tx-device:
     $ref: /schemas/types.yaml#/definitions/phandle-array
     description: A reference to Soundwire tx device phandle
index fdb7f29..1d73204 100644 (file)
@@ -25,6 +25,9 @@ properties:
       0 means shut down; 1 means power on.
     maxItems: 1
 
+  "#sound-dai-cells":
+    const: 0
+
 required:
   - compatible
 
index cd8c53d..c5f2b8f 100644 (file)
@@ -46,6 +46,8 @@ Optional properties:
 
 - realtek,dmic-clk-driving-high : Set the high driving of the DMIC clock out.
 
+- #sound-dai-cells: Should be set to '<0>'.
+
 Pins on the device (for linking into audio routes) for RT5682:
 
   * DMIC L1
@@ -1,25 +1,26 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-# Copyright (C) 2020 Texas Instruments Incorporated
+# Copyright (C) 2020-2022 Texas Instruments Incorporated
 %YAML 1.2
 ---
-$id: "http://devicetree.org/schemas/sound/tas2764.yaml#"
+$id: "http://devicetree.org/schemas/sound/tas27xx.yaml#"
 $schema: "http://devicetree.org/meta-schemas/core.yaml#"
 
-title: Texas Instruments TAS2764 Smart PA
+title: Texas Instruments TAS2764/TAS2780 Smart PA
 
 maintainers:
-  - Dan Murphy <dmurphy@ti.com>
+  - Shenghao Ding <shenghao-ding@ti.com>
 
 description: |
-  The TAS2764 is a mono, digital input Class-D audio amplifier optimized for
-  efficiently driving high peak power into small loudspeakers.
-  Integrated speaker voltage and current sense provides for
-  real time monitoring of loudspeaker behavior.
+  The TAS2764/TAS2780 is a mono, digital input Class-D audio amplifier
+  optimized for efficiently driving high peak power into small
+  loudspeakers. Integrated speaker voltage and current sense provides
+  for real time monitoring of loudspeaker behavior.
 
 properties:
   compatible:
     enum:
       - ti,tas2764
+      - ti,tas2780
 
   reg:
     maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8731.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8731.yaml
new file mode 100644 (file)
index 0000000..e7220e8
--- /dev/null
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8731.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Wolfson Microelectromics WM8731 audio CODEC
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+description: |
+  Wolfson Microelectronics WM8731 audio CODEC
+
+  Pins on the device (for linking into audio routes):
+    * LOUT: Left Channel Line Output
+    * ROUT: Right Channel Line Output
+    * LHPOUT: Left Channel Headphone Output
+    * RHPOUT: Right Channel Headphone Output
+    * LLINEIN: Left Channel Line Input
+    * RLINEIN: Right Channel Line Input
+    * MICIN: Microphone Input
+
+properties:
+  compatible:
+    enum:
+      - wlf,wm8731
+
+  reg:
+    maxItems: 1
+
+  "#sound-dai-cells":
+    const: 0
+
+  clocks:
+    description: Clock provider for MCLK pin.
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: mclk
+
+  AVDD-supply:
+    description: Analog power supply regulator on the AVDD pin.
+
+  HPVDD-supply:
+    description: Headphone power supply regulator on the HPVDD pin.
+
+  DBVDD-supply:
+    description: Digital buffer supply regulator for the DBVDD pin.
+
+  DCVDD-supply:
+    description: Digital core supply regulator for the DCVDD pin.
+
+  spi-max-frequency: true
+
+additionalProperties: false
+
+required:
+  - reg
+  - compatible
+  - AVDD-supply
+  - HPVDD-supply
+  - DBVDD-supply
+  - DCVDD-supply
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        wm8731_i2c: codec@0 {
+            compatible = "wlf,wm8731";
+            reg = <0>;
+            spi-max-frequency = <12500000>;
+
+            AVDD-supply = <&avdd_reg>;
+            HPVDD-supply = <&hpvdd_reg>;
+            DCVDD-supply = <&dcvdd_reg>;
+            DBVDD-supply = <&dbvdd_reg>;
+        };
+    };
+  - |
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        wm8731_spi: codec@1b {
+            compatible = "wlf,wm8731";
+            reg = <0x1b>;
+
+            AVDD-supply = <&avdd_reg>;
+            HPVDD-supply = <&hpvdd_reg>;
+            DCVDD-supply = <&dcvdd_reg>;
+            DBVDD-supply = <&dbvdd_reg>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8940.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8940.yaml
new file mode 100644 (file)
index 0000000..8aadcbe
--- /dev/null
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wlf,wm8940.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Wolfson WM8940 Codec Device Tree Bindings
+
+maintainers:
+  - patches@opensource.cirrus.com
+
+properties:
+  '#sound-dai-cells':
+    const: 0
+
+  compatible:
+    const: wlf,wm8940
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 526000
+
+required:
+  - '#sound-dai-cells'
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@0 {
+            #sound-dai-cells = <0>;
+            compatible = "wlf,wm8940";
+            reg = <0>;
+            spi-max-frequency = <500000>;
+        };
+    };
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@1a {
+            #sound-dai-cells = <0>;
+            compatible = "wlf,wm8940";
+            reg = <0x1a>;
+        };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/wm8731.txt b/Documentation/devicetree/bindings/sound/wm8731.txt
deleted file mode 100644 (file)
index f660d9b..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-WM8731 audio CODEC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
-  - compatible : "wlf,wm8731"
-
-  - reg : the I2C address of the device for I2C, the chip select
-          number for SPI.
-
-Example:
-
-wm8731: codec@1a {
-       compatible = "wlf,wm8731";
-       reg = <0x1a>;
-};
-
-Available audio endpoints for an audio-routing table:
- * LOUT: Left Channel Line Output
- * ROUT: Right Channel Line Output
- * LHPOUT: Left Channel Headphone Output
- * RHPOUT: Right Channel Headphone Output
- * LLINEIN: Left Channel Line Input
- * RLINEIN: Right Channel Line Input
- * MICIN: Microphone Input
index 61d9f11..1567492 100644 (file)
@@ -4688,10 +4688,12 @@ CIRRUS LOGIC AUDIO CODEC DRIVERS
 M:     James Schulman <james.schulman@cirrus.com>
 M:     David Rhodes <david.rhodes@cirrus.com>
 M:     Lucas Tanure <tanureal@opensource.cirrus.com>
+M:     Richard Fitzgerald <rf@opensource.cirrus.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     patches@opensource.cirrus.com
 S:     Maintained
 F:     Documentation/devicetree/bindings/sound/cirrus,cs*
+F:     include/dt-bindings/sound/cs*
 F:     sound/pci/hda/cs*
 F:     sound/soc/codecs/cs*
 
index 70241ad..f8d3011 100644 (file)
@@ -211,7 +211,8 @@ CONFIG_SND_ATMEL_SOC_WM8904=m
 CONFIG_SND_AT91_SOC_SAM9X5_WM8731=m
 CONFIG_SND_KIRKWOOD_SOC=y
 CONFIG_SND_SOC_ALC5623=y
-CONFIG_SND_SOC_WM8731=y
+CONFIG_SND_SOC_WM8731_I2C=y
+CONFIG_SND_SOC_WM8731_SPI=y
 CONFIG_SND_SIMPLE_CARD=y
 CONFIG_HID_DRAGONRISE=y
 CONFIG_HID_GYRATION=y
index 2ddc31e..3bffe3e 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/of_dma.h>
 
 #include <asm/irq.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 #include "dmaengine.h"
 #define IMXDMA_MAX_CHAN_DESCRIPTORS    16
index 70c0aa9..95367a8 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
@@ -35,7 +36,7 @@
 #include <linux/workqueue.h>
 
 #include <asm/irq.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
@@ -73,6 +74,7 @@
 #define SDMA_CHNENBL0_IMX35    0x200
 #define SDMA_CHNENBL0_IMX31    0x080
 #define SDMA_CHNPRI_0          0x100
+#define SDMA_DONE0_CONFIG      0x1000
 
 /*
  * Buffer descriptor status values.
                                 BIT(DMA_MEM_TO_DEV) | \
                                 BIT(DMA_DEV_TO_DEV))
 
+#define SDMA_WATERMARK_LEVEL_N_FIFOS   GENMASK(15, 12)
+#define SDMA_WATERMARK_LEVEL_SW_DONE   BIT(23)
+
+#define SDMA_DONE0_CONFIG_DONE_SEL     BIT(7)
+#define SDMA_DONE0_CONFIG_DONE_DIS     BIT(6)
+
 /**
  * struct sdma_script_start_addrs - SDMA script start pointers
  *
@@ -441,6 +449,9 @@ struct sdma_channel {
        struct work_struct              terminate_worker;
        struct list_head                terminated;
        bool                            is_ram_script;
+       unsigned int                    n_fifos_src;
+       unsigned int                    n_fifos_dst;
+       bool                            sw_done;
 };
 
 #define IMX_DMA_SG_LOOP                BIT(0)
@@ -778,6 +789,14 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event)
        val = readl_relaxed(sdma->regs + chnenbl);
        __set_bit(channel, &val);
        writel_relaxed(val, sdma->regs + chnenbl);
+
+       /* Set SDMA_DONEx_CONFIG is sw_done enabled */
+       if (sdmac->sw_done) {
+               val = readl_relaxed(sdma->regs + SDMA_DONE0_CONFIG);
+               val |= SDMA_DONE0_CONFIG_DONE_SEL;
+               val &= ~SDMA_DONE0_CONFIG_DONE_DIS;
+               writel_relaxed(val, sdma->regs + SDMA_DONE0_CONFIG);
+       }
 }
 
 static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
@@ -940,7 +959,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
 /*
  * sets the pc of SDMA script according to the peripheral type
  */
-static void sdma_get_pc(struct sdma_channel *sdmac,
+static int sdma_get_pc(struct sdma_channel *sdmac,
                enum sdma_peripheral_type peripheral_type)
 {
        struct sdma_engine *sdma = sdmac->sdma;
@@ -1038,14 +1057,22 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
        case IMX_DMATYPE_IPU_MEMORY:
                emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr;
                break;
-       default:
+       case IMX_DMATYPE_MULTI_SAI:
+               per_2_emi = sdma->script_addrs->sai_2_mcu_addr;
+               emi_2_per = sdma->script_addrs->mcu_2_sai_addr;
                break;
+       default:
+               dev_err(sdma->dev, "Unsupported transfer type %d\n",
+                       peripheral_type);
+               return -EINVAL;
        }
 
        sdmac->pc_from_device = per_2_emi;
        sdmac->pc_to_device = emi_2_per;
        sdmac->device_to_device = per_2_per;
        sdmac->pc_to_pc = emi_2_emi;
+
+       return 0;
 }
 
 static int sdma_load_context(struct sdma_channel *sdmac)
@@ -1210,9 +1237,26 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
        sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT;
 }
 
+static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac)
+{
+       unsigned int n_fifos;
+
+       if (sdmac->sw_done)
+               sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE;
+
+       if (sdmac->direction == DMA_DEV_TO_MEM)
+               n_fifos = sdmac->n_fifos_src;
+       else
+               n_fifos = sdmac->n_fifos_dst;
+
+       sdmac->watermark_level |=
+                       FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos);
+}
+
 static int sdma_config_channel(struct dma_chan *chan)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
+       int ret;
 
        sdma_disable_channel(chan);
 
@@ -1233,7 +1277,9 @@ static int sdma_config_channel(struct dma_chan *chan)
                break;
        }
 
-       sdma_get_pc(sdmac, sdmac->peripheral_type);
+       ret = sdma_get_pc(sdmac, sdmac->peripheral_type);
+       if (ret)
+               return ret;
 
        if ((sdmac->peripheral_type != IMX_DMATYPE_MEMORY) &&
                        (sdmac->peripheral_type != IMX_DMATYPE_DSP)) {
@@ -1243,6 +1289,10 @@ static int sdma_config_channel(struct dma_chan *chan)
                            sdmac->peripheral_type == IMX_DMATYPE_ASRC)
                                sdma_set_watermarklevel_for_p2p(sdmac);
                } else {
+                       if (sdmac->peripheral_type ==
+                                       IMX_DMATYPE_MULTI_SAI)
+                               sdma_set_watermarklevel_for_sais(sdmac);
+
                        __set_bit(sdmac->event_id0, sdmac->event_mask);
                }
 
@@ -1349,7 +1399,9 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
                mem_data.dma_request2 = 0;
                data = &mem_data;
 
-               sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY);
+               ret = sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY);
+               if (ret)
+                       return ret;
        }
 
        switch (data->priority) {
@@ -1698,9 +1750,23 @@ static int sdma_config(struct dma_chan *chan,
                       struct dma_slave_config *dmaengine_cfg)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
+       struct sdma_engine *sdma = sdmac->sdma;
 
        memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg));
 
+       if (dmaengine_cfg->peripheral_config) {
+               struct sdma_peripheral_config *sdmacfg = dmaengine_cfg->peripheral_config;
+               if (dmaengine_cfg->peripheral_size != sizeof(struct sdma_peripheral_config)) {
+                       dev_err(sdma->dev, "Invalid peripheral size %zu, expected %zu\n",
+                               dmaengine_cfg->peripheral_size,
+                               sizeof(struct sdma_peripheral_config));
+                       return -EINVAL;
+               }
+               sdmac->n_fifos_src = sdmacfg->n_fifos_src;
+               sdmac->n_fifos_dst = sdmacfg->n_fifos_dst;
+               sdmac->sw_done = sdmacfg->sw_done;
+       }
+
        /* Set ENBLn earlier to make sure dma request triggered after that */
        if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events)
                return -EINVAL;
index e5cfb01..f7bc830 100644 (file)
@@ -203,6 +203,15 @@ config INTEL_STRATIX10_RSU
 
          Say Y here if you want Intel RSU support.
 
+config MTK_ADSP_IPC
+       tristate "MTK ADSP IPC Protocol driver"
+       depends on MTK_ADSP_MBOX
+       help
+         Say yes here to add support for the MediaTek ADSP IPC
+         between host AP (Linux) and the firmware running on ADSP.
+         ADSP exists on some mtk processors.
+         Client might use shared memory to exchange information with ADSP.
+
 config QCOM_SCM
        tristate
 
index 4e58cb4..1be0e82 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_INTEL_STRATIX10_RSU)     += stratix10-rsu.o
 obj-$(CONFIG_ISCSI_IBFT_FIND)  += iscsi_ibft_find.o
 obj-$(CONFIG_ISCSI_IBFT)       += iscsi_ibft.o
 obj-$(CONFIG_FIRMWARE_MEMMAP)  += memmap.o
+obj-$(CONFIG_MTK_ADSP_IPC)     += mtk-adsp-ipc.o
 obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 obj-$(CONFIG_FW_CFG_SYSFS)     += qemu_fw_cfg.o
 obj-$(CONFIG_QCOM_SCM)         += qcom-scm.o
diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c
new file mode 100644 (file)
index 0000000..cb255a9
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ * Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+ */
+
+#include <linux/firmware/mediatek/mtk-adsp-ipc.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * mtk_adsp_ipc_send - send ipc cmd to MTK ADSP
+ *
+ * @ipc: ADSP IPC handle
+ * @idx: index of the mailbox channel
+ * @msg: IPC cmd (reply or request)
+ *
+ * Returns zero for success from mbox_send_message
+ * negative value for error
+ */
+int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t msg)
+{
+       struct mtk_adsp_chan *adsp_chan;
+       int ret;
+
+       if (idx >= MTK_ADSP_MBOX_NUM)
+               return -EINVAL;
+
+       adsp_chan = &ipc->chans[idx];
+       ret = mbox_send_message(adsp_chan->ch, &msg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_adsp_ipc_send);
+
+/*
+ * mtk_adsp_ipc_recv - recv callback used by MTK ADSP mailbox
+ *
+ * @c: mbox client
+ * @msg: message received
+ *
+ * Users of ADSP IPC will need to privde handle_reply and handle_request
+ * callbacks.
+ */
+static void mtk_adsp_ipc_recv(struct mbox_client *c, void *msg)
+{
+       struct mtk_adsp_chan *chan = container_of(c, struct mtk_adsp_chan, cl);
+       struct device *dev = c->dev;
+
+       switch (chan->idx) {
+       case MTK_ADSP_MBOX_REPLY:
+               chan->ipc->ops->handle_reply(chan->ipc);
+               break;
+       case MTK_ADSP_MBOX_REQUEST:
+               chan->ipc->ops->handle_request(chan->ipc);
+               break;
+       default:
+               dev_err(dev, "wrong mbox chan %d\n", chan->idx);
+               break;
+       }
+}
+
+static int mtk_adsp_ipc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mtk_adsp_ipc *adsp_ipc;
+       struct mtk_adsp_chan *adsp_chan;
+       struct mbox_client *cl;
+       char *chan_name;
+       int ret;
+       int i, j;
+
+       device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+       adsp_ipc = devm_kzalloc(dev, sizeof(*adsp_ipc), GFP_KERNEL);
+       if (!adsp_ipc)
+               return -ENOMEM;
+
+       for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) {
+               chan_name = kasprintf(GFP_KERNEL, "mbox%d", i);
+               if (!chan_name) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               adsp_chan = &adsp_ipc->chans[i];
+               cl = &adsp_chan->cl;
+               cl->dev = dev->parent;
+               cl->tx_block = false;
+               cl->knows_txdone = false;
+               cl->tx_prepare = NULL;
+               cl->rx_callback = mtk_adsp_ipc_recv;
+
+               adsp_chan->ipc = adsp_ipc;
+               adsp_chan->idx = i;
+               adsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
+               if (IS_ERR(adsp_chan->ch)) {
+                       ret = PTR_ERR(adsp_chan->ch);
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dev, "Failed to request mbox chan %d ret %d\n",
+                                       i, ret);
+                       goto out_free;
+               }
+
+               dev_dbg(dev, "request mbox chan %s\n", chan_name);
+               kfree(chan_name);
+       }
+
+       adsp_ipc->dev = dev;
+       dev_set_drvdata(dev, adsp_ipc);
+       dev_dbg(dev, "MTK ADSP IPC initialized\n");
+
+       return 0;
+
+out_free:
+       kfree(chan_name);
+out:
+       for (j = 0; j < i; j++) {
+               adsp_chan = &adsp_ipc->chans[j];
+               mbox_free_channel(adsp_chan->ch);
+       }
+
+       return ret;
+}
+
+static int mtk_adsp_ipc_remove(struct platform_device *pdev)
+{
+       struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev);
+       struct mtk_adsp_chan *adsp_chan;
+       int i;
+
+       for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) {
+               adsp_chan = &adsp_ipc->chans[i];
+               mbox_free_channel(adsp_chan->ch);
+       }
+
+       return 0;
+}
+
+static struct platform_driver mtk_adsp_ipc_driver = {
+       .driver = {
+               .name = "mtk-adsp-ipc",
+       },
+       .probe = mtk_adsp_ipc_probe,
+       .remove = mtk_adsp_ipc_remove,
+};
+builtin_platform_driver(mtk_adsp_ipc_driver);
+
+MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>");
+MODULE_DESCRIPTION("MTK ADSP IPC Driver");
+MODULE_LICENSE("GPL");
index 40b6878..de04b5a 100644 (file)
@@ -39,7 +39,7 @@
 #include <asm/irq.h>
 #include <linux/platform_data/mmc-mxcmmc.h>
 
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 #define DRIVER_NAME "mxc-mmc"
 #define MXCMCI_TIMEOUT_MS 10000
index 4c60129..19b1f3d 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
index b2dd0a4..a944c78 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/of_device.h>
 #include <linux/property.h>
 
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 #define DRIVER_NAME "spi_imx"
 
index b589cf6..db0b600 100644 (file)
@@ -702,8 +702,9 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module,
 
        headset->pin = module->jack_name;
        headset->mask = module->jack_mask;
-       ret = snd_soc_card_jack_new(card, module->jack_name, module->jack_mask,
-                                   &module->headset.jack, headset, 1);
+       ret = snd_soc_card_jack_new_pins(card, module->jack_name,
+                                        module->jack_mask,
+                                        &module->headset.jack, headset, 1);
        if (ret) {
                dev_err(module->dev, "Failed to create new jack\n");
                return ret;
@@ -725,9 +726,10 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module,
 
        button->pin = module->button_name;
        button->mask = module->button_mask;
-       ret = snd_soc_card_jack_new(card, module->button_name,
-                                   module->button_mask, &module->button.jack,
-                                   button, 1);
+       ret = snd_soc_card_jack_new_pins(card, module->button_name,
+                                        module->button_mask,
+                                        &module->button.jack,
+                                        button, 1);
        if (ret) {
                dev_err(module->dev, "Failed to create button jack\n");
                goto free_jacks;
index fd38e6e..f8b5400 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/dma-mapping.h>
 
 #include <asm/irq.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 #include "serial_mctrl_gpio.h"
 
index fabb271..b945b68 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/dma/ipu-dma.h>
 #include <linux/backlight.h>
 
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <linux/platform_data/video-mx3fb.h>
 
 #include <asm/io.h>
diff --git a/include/dt-bindings/sound/cs35l45.h b/include/dt-bindings/sound/cs35l45.h
new file mode 100644 (file)
index 0000000..076da4b
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * cs35l45.h -- CS35L45 ALSA SoC audio driver DT bindings header
+ *
+ * Copyright 2022 Cirrus Logic, Inc.
+ */
+
+#ifndef DT_CS35L45_H
+#define DT_CS35L45_H
+
+/*
+ * cirrus,asp-sdout-hiz-ctrl
+ *
+ * TX_HIZ_UNUSED:   TX pin high-impedance during unused slots.
+ * TX_HIZ_DISABLED: TX pin high-impedance when all channels disabled.
+ */
+#define CS35L45_ASP_TX_HIZ_UNUSED      0x1
+#define CS35L45_ASP_TX_HIZ_DISABLED    0x2
+
+#endif /* DT_CS35L45_H */
similarity index 67%
rename from include/linux/platform_data/dma-imx.h
rename to include/linux/dma/imx-dma.h
index 281adbb..8887762 100644 (file)
@@ -3,8 +3,8 @@
  * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
  */
 
-#ifndef __ASM_ARCH_MXC_DMA_H__
-#define __ASM_ARCH_MXC_DMA_H__
+#ifndef __LINUX_DMA_IMX_H
+#define __LINUX_DMA_IMX_H
 
 #include <linux/scatterlist.h>
 #include <linux/device.h>
@@ -39,6 +39,7 @@ enum sdma_peripheral_type {
        IMX_DMATYPE_SSI_DUAL,   /* SSI Dual FIFO */
        IMX_DMATYPE_ASRC_SP,    /* Shared ASRC */
        IMX_DMATYPE_SAI,        /* SAI */
+       IMX_DMATYPE_MULTI_SAI,  /* MULTI FIFOs For Audio */
 };
 
 enum imx_dma_prio {
@@ -65,4 +66,23 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
                !strcmp(chan->device->dev->driver->name, "imx-dma");
 }
 
-#endif
+/**
+ * struct sdma_peripheral_config - SDMA config for audio
+ * @n_fifos_src: Number of FIFOs for recording
+ * @n_fifos_dst: Number of FIFOs for playback
+ * @sw_done: Use software done. Needed for PDM (micfil)
+ *
+ * Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO
+ * registers. For multichannel recording/playback the SAI/micfil have
+ * one FIFO register per channel and the SDMA engine has to read/write
+ * the next channel from/to the next register and wrap around to the
+ * first register when all channels are handled. The number of active
+ * channels must be communicated to the SDMA engine using this struct.
+ */
+struct sdma_peripheral_config {
+       int n_fifos_src;
+       int n_fifos_dst;
+       bool sw_done;
+};
+
+#endif /* __LINUX_DMA_IMX_H */
index 38b4da3..3005570 100644 (file)
@@ -68,36 +68,36 @@ struct cs_dsp_alg_region {
 
 /**
  * struct cs_dsp_coeff_ctl - Describes a coefficient control
+ * @list:              List node for internal use
+ * @dsp:               DSP instance associated with this control
+ * @cache:             Cached value of the control
  * @fw_name:           Name of the firmware
  * @subname:           Name of the control parsed from the WMFW
  * @subname_len:       Length of subname
- * @alg_region:                Logical region associated with this control
- * @dsp:               DSP instance associated with this control
- * @enabled:           Flag indicating whether control is enabled
- * @list:              List node for internal use
- * @cache:             Cached value of the control
  * @offset:            Offset of control within alg_region in words
  * @len:               Length of the cached value in bytes
- * @set:               Flag indicating the value has been written by the user
- * @flags:             Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h
  * @type:              One of the WMFW_CTL_TYPE_ control types defined in wmfw.h
+ * @flags:             Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h
+ * @set:               Flag indicating the value has been written by the user
+ * @enabled:           Flag indicating whether control is enabled
+ * @alg_region:                Logical region associated with this control
  * @priv:              For use by the client
  */
 struct cs_dsp_coeff_ctl {
+       struct list_head list;
+       struct cs_dsp *dsp;
+       void *cache;
        const char *fw_name;
        /* Subname is needed to match with firmware */
        const char *subname;
        unsigned int subname_len;
-       struct cs_dsp_alg_region alg_region;
-       struct cs_dsp *dsp;
-       unsigned int enabled:1;
-       struct list_head list;
-       void *cache;
        unsigned int offset;
        size_t len;
-       unsigned int set:1;
-       unsigned int flags;
        unsigned int type;
+       unsigned int flags;
+       unsigned int set:1;
+       unsigned int enabled:1;
+       struct cs_dsp_alg_region alg_region;
 
        void *priv;
 };
diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h
new file mode 100644 (file)
index 0000000..28fd313
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef MTK_ADSP_IPC_H
+#define MTK_ADSP_IPC_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+
+#define MTK_ADSP_IPC_REQ 0
+#define MTK_ADSP_IPC_RSP 1
+#define MTK_ADSP_IPC_OP_REQ 0x1
+#define MTK_ADSP_IPC_OP_RSP 0x2
+
+enum {
+       MTK_ADSP_MBOX_REPLY,
+       MTK_ADSP_MBOX_REQUEST,
+       MTK_ADSP_MBOX_NUM,
+};
+
+struct mtk_adsp_ipc;
+
+struct mtk_adsp_ipc_ops {
+       void (*handle_reply)(struct mtk_adsp_ipc *ipc);
+       void (*handle_request)(struct mtk_adsp_ipc *ipc);
+};
+
+struct mtk_adsp_chan {
+       struct mtk_adsp_ipc *ipc;
+       struct mbox_client cl;
+       struct mbox_chan *ch;
+       char *name;
+       int idx;
+};
+
+struct mtk_adsp_ipc {
+       struct mtk_adsp_chan chans[MTK_ADSP_MBOX_NUM];
+       struct device *dev;
+       struct mtk_adsp_ipc_ops *ops;
+       void *private_data;
+};
+
+static inline void mtk_adsp_ipc_set_data(struct mtk_adsp_ipc *ipc, void *data)
+{
+       if (!ipc)
+               return;
+
+       ipc->private_data = data;
+}
+
+static inline void *mtk_adsp_ipc_get_data(struct mtk_adsp_ipc *ipc)
+{
+       if (!ipc)
+               return NULL;
+
+       return ipc->private_data;
+}
+
+int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t op);
+
+#endif /* MTK_ADSP_IPC_H */
index dd70fb8..8972fa6 100644 (file)
 
 #define CS35L41_MAX_CACHE_REG          36
 #define CS35L41_OTP_SIZE_WORDS         32
-#define CS35L41_NUM_OTP_ELEM           100
 
 #define CS35L41_NUM_SUPPLIES            2
 
index d460907..34c9759 100644 (file)
@@ -15,7 +15,8 @@ enum {
        SND_INTEL_DSP_DRIVER_LEGACY,
        SND_INTEL_DSP_DRIVER_SST,
        SND_INTEL_DSP_DRIVER_SOF,
-       SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_SOF
+       SND_INTEL_DSP_DRIVER_AVS,
+       SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_AVS
 };
 
 #if IS_ENABLED(CONFIG_SND_INTEL_DSP_CONFIG)
index 6fb2d5e..3d5cf20 100644 (file)
@@ -25,8 +25,6 @@ enum nhlt_device_type {
        NHLT_DEVICE_INVALID
 };
 
-#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
-
 struct wav_fmt {
        u16 fmt_tag;
        u16 channels;
@@ -126,6 +124,8 @@ enum {
        NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
 };
 
+#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
+
 struct nhlt_acpi_table *intel_nhlt_init(struct device *dev);
 
 void intel_nhlt_free(struct nhlt_acpi_table *addr);
@@ -143,8 +143,6 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
 
 #else
 
-struct nhlt_acpi_table;
-
 static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
 {
        return NULL;
index d33cf8d..b38fd25 100644 (file)
@@ -156,6 +156,7 @@ struct snd_soc_acpi_link_adr {
  * @links: array of link _ADR descriptors, null terminated.
  * @drv_name: machine driver name
  * @fw_filename: firmware file name. Used when SOF is not enabled.
+ * @tplg_filename: topology file name. Used when SOF is not enabled.
  * @board: board name
  * @machine_quirk: pointer to quirk, usually based on DMI information when
  * ACPI ID alone is not sufficient, wrong or misleading
@@ -174,6 +175,7 @@ struct snd_soc_acpi_mach {
        const struct snd_soc_acpi_link_adr *links;
        const char *drv_name;
        const char *fw_filename;
+       const char *tplg_filename;
        const char *board;
        struct snd_soc_acpi_mach * (*machine_quirk)(void *arg);
        const void *quirk_data;
index 4f2cc4f..df08573 100644 (file)
@@ -16,8 +16,11 @@ enum snd_soc_card_subclass {
 struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
                                               const char *name);
 int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
-                         struct snd_soc_jack *jack,
-                         struct snd_soc_jack_pin *pins, unsigned int num_pins);
+                         struct snd_soc_jack *jack);
+int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
+                              int type, struct snd_soc_jack *jack,
+                              struct snd_soc_jack_pin *pins,
+                              unsigned int num_pins);
 
 int snd_soc_card_suspend_pre(struct snd_soc_card *card);
 int snd_soc_card_suspend_post(struct snd_soc_card *card);
index 766dc6f..5a764c3 100644 (file)
@@ -169,6 +169,15 @@ struct snd_soc_component_driver {
        unsigned int idle_bias_on:1;
        unsigned int suspend_bias_off:1;
        unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
+       /*
+        * Indicates that the component does not care about the endianness of
+        * PCM audio data and the core will ensure that both LE and BE variants
+        * of each used format are present. Typically this is because the
+        * component sits behind a bus that abstracts away the endian of the
+        * original data, ie. one for which the transmission endian is defined
+        * (I2S/SLIMbus/SoundWire), or the concept of endian doesn't exist (PDM,
+        * analogue).
+        */
        unsigned int endianness:1;
        unsigned int non_legacy_dai_naming:1;
 
index 75b92d8..5b689c6 100644 (file)
@@ -103,6 +103,8 @@ struct snd_soc_dpcm_runtime {
        int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
 
        int be_start; /* refcount protected by BE stream pcm lock */
+       int be_pause; /* refcount protected by BE stream pcm lock */
+       bool fe_pause; /* used to track STOP after PAUSE */
 };
 
 #define for_each_dpcm_fe(be, stream, _dpcm)                            \
index 7a1650b..f20f5f8 100644 (file)
        .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
        .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \
                                            xmin, xmax, xsign_bit, xinvert) }
+#define SOC_SINGLE_S_TLV(xname, xreg, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) \
+       SOC_DOUBLE_R_S_TLV(xname, xreg, xreg, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array)
 #define SOC_SINGLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
 {      .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
                                            xmax, xinvert) }
+#define SOC_DOUBLE_R_S_EXT_TLV(xname, reg_left, reg_right, xshift, xmin, xmax, \
+                              xsign_bit, xinvert, xhandler_get, xhandler_put, \
+                              tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \
+                                             xmin, xmax, xsign_bit, xinvert) }
+#define SOC_SINGLE_S_EXT_TLV(xname, xreg, xshift, xmin, xmax, \
+                            xsign_bit, xinvert, xhandler_get, xhandler_put, \
+                            tlv_array) \
+       SOC_DOUBLE_R_S_EXT_TLV(xname, xreg, xreg, xshift, xmin, xmax, \
+                              xsign_bit, xinvert, xhandler_get, xhandler_put, \
+                              tlv_array)
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_bool_ext, \
@@ -485,6 +504,8 @@ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
 int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
 int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots);
 int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
+int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params,
+                              int tdm_width, int tdm_slots, int slot_multiple);
 
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -1238,7 +1259,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname);
 int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname);
 
-unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt);
+unsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt);
 unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame);
 
 unsigned int snd_soc_daifmt_parse_format(struct device_node *np, const char *prefix);
@@ -1263,6 +1284,10 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev,
                                   struct device_node *of_node,
                                   struct snd_soc_dai_link *dai_link);
 void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link);
+int snd_soc_of_get_dai_link_cpus(struct device *dev,
+                                struct device_node *of_node,
+                                struct snd_soc_dai_link *dai_link);
+void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link);
 
 int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
                            struct snd_soc_dai_link *dai_link);
index 7cdfc95..1a82a0d 100644 (file)
@@ -16,6 +16,7 @@
 #include <sound/soc-acpi.h>
 
 struct snd_sof_dsp_ops;
+struct snd_sof_dev;
 
 /**
  * enum sof_fw_state - DSP firmware state definitions
@@ -47,6 +48,13 @@ enum sof_dsp_power_states {
        SOF_DSP_PM_D3,
 };
 
+/* Definitions for multiple IPCs */
+enum sof_ipc_type {
+       SOF_IPC,
+       SOF_INTEL_IPC4,
+       SOF_IPC_TYPE_COUNT
+};
+
 /*
  * SOF Platform data.
  */
@@ -83,6 +91,8 @@ struct snd_sof_pdata {
        const struct snd_soc_acpi_mach *machine;
 
        void *hw_pdata;
+
+       enum sof_ipc_type ipc_type;
 };
 
 /*
@@ -115,14 +125,19 @@ struct sof_dev_desc {
        /* defaults for no codec mode */
        const char *nocodec_tplg_filename;
 
+       /* information on supported IPCs */
+       unsigned int ipc_supported_mask;
+       enum sof_ipc_type ipc_default;
+
        /* defaults paths for firmware and topology files */
-       const char *default_fw_path;
-       const char *default_tplg_path;
+       const char *default_fw_path[SOF_IPC_TYPE_COUNT];
+       const char *default_tplg_path[SOF_IPC_TYPE_COUNT];
 
        /* default firmware name */
-       const char *default_fw_filename;
+       const char *default_fw_filename[SOF_IPC_TYPE_COUNT];
 
-       const struct snd_sof_dsp_ops *ops;
+       struct snd_sof_dsp_ops *ops;
+       int (*ops_init)(struct snd_sof_dev *sdev);
 };
 
 int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd);
diff --git a/include/sound/sof/ext_manifest4.h b/include/sound/sof/ext_manifest4.h
new file mode 100644 (file)
index 0000000..ec97edc
--- /dev/null
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+/*
+ * Extended manifest is a place to store metadata about firmware, known during
+ * compilation time - for example firmware version or used compiler.
+ * Given information are read on host side before firmware startup.
+ * This part of output binary is not signed.
+ */
+
+#ifndef __SOF_FIRMWARE_EXT_MANIFEST4_H__
+#define __SOF_FIRMWARE_EXT_MANIFEST4_H__
+
+#include <linux/uuid.h>
+
+/* In ASCII  $AE1 */
+#define SOF_EXT_MAN4_MAGIC_NUMBER      0x31454124
+
+#define MAX_MODULE_NAME_LEN            8
+#define MAX_FW_BINARY_NAME             8
+#define DEFAULT_HASH_SHA256_LEN                32
+#define SOF_MAN4_FW_HDR_OFFSET         0x2000
+#define SOF_MAN4_FW_HDR_OFFSET_CAVS_1_5        0x284
+
+/*********************************************************************
+ *     extended manifest               (struct sof_ext_manifest4_hdr)
+ *-------------------
+ *     css_manifest hdr
+ *-------------------
+ *     offset reserved for future
+ *-------------------
+ *     fw_hdr                          (struct sof_man4_fw_binary_header)
+ *-------------------
+ *     module_entry[0]                 (struct sof_man4_module)
+ *-------------------
+ *     module_entry[1]
+ *-------------------
+ *     ...
+ *-------------------
+ *     module_entry[n]
+ *-------------------
+ *     module_config[0]                (struct sof_man4_module_config)
+ *-------------------
+ *     module_config[1]
+ *-------------------
+ *     ...
+ *-------------------
+ *     module_config[m]
+ *-------------------
+ *     FW content
+ *-------------------
+ *********************************************************************/
+
+struct sof_ext_manifest4_hdr {
+       uint32_t id;
+       uint32_t len; /* length of extension manifest */
+       uint16_t version_major; /* header version */
+       uint16_t version_minor;
+       uint32_t num_module_entries;
+} __packed;
+
+struct sof_man4_fw_binary_header {
+       /* This part must be unchanged to be backward compatible with SPT-LP ROM */
+       uint32_t id;
+       uint32_t len; /* sizeof(sof_man4_fw_binary_header) in bytes */
+       uint8_t name[MAX_FW_BINARY_NAME];
+       uint32_t preload_page_count; /* number of pages of preloaded image */
+       uint32_t fw_image_flags;
+       uint32_t feature_mask;
+       uint16_t major_version; /* Firmware version */
+       uint16_t minor_version;
+       uint16_t hotfix_version;
+       uint16_t build_version;
+       uint32_t num_module_entries;
+
+       /* This part may change to contain any additional data for BaseFw that is skipped by ROM */
+       uint32_t hw_buf_base_addr;
+       uint32_t hw_buf_length;
+       uint32_t load_offset; /* This value is used by ROM */
+} __packed;
+
+struct sof_man4_segment_desc {
+       uint32_t flags;
+       uint32_t v_base_addr;
+       uint32_t file_offset;
+} __packed;
+
+struct sof_man4_module {
+       uint32_t id;
+       uint8_t name[MAX_MODULE_NAME_LEN];
+       guid_t uuid;
+       uint32_t type;
+       uint8_t hash[DEFAULT_HASH_SHA256_LEN];
+       uint32_t entry_point;
+       uint16_t cfg_offset;
+       uint16_t cfg_count;
+       uint32_t affinity_mask;
+       uint16_t instance_max_count;
+       uint16_t instance_stack_size;
+       struct sof_man4_segment_desc    segments[3];
+} __packed;
+
+struct sof_man4_module_config {
+       uint32_t par[4];        /* module parameters */
+       uint32_t is_bytes;      /* actual size of instance .bss (bytes) */
+       uint32_t cps;           /* cycles per second */
+       uint32_t ibs;           /* input buffer size (bytes) */
+       uint32_t obs;           /* output buffer size (bytes) */
+       uint32_t module_flags;  /* flags, reserved for future use */
+       uint32_t cpc;           /* cycles per single run */
+       uint32_t obls;          /* output block size, reserved for future use */
+} __packed;
+
+#endif /* __SOF_FIRMWARE_EXT_MANIFEST4_H__ */
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
new file mode 100644 (file)
index 0000000..b8b8e5b
--- /dev/null
@@ -0,0 +1,460 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __INCLUDE_SOUND_SOF_IPC4_HEADER_H__
+#define __INCLUDE_SOUND_SOF_IPC4_HEADER_H__
+
+#include <linux/types.h>
+#include <uapi/sound/sof/abi.h>
+
+/* maximum message size for mailbox Tx/Rx */
+#define SOF_IPC4_MSG_MAX_SIZE                  4096
+
+/** \addtogroup sof_uapi uAPI
+ *  SOF uAPI specification.
+ *  @{
+ */
+
+/**
+ * struct sof_ipc4_msg - Placeholder of an IPC4 message
+ * @header_u64:                IPC4 header as single u64 number
+ * @primary:           Primary, mandatory part of the header
+ * @extension:         Extended part of the header, if not used it should be
+ *                     set to 0
+ * @data_size:         Size of data in bytes pointed by @data_ptr
+ * @data_ptr:          Pointer to the optional payload of a message
+ */
+struct sof_ipc4_msg {
+       union {
+               u64 header_u64;
+               struct {
+                       u32 primary;
+                       u32 extension;
+               };
+       };
+
+       size_t data_size;
+       void *data_ptr;
+};
+
+/**
+ * struct sof_ipc4_tuple - Generic type/ID and parameter tuple
+ * @type:              type/ID
+ * @size:              size of the @value array in bytes
+ * @value:             value for the given type
+ */
+struct sof_ipc4_tuple {
+       uint32_t type;
+       uint32_t size;
+       uint32_t value[];
+} __packed;
+
+/*
+ * IPC4 messages have two 32 bit identifier made up as follows :-
+ *
+ * header - msg type, msg id, msg direction ...
+ * extension - extra params such as msg data size in mailbox
+ *
+ * These are sent at the start of the IPC message in the mailbox. Messages
+ * should not be sent in the doorbell (special exceptions for firmware).
+ */
+
+/*
+ * IPC4 primary header bit allocation for messages
+ * bit 0-23:   message type specific
+ * bit 24-28:  type:   enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG
+ *                     enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG
+ * bit 29:     response - sof_ipc4_msg_dir
+ * bit 30:     target - enum sof_ipc4_msg_target
+ * bit 31:     reserved, unused
+ */
+
+/* Value of target field - must fit into 1 bit */
+enum sof_ipc4_msg_target {
+       /* Global FW message */
+       SOF_IPC4_FW_GEN_MSG,
+
+       /* Module message */
+       SOF_IPC4_MODULE_MSG
+};
+
+/* Value of type field - must fit into 5 bits */
+enum sof_ipc4_global_msg {
+       SOF_IPC4_GLB_BOOT_CONFIG,
+       SOF_IPC4_GLB_ROM_CONTROL,
+       SOF_IPC4_GLB_IPCGATEWAY_CMD,
+
+       /* 3 .. 12: RESERVED - do not use */
+
+       SOF_IPC4_GLB_PERF_MEASUREMENTS_CMD = 13,
+       SOF_IPC4_GLB_CHAIN_DMA,
+
+       SOF_IPC4_GLB_LOAD_MULTIPLE_MODULES,
+       SOF_IPC4_GLB_UNLOAD_MULTIPLE_MODULES,
+
+       /* pipeline settings */
+       SOF_IPC4_GLB_CREATE_PIPELINE,
+       SOF_IPC4_GLB_DELETE_PIPELINE,
+       SOF_IPC4_GLB_SET_PIPELINE_STATE,
+       SOF_IPC4_GLB_GET_PIPELINE_STATE,
+       SOF_IPC4_GLB_GET_PIPELINE_CONTEXT_SIZE,
+       SOF_IPC4_GLB_SAVE_PIPELINE,
+       SOF_IPC4_GLB_RESTORE_PIPELINE,
+
+       /* Loads library (using Code Load or HD/A Host Output DMA) */
+       SOF_IPC4_GLB_LOAD_LIBRARY,
+
+       /* 25: RESERVED - do not use */
+
+       SOF_IPC4_GLB_INTERNAL_MESSAGE = 26,
+
+       /* Notification (FW to SW driver) */
+       SOF_IPC4_GLB_NOTIFICATION,
+
+       /* 28 .. 31: RESERVED - do not use */
+
+       SOF_IPC4_GLB_TYPE_LAST,
+};
+
+/* Value of response field - must fit into 1 bit */
+enum sof_ipc4_msg_dir {
+       SOF_IPC4_MSG_REQUEST,
+       SOF_IPC4_MSG_REPLY,
+};
+
+enum sof_ipc4_pipeline_state {
+       SOF_IPC4_PIPE_INVALID_STATE,
+       SOF_IPC4_PIPE_UNINITIALIZED,
+       SOF_IPC4_PIPE_RESET,
+       SOF_IPC4_PIPE_PAUSED,
+       SOF_IPC4_PIPE_RUNNING,
+       SOF_IPC4_PIPE_EOS
+};
+
+/* Generic message fields (bit 24-30) */
+
+/* encoded to header's msg_tgt field */
+#define SOF_IPC4_MSG_TARGET_SHIFT              30
+#define SOF_IPC4_MSG_TARGET_MASK               BIT(30)
+#define SOF_IPC4_MSG_TARGET(x)                 ((x) << SOF_IPC4_MSG_TARGET_SHIFT)
+#define SOF_IPC4_MSG_IS_MODULE_MSG(x)          ((x) & SOF_IPC4_MSG_TARGET_MASK ? 1 : 0)
+
+/* encoded to header's rsp field */
+#define SOF_IPC4_MSG_DIR_SHIFT                 29
+#define SOF_IPC4_MSG_DIR_MASK                  BIT(29)
+#define SOF_IPC4_MSG_DIR(x)                    ((x) << SOF_IPC4_MSG_DIR_SHIFT)
+
+/* encoded to header's type field */
+#define SOF_IPC4_MSG_TYPE_SHIFT                        24
+#define SOF_IPC4_MSG_TYPE_MASK                 GENMASK(28, 24)
+#define SOF_IPC4_MSG_TYPE_SET(x)               (((x) << SOF_IPC4_MSG_TYPE_SHIFT) & \
+                                                SOF_IPC4_MSG_TYPE_MASK)
+#define SOF_IPC4_MSG_TYPE_GET(x)               (((x) & SOF_IPC4_MSG_TYPE_MASK) >> \
+                                                SOF_IPC4_MSG_TYPE_SHIFT)
+
+/* Global message type specific field definitions */
+
+/* pipeline creation ipc msg */
+#define SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT       16
+#define SOF_IPC4_GLB_PIPE_INSTANCE_MASK                GENMASK(23, 16)
+#define SOF_IPC4_GLB_PIPE_INSTANCE_ID(x)       ((x) << SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT       11
+#define SOF_IPC4_GLB_PIPE_PRIORITY_MASK                GENMASK(15, 11)
+#define SOF_IPC4_GLB_PIPE_PRIORITY(x)          ((x) << SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT       0
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE_MASK                GENMASK(10, 0)
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE(x)          ((x) << SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT         0
+#define SOF_IPC4_GLB_PIPE_EXT_LP_MASK          BIT(0)
+#define SOF_IPC4_GLB_PIPE_EXT_LP(x)            ((x) << SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT)
+
+/* pipeline set state ipc msg */
+#define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT               16
+#define SOF_IPC4_GLB_PIPE_STATE_ID_MASK                GENMASK(23, 16)
+#define SOF_IPC4_GLB_PIPE_STATE_ID(x)          ((x) << SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_STATE_SHIFT          0
+#define SOF_IPC4_GLB_PIPE_STATE_MASK           GENMASK(15, 0)
+#define SOF_IPC4_GLB_PIPE_STATE(x)             ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
+
+enum sof_ipc4_channel_config {
+       /* one channel only. */
+       SOF_IPC4_CHANNEL_CONFIG_MONO,
+       /* L & R. */
+       SOF_IPC4_CHANNEL_CONFIG_STEREO,
+       /* L, R & LFE; PCM only. */
+       SOF_IPC4_CHANNEL_CONFIG_2_POINT_1,
+       /* L, C & R; MP3 & AAC only. */
+       SOF_IPC4_CHANNEL_CONFIG_3_POINT_0,
+       /* L, C, R & LFE; PCM only. */
+       SOF_IPC4_CHANNEL_CONFIG_3_POINT_1,
+       /* L, R, Ls & Rs; PCM only. */
+       SOF_IPC4_CHANNEL_CONFIG_QUATRO,
+       /* L, C, R & Cs; MP3 & AAC only. */
+       SOF_IPC4_CHANNEL_CONFIG_4_POINT_0,
+       /* L, C, R, Ls & Rs. */
+       SOF_IPC4_CHANNEL_CONFIG_5_POINT_0,
+       /* L, C, R, Ls, Rs & LFE. */
+       SOF_IPC4_CHANNEL_CONFIG_5_POINT_1,
+       /* one channel replicated in two. */
+       SOF_IPC4_CHANNEL_CONFIG_DUAL_MONO,
+       /* Stereo (L,R) in 4 slots, 1st stream: [ L, R, -, - ] */
+       SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_0,
+       /* Stereo (L,R) in 4 slots, 2nd stream: [ -, -, L, R ] */
+       SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_1,
+       /* L, C, R, Ls, Rs & LFE., LS, RS */
+       SOF_IPC4_CHANNEL_CONFIG_7_POINT_1,
+};
+
+enum sof_ipc4_interleaved_style {
+       SOF_IPC4_CHANNELS_INTERLEAVED,
+       SOF_IPC4_CHANNELS_NONINTERLEAVED,
+};
+
+enum sof_ipc4_sample_type {
+       SOF_IPC4_MSB_INTEGER, /* integer with Most Significant Byte first */
+       SOF_IPC4_LSB_INTEGER, /* integer with Least Significant Byte first */
+};
+
+struct sof_ipc4_audio_format {
+       uint32_t sampling_frequency;
+       uint32_t bit_depth;
+       uint32_t ch_map;
+       uint32_t ch_cfg; /* sof_ipc4_channel_config */
+       uint32_t interleaving_style;
+       uint32_t fmt_cfg; /* channels_count valid_bit_depth s_type */
+} __packed __aligned(4);
+
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_SHIFT 0
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK  GENMASK(7, 0)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(x)    \
+       ((x) & SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT    8
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK     GENMASK(15, 8)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(x)       \
+       (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK) >> \
+        SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT    16
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK     GENMASK(23, 16)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(x)       \
+       (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK) >>  \
+        SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT)
+
+/* Module message type specific field definitions */
+
+enum sof_ipc4_module_type {
+       SOF_IPC4_MOD_INIT_INSTANCE,
+       SOF_IPC4_MOD_CONFIG_GET,
+       SOF_IPC4_MOD_CONFIG_SET,
+       SOF_IPC4_MOD_LARGE_CONFIG_GET,
+       SOF_IPC4_MOD_LARGE_CONFIG_SET,
+       SOF_IPC4_MOD_BIND,
+       SOF_IPC4_MOD_UNBIND,
+       SOF_IPC4_MOD_SET_DX,
+       SOF_IPC4_MOD_SET_D0IX,
+       SOF_IPC4_MOD_ENTER_MODULE_RESTORE,
+       SOF_IPC4_MOD_EXIT_MODULE_RESTORE,
+       SOF_IPC4_MOD_DELETE_INSTANCE,
+
+       SOF_IPC4_MOD_TYPE_LAST,
+};
+
+struct sof_ipc4_base_module_cfg {
+       uint32_t cpc; /* the max count of Cycles Per Chunk processing */
+       uint32_t ibs; /* input Buffer Size (in bytes)  */
+       uint32_t obs; /* output Buffer Size (in bytes) */
+       uint32_t is_pages; /* number of physical pages used */
+       struct sof_ipc4_audio_format audio_fmt;
+} __packed __aligned(4);
+
+/* common module ipc msg */
+#define SOF_IPC4_MOD_INSTANCE_SHIFT            16
+#define SOF_IPC4_MOD_INSTANCE_MASK             GENMASK(23, 16)
+#define SOF_IPC4_MOD_INSTANCE(x)               ((x) << SOF_IPC4_MOD_INSTANCE_SHIFT)
+
+#define SOF_IPC4_MOD_ID_SHIFT                  0
+#define SOF_IPC4_MOD_ID_MASK                   GENMASK(15, 0)
+#define SOF_IPC4_MOD_ID(x)                     ((x) << SOF_IPC4_MOD_ID_SHIFT)
+
+/* init module ipc msg */
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT      0
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK       GENMASK(15, 0)
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE(x)         ((x) << SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_PPL_ID_SHIFT          16
+#define SOF_IPC4_MOD_EXT_PPL_ID_MASK           GENMASK(23, 16)
+#define SOF_IPC4_MOD_EXT_PPL_ID(x)             ((x) << SOF_IPC4_MOD_EXT_PPL_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_CORE_ID_SHIFT         24
+#define SOF_IPC4_MOD_EXT_CORE_ID_MASK          GENMASK(27, 24)
+#define SOF_IPC4_MOD_EXT_CORE_ID(x)            ((x) << SOF_IPC4_MOD_EXT_CORE_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DOMAIN_SHIFT          28
+#define SOF_IPC4_MOD_EXT_DOMAIN_MASK           BIT(28)
+#define SOF_IPC4_MOD_EXT_DOMAIN(x)             ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT)
+
+/*  bind/unbind module ipc msg */
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT      0
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK       GENMASK(15, 0)
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID(x)         ((x) << SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT        16
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_MASK GENMASK(23, 16)
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(x)   ((x) << SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT        24
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_MASK GENMASK(26, 24)
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(x)   ((x) << SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT        27
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_MASK GENMASK(29, 27)
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(x)   ((x) << SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT)
+
+#define MOD_ENABLE_LOG 6
+#define MOD_SYSTEM_TIME        20
+
+/* set module large config */
+#define SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT                0
+#define SOF_IPC4_MOD_EXT_MSG_SIZE_MASK         GENMASK(19, 0)
+#define SOF_IPC4_MOD_EXT_MSG_SIZE(x)           ((x) << SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT    20
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK     GENMASK(27, 20)
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID(x)       ((x) << SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT  28
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_MASK   BIT(28)
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(x)     ((x) << SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT 29
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK  BIT(29)
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(x)    ((x) << SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT)
+
+/* Init instance messagees */
+#define SOF_IPC4_MOD_INIT_BASEFW_MOD_ID                0
+#define SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID   0
+
+enum sof_ipc4_base_fw_params {
+       SOF_IPC4_FW_PARAM_ENABLE_LOGS = 6,
+       SOF_IPC4_FW_PARAM_FW_CONFIG,
+       SOF_IPC4_FW_PARAM_HW_CONFIG_GET,
+       SOF_IPC4_FW_PARAM_MODULES_INFO_GET,
+       SOF_IPC4_FW_PARAM_LIBRARIES_INFO_GET = 16,
+       SOF_IPC4_FW_PARAM_SYSTEM_TIME = 20,
+};
+
+enum sof_ipc4_fw_config_params {
+       SOF_IPC4_FW_CFG_FW_VERSION,
+       SOF_IPC4_FW_CFG_MEMORY_RECLAIMED,
+       SOF_IPC4_FW_CFG_SLOW_CLOCK_FREQ_HZ,
+       SOF_IPC4_FW_CFG_FAST_CLOCK_FREQ_HZ,
+       SOF_IPC4_FW_CFG_DMA_BUFFER_CONFIG,
+       SOF_IPC4_FW_CFG_ALH_SUPPORT_LEVEL,
+       SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES,
+       SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES,
+       SOF_IPC4_FW_CFG_TRACE_LOG_BYTES,
+       SOF_IPC4_FW_CFG_MAX_PPL_COUNT,
+       SOF_IPC4_FW_CFG_MAX_ASTATE_COUNT,
+       SOF_IPC4_FW_CFG_MAX_MODULE_PIN_COUNT,
+       SOF_IPC4_FW_CFG_MODULES_COUNT,
+       SOF_IPC4_FW_CFG_MAX_MOD_INST_COUNT,
+       SOF_IPC4_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT,
+       SOF_IPC4_FW_CFG_LL_PRI_COUNT,
+       SOF_IPC4_FW_CFG_MAX_DP_TASKS_COUNT,
+       SOF_IPC4_FW_CFG_MAX_LIBS_COUNT,
+       SOF_IPC4_FW_CFG_SCHEDULER_CONFIG,
+       SOF_IPC4_FW_CFG_XTAL_FREQ_HZ,
+       SOF_IPC4_FW_CFG_CLOCKS_CONFIG,
+       SOF_IPC4_FW_CFG_RESERVED,
+       SOF_IPC4_FW_CFG_POWER_GATING_POLICY,
+       SOF_IPC4_FW_CFG_ASSERT_MODE,
+};
+
+struct sof_ipc4_fw_version {
+       uint16_t major;
+       uint16_t minor;
+       uint16_t hotfix;
+       uint16_t build;
+} __packed;
+
+/* Reply messages */
+
+/*
+ * IPC4 primary header bit allocation for replies
+ * bit 0-23:   status
+ * bit 24-28:  type:   enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG
+ *                     enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG
+ * bit 29:     response - sof_ipc4_msg_dir
+ * bit 30:     target - enum sof_ipc4_msg_target
+ * bit 31:     reserved, unused
+ */
+
+#define SOF_IPC4_REPLY_STATUS                  GENMASK(23, 0)
+
+/* Notification messages */
+
+/*
+ * IPC4 primary header bit allocation for notifications
+ * bit 0-15:   notification type specific
+ * bit 16-23:  enum sof_ipc4_notification_type
+ * bit 24-28:  SOF_IPC4_GLB_NOTIFICATION
+ * bit 29:     response - sof_ipc4_msg_dir
+ * bit 30:     target - enum sof_ipc4_msg_target
+ * bit 31:     reserved, unused
+ */
+
+#define SOF_IPC4_MSG_IS_NOTIFICATION(x)                (SOF_IPC4_MSG_TYPE_GET(x) == \
+                                                SOF_IPC4_GLB_NOTIFICATION)
+
+#define SOF_IPC4_NOTIFICATION_TYPE_SHIFT       16
+#define SOF_IPC4_NOTIFICATION_TYPE_MASK                GENMASK(23, 16)
+#define SOF_IPC4_NOTIFICATION_TYPE_GET(x)      (((x) & SOF_IPC4_NOTIFICATION_TYPE_MASK) >> \
+                                                SOF_IPC4_NOTIFICATION_TYPE_SHIFT)
+
+/* Value of notification type field - must fit into 8 bits */
+enum sof_ipc4_notification_type {
+       /* Phrase detected (notification from WoV module) */
+       SOF_IPC4_NOTIFY_PHRASE_DETECTED = 4,
+       /* Event from a resource (pipeline or module instance) */
+       SOF_IPC4_NOTIFY_RESOURCE_EVENT,
+       /* Debug log buffer status changed */
+       SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS,
+       /* Timestamp captured at the link */
+       SOF_IPC4_NOTIFY_TIMESTAMP_CAPTURED,
+       /* FW complete initialization */
+       SOF_IPC4_NOTIFY_FW_READY,
+       /* Audio classifier result (ACA) */
+       SOF_IPC4_NOTIFY_FW_AUD_CLASS_RESULT,
+       /* Exception caught by DSP FW */
+       SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT,
+       /* 11 is skipped by the existing cavs firmware */
+       /* Custom module notification */
+       SOF_IPC4_NOTIFY_MODULE_NOTIFICATION = 12,
+       /* 13 is reserved - do not use */
+       /* Probe notify data available */
+       SOF_IPC4_NOTIFY_PROBE_DATA_AVAILABLE = 14,
+       /* AM module notifications */
+       SOF_IPC4_NOTIFY_ASYNC_MSG_SRVC_MESSAGE,
+
+       SOF_IPC4_NOTIFY_TYPE_LAST,
+};
+
+struct sof_ipc4_notify_resource_data {
+       uint32_t resource_type;
+       uint32_t resource_id;
+       uint32_t event_type;
+       uint32_t reserved;
+       uint32_t data[6];
+} __packed __aligned(4);
+
+/** @}*/
+
+#endif
index 58a0d49..1db3bbc 100644 (file)
@@ -85,8 +85,9 @@ struct sof_ipc_stream_params {
 
        uint32_t host_period_bytes;
        uint16_t no_stream_position; /**< 1 means don't send stream position */
+       uint8_t cont_update_posn; /**< 1 means continuous update stream position */
 
-       uint16_t reserved[3];
+       uint8_t reserved[5];
        uint16_t chmap[SOF_IPC_MAX_CHANNELS];   /**< channel map - SOF_CHMAP_ */
 } __packed;
 
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
new file mode 100644 (file)
index 0000000..754f02b
--- /dev/null
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __UAPI_SOUND_INTEL_AVS_TOKENS_H
+#define __UAPI_SOUND_INTEL_AVS_TOKENS_H
+
+enum avs_tplg_token {
+       /* struct avs_tplg */
+       AVS_TKN_MANIFEST_NAME_STRING                    = 1,
+       AVS_TKN_MANIFEST_VERSION_U32                    = 2,
+       AVS_TKN_MANIFEST_NUM_LIBRARIES_U32              = 3,
+       AVS_TKN_MANIFEST_NUM_AFMTS_U32                  = 4,
+       AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32           = 5,
+       AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32            = 6,
+       AVS_TKN_MANIFEST_NUM_PPLCFGS_U32                = 7,
+       AVS_TKN_MANIFEST_NUM_BINDINGS_U32               = 8,
+
+       /* struct avs_tplg_library */
+       AVS_TKN_LIBRARY_ID_U32                          = 101,
+       AVS_TKN_LIBRARY_NAME_STRING                     = 102,
+
+       /* struct avs_audio_format */
+       AVS_TKN_AFMT_ID_U32                             = 201,
+       AVS_TKN_AFMT_SAMPLE_RATE_U32                    = 202,
+       AVS_TKN_AFMT_BIT_DEPTH_U32                      = 203,
+       AVS_TKN_AFMT_CHANNEL_MAP_U32                    = 204,
+       AVS_TKN_AFMT_CHANNEL_CFG_U32                    = 205,
+       AVS_TKN_AFMT_INTERLEAVING_U32                   = 206,
+       AVS_TKN_AFMT_NUM_CHANNELS_U32                   = 207,
+       AVS_TKN_AFMT_VALID_BIT_DEPTH_U32                = 208,
+       AVS_TKN_AFMT_SAMPLE_TYPE_U32                    = 209,
+
+       /* struct avs_tplg_modcfg_base */
+       AVS_TKN_MODCFG_BASE_ID_U32                      = 301,
+       AVS_TKN_MODCFG_BASE_CPC_U32                     = 302,
+       AVS_TKN_MODCFG_BASE_IBS_U32                     = 303,
+       AVS_TKN_MODCFG_BASE_OBS_U32                     = 304,
+       AVS_TKN_MODCFG_BASE_PAGES_U32                   = 305,
+
+       /* struct avs_tplg_modcfg_ext */
+       AVS_TKN_MODCFG_EXT_ID_U32                       = 401,
+       AVS_TKN_MODCFG_EXT_TYPE_UUID                    = 402,
+       AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32              = 403,
+       AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32             = 404,
+       AVS_TKN_MODCFG_CPR_DMA_TYPE_U32                 = 405,
+       AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32             = 406,
+       AVS_TKN_MODCFG_CPR_VINDEX_U8                    = 407,
+       AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32              = 408,
+       AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32           = 409,
+       AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32         = 410,
+       AVS_TKN_MODCFG_SRC_OUT_FREQ_U32                 = 411,
+       AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32              = 412,
+       AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32              = 413,
+       AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32              = 414,
+       AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32              = 415,
+       AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32              = 416,
+       AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32                = 417,
+       AVS_TKN_MODCFG_ASRC_MODE_U8                     = 418,
+       AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8           = 419,
+       AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32      = 420,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32      = 421,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32           = 422,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32           = 423,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32           = 424,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32           = 425,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32           = 426,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32           = 427,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32           = 428,
+       AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32           = 429,
+       AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32          = 430,
+       AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16           = 431,
+       AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16          = 432,
+
+       /* struct avs_tplg_pplcfg */
+       AVS_TKN_PPLCFG_ID_U32                           = 1401,
+       AVS_TKN_PPLCFG_REQ_SIZE_U16                     = 1402,
+       AVS_TKN_PPLCFG_PRIORITY_U8                      = 1403,
+       AVS_TKN_PPLCFG_LOW_POWER_BOOL                   = 1404,
+       AVS_TKN_PPLCFG_ATTRIBUTES_U16                   = 1405,
+       AVS_TKN_PPLCFG_TRIGGER_U32                      = 1406,
+
+       /* struct avs_tplg_binding */
+       AVS_TKN_BINDING_ID_U32                          = 1501,
+       AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING         = 1502,
+       AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32         = 1503,
+       AVS_TKN_BINDING_TARGET_PPL_ID_U32               = 1504,
+       AVS_TKN_BINDING_TARGET_MOD_ID_U32               = 1505,
+       AVS_TKN_BINDING_TARGET_MOD_PIN_U8               = 1506,
+       AVS_TKN_BINDING_MOD_ID_U32                      = 1507,
+       AVS_TKN_BINDING_MOD_PIN_U8                      = 1508,
+       AVS_TKN_BINDING_IS_SINK_U8                      = 1509,
+
+       /* struct avs_tplg_pipeline */
+       AVS_TKN_PPL_ID_U32                              = 1601,
+       AVS_TKN_PPL_PPLCFG_ID_U32                       = 1602,
+       AVS_TKN_PPL_NUM_BINDING_IDS_U32                 = 1603,
+       AVS_TKN_PPL_BINDING_ID_U32                      = 1604,
+
+       /* struct avs_tplg_module */
+       AVS_TKN_MOD_ID_U32                              = 1701,
+       AVS_TKN_MOD_MODCFG_BASE_ID_U32                  = 1702,
+       AVS_TKN_MOD_IN_AFMT_ID_U32                      = 1703,
+       AVS_TKN_MOD_CORE_ID_U8                          = 1704,
+       AVS_TKN_MOD_PROC_DOMAIN_U8                      = 1705,
+       AVS_TKN_MOD_MODCFG_EXT_ID_U32                   = 1706,
+
+       /* struct avs_tplg_path_template */
+       AVS_TKN_PATH_TMPL_ID_U32                        = 1801,
+
+       /* struct avs_tplg_path */
+       AVS_TKN_PATH_ID_U32                             = 1901,
+       AVS_TKN_PATH_FE_FMT_ID_U32                      = 1902,
+       AVS_TKN_PATH_BE_FMT_ID_U32                      = 1903,
+
+       /* struct avs_tplg_pin_format */
+       AVS_TKN_PIN_FMT_INDEX_U32                       = 2201,
+       AVS_TKN_PIN_FMT_IOBS_U32                        = 2202,
+       AVS_TKN_PIN_FMT_AFMT_ID_U32                     = 2203,
+};
+
+#endif
index e052653..0e7dccd 100644 (file)
@@ -26,8 +26,8 @@
 
 /* SOF ABI version major, minor and patch numbers */
 #define SOF_ABI_MAJOR 3
-#define SOF_ABI_MINOR 19
-#define SOF_ABI_PATCH 1
+#define SOF_ABI_MINOR 21
+#define SOF_ABI_PATCH 0
 
 /* SOF ABI version number. Format within 32bit word is MMmmmppp */
 #define SOF_ABI_MAJOR_SHIFT    24
index 5dcf77a..7d4747b 100644 (file)
@@ -14,7 +14,7 @@ menuconfig SND_SOC
 
          If you want ASoC support, you should say Y here and also to the
          specific driver for your SoC platform below.
-         
+
          ASoC provides power efficient ALSA support for embedded battery powered
          SoC based systems like PDA's, Phones and Personal Media Players.
 
@@ -55,6 +55,13 @@ config SND_SOC_TOPOLOGY_KUNIT_TEST
          userspace applications such as pulseaudio, to prevent unnecessary
          problems.
 
+config SND_SOC_UTILS_KUNIT_TEST
+       tristate "KUnit tests for SoC utils"
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         If you want to perform tests on ALSA SoC utils library say Y here.
+
 config SND_SOC_ACPI
        tristate
 
index a7b37c0..d452896 100644 (file)
@@ -12,6 +12,11 @@ ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),)
 obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) := soc-topology-test.o
 endif
 
+ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),)
+# snd-soc-test-objs := soc-utils-test.o
+obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) := soc-utils-test.o
+endif
+
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
index 3bf86c2..ef1b4ce 100644 (file)
@@ -71,7 +71,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
                                SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                               &cz_jack, NULL, 0);
+                               &cz_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
@@ -151,7 +151,7 @@ static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &cz_jack, NULL, 0);
+                                   &cz_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
index a79a466..532aa98 100644 (file)
@@ -80,7 +80,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
                                SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
                                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                               &cz_jack, NULL, 0);
+                               &cz_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
index 626e4a5..9dae271 100644 (file)
@@ -42,7 +42,7 @@ config SND_AMD_ASOC_RENOIR
 
 config SND_SOC_AMD_MACH_COMMON
        tristate
-       depends on X86 && PCI && I2C && GPIOLIB
+       depends on X86 && PCI && I2C
        select CLK_FIXED_FCH
        select SND_SOC_RT5682_I2C
        select SND_SOC_DMIC
@@ -54,14 +54,14 @@ config SND_SOC_AMD_MACH_COMMON
 
 config SND_SOC_AMD_LEGACY_MACH
        tristate "AMD Legacy Machine Driver Support"
-       depends on X86 && PCI && I2C && GPIOLIB
+       depends on X86 && PCI && I2C
        select SND_SOC_AMD_MACH_COMMON
        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 && GPIOLIB
+       depends on X86 && PCI && I2C
        select SND_SOC_AMD_MACH_COMMON
        help
          This option enables SOF sound card support for ACP audio.
index 5d27636..7f04a04 100644 (file)
@@ -27,7 +27,6 @@ static struct acp_card_drvdata 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 rt5682s_max_data = {
@@ -37,7 +36,6 @@ static struct acp_card_drvdata rt5682s_max_data = {
        .hs_codec_id = RT5682S,
        .amp_codec_id = MAX98360A,
        .dmic_codec_id = DMIC,
-       .gpio_spkr_en = EN_SPKR_GPIO_NONE,
 };
 
 static struct acp_card_drvdata rt5682s_rt1019_data = {
@@ -47,7 +45,6 @@ static struct acp_card_drvdata rt5682s_rt1019_data = {
        .hs_codec_id = RT5682S,
        .amp_codec_id = RT1019,
        .dmic_codec_id = DMIC,
-       .gpio_spkr_en = EN_SPKR_GPIO_NONE,
 };
 
 static const struct snd_kcontrol_new acp_controls[] = {
@@ -62,16 +59,15 @@ 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", event_spkr_handler),
-       SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
-       SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
+       SND_SOC_DAPM_SPK("Spk", NULL),
+       SND_SOC_DAPM_SPK("Left Spk", NULL),
+       SND_SOC_DAPM_SPK("Right Spk", NULL),
 };
 
 static int acp_asoc_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = NULL;
        struct device *dev = &pdev->dev;
-       unsigned int spkr_gpio;
        int ret;
 
        if (!pdev->id_entry)
@@ -89,20 +85,9 @@ 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,
@@ -131,6 +116,7 @@ static const struct platform_device_id board_ids[] = {
 };
 static struct platform_driver acp_asoc_audio = {
        .driver = {
+               .pm = &snd_soc_pm_ops,
                .name = "acp_mach",
        },
        .probe = acp_asoc_probe,
index caa202f..6ae454b 100644 (file)
@@ -71,31 +71,6 @@ 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)
 {
@@ -145,7 +120,7 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &pco_jack, NULL, 0);
+                                   &pco_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
@@ -266,7 +241,7 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &pco_jack, NULL, 0);
+                                   &pco_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
index c855f50..5dc47cf 100644 (file)
 #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_NONE      -EINVAL
 
 enum be_id {
        HEADSET_BE_ID = 0,
@@ -54,11 +49,9 @@ 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
index 3346677..d1531cd 100644 (file)
@@ -27,7 +27,6 @@ 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 = {
@@ -37,7 +36,6 @@ 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_NONE,
 };
 
 static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
@@ -56,7 +54,6 @@ 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_NONE,
 };
 
 static const struct snd_kcontrol_new acp_controls[] = {
@@ -70,16 +67,15 @@ 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", event_spkr_handler),
-       SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
-       SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
+       SND_SOC_DAPM_SPK("Spk", NULL),
+       SND_SOC_DAPM_SPK("Left Spk", NULL),
+       SND_SOC_DAPM_SPK("Right Spk", NULL),
 };
 
 static int acp_sof_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = NULL;
        struct device *dev = &pdev->dev;
-       unsigned int spkr_gpio;
        int ret;
 
        if (!pdev->id_entry)
@@ -97,20 +93,9 @@ 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,
@@ -144,6 +129,7 @@ static const struct platform_device_id board_ids[] = {
 static struct platform_driver acp_asoc_audio = {
        .driver = {
                .name = "sof_mach",
+               .pm = &snd_soc_pm_ops,
        },
        .probe = acp_sof_probe,
        .id_table = board_ids,
index dad7043..0543dda 100644 (file)
@@ -90,7 +90,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
                                SND_JACK_HEADSET | SND_JACK_LINEOUT |
                                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                               &pco_jack, NULL, 0);
+                               &pco_jack);
        if (ret) {
                dev_err(card->dev, "HP jack creation failed %d\n", ret);
                return ret;
index 1551546..727de46 100644 (file)
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
-#include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
-#include <linux/io.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
 
@@ -61,10 +59,10 @@ static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
         * Headset buttons map to the google Reference headset.
         * These can be configured by userspace.
         */
-       ret = snd_soc_card_jack_new(card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &vg_headset, acp5x_nau8821_jack_pins,
-                                   ARRAY_SIZE(acp5x_nau8821_jack_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                        &vg_headset, acp5x_nau8821_jack_pins,
+                                        ARRAY_SIZE(acp5x_nau8821_jack_pins));
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
index 9a767f4..f06e6c1 100644 (file)
@@ -12,6 +12,7 @@
 #include <sound/pcm_params.h>
 #include <linux/io.h>
 #include <linux/dmi.h>
+#include <linux/acpi.h>
 
 #include "acp6x.h"
 
@@ -45,108 +46,126 @@ static struct snd_soc_card acp6x_card = {
 
 static const struct dmi_system_id yc_acp_quirk_table[] = {
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21AW"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21AX"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21BN"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21BQ"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D8"),
                }
        },
        {
+               .driver_data = &acp6x_card,
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "21D9"),
@@ -157,18 +176,33 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
 
 static int acp6x_probe(struct platform_device *pdev)
 {
+       const struct dmi_system_id *dmi_id;
        struct acp6x_pdm *machine = NULL;
        struct snd_soc_card *card;
+       struct acpi_device *adev;
        int ret;
-       const struct dmi_system_id *dmi_id;
 
+       /* check the parent device's firmware node has _DSD or not */
+       adev = ACPI_COMPANION(pdev->dev.parent);
+       if (adev) {
+               const union acpi_object *obj;
+
+               if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) &&
+                   obj->integer.value == 1)
+                       platform_set_drvdata(pdev, &acp6x_card);
+       }
+
+       /* check for any DMI overrides */
        dmi_id = dmi_first_match(yc_acp_quirk_table);
-       if (!dmi_id)
+       if (dmi_id)
+               platform_set_drvdata(pdev, dmi_id->driver_data);
+
+       card = platform_get_drvdata(pdev);
+       if (!card)
                return -ENODEV;
-       card = &acp6x_card;
+       dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI");
        acp6x_card.dev = &pdev->dev;
 
-       platform_set_drvdata(pdev, card);
        snd_soc_card_set_drvdata(card, machine);
        ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
index 7e9a9a9..20f7a99 100644 (file)
@@ -154,9 +154,14 @@ static int snd_acp6x_probe(struct pci_dev *pci,
 
        irqflags = IRQF_SHARED;
        /* Yellow Carp device check */
-       if (pci->revision != 0x60)
+       switch (pci->revision) {
+       case 0x60:
+       case 0x6f:
+               break;
+       default:
+               dev_err(&pci->dev, "acp6x pci device not found\n");
                return -ENODEV;
-
+       }
        if (pci_enable_device(pci)) {
                dev_err(&pci->dev, "pci_enable_device failed\n");
                return -ENODEV;
index 795c0b0..5d59e00 100644 (file)
@@ -42,9 +42,9 @@ config SND_ATMEL_SOC_SSC_DMA
 config SND_AT91_SOC_SAM9G20_WM8731
        tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
        depends on ARCH_AT91 || COMPILE_TEST
-       depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+       depends on ATMEL_SSC && I2C
        select SND_ATMEL_SOC_SSC_PDC
-       select SND_SOC_WM8731
+       select SND_SOC_WM8731_I2C
        help
          Say Y if you want to add support for SoC audio on WM8731-based
          AT91sam9g20 evaluation board.
index a9f9f44..74b7b26 100644 (file)
@@ -458,7 +458,6 @@ static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
        .num_controls           = ARRAY_SIZE(atmel_classd_snd_controls),
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
-       .endianness             = 1,
 };
 
 /* ASoC sound card */
index 42117de..ea34efa 100644 (file)
@@ -481,7 +481,6 @@ static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
        .num_controls           = ARRAY_SIZE(atmel_pdmic_snd_controls),
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
-       .endianness             = 1,
 };
 
 /* ASoC sound card */
index 0d639a3..4d25fb6 100644 (file)
@@ -127,8 +127,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
 
        ret = atmel_ssc_set_audio(0);
        if (ret) {
-               dev_err(&pdev->dev, "ssc channel is not valid\n");
-               return -EINVAL;
+               dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
+               return ret;
        }
 
        card->dev = &pdev->dev;
@@ -148,7 +148,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
        codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
        if (!codec_np) {
                dev_err(&pdev->dev, "codec info missing\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
        at91sam9g20ek_dai.codecs->of_node = codec_np;
 
@@ -159,7 +160,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
        if (!cpu_np) {
                dev_err(&pdev->dev, "dai and pcm info missing\n");
                of_node_put(codec_np);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
        at91sam9g20ek_dai.cpus->of_node = cpu_np;
        at91sam9g20ek_dai.platforms->of_node = cpu_np;
@@ -169,10 +171,12 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_card(card);
        if (ret) {
-               dev_err(&pdev->dev, "snd_soc_register_card() failed\n");
+               dev_err_probe(&pdev->dev, ret,
+                             "snd_soc_register_card() failed: %d\n", ret);
+               goto err;
        }
 
-       return ret;
+       return 0;
 
 err:
        atmel_ssc_put_audio(0);
index 38de7c0..8a78809 100644 (file)
@@ -58,7 +58,7 @@ config SND_SOC_DB1200
        select SND_SOC_AC97_CODEC
        select SND_SOC_WM9712
        select SND_SOC_AU1XPSC_I2S
-       select SND_SOC_WM8731
+       select SND_SOC_WM8731_I2C
        help
          Select this option to enable audio (AC97 and I2S) on the
          Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards.
index f46a226..6165db9 100644 (file)
@@ -65,6 +65,8 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_CS35L36
        imply SND_SOC_CS35L41_SPI
        imply SND_SOC_CS35L41_I2C
+       imply SND_SOC_CS35L45_I2C
+       imply SND_SOC_CS35L45_SPI
        imply SND_SOC_CS42L42
        imply SND_SOC_CS42L51_I2C
        imply SND_SOC_CS42L52
@@ -127,6 +129,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_MAX98373_I2C
        imply SND_SOC_MAX98373_SDW
        imply SND_SOC_MAX98390
+       imply SND_SOC_MAX98396
        imply SND_SOC_MAX9850
        imply SND_SOC_MAX9860
        imply SND_SOC_MAX9759
@@ -168,6 +171,7 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_RT1011
        imply SND_SOC_RT1015
        imply SND_SOC_RT1015P
+       imply SND_SOC_RT1016
        imply SND_SOC_RT1019
        imply SND_SOC_RT1305
        imply SND_SOC_RT1308
@@ -265,7 +269,8 @@ config SND_SOC_ALL_CODECS
        imply SND_SOC_WM8711
        imply SND_SOC_WM8727
        imply SND_SOC_WM8728
-       imply SND_SOC_WM8731
+       imply SND_SOC_WM8731_I2C
+       imply SND_SOC_WM8731_SPI
        imply SND_SOC_WM8737
        imply SND_SOC_WM8741
        imply SND_SOC_WM8750
@@ -655,6 +660,34 @@ config SND_SOC_CS35L41_I2C
        select SND_SOC_CS35L41
        select REGMAP_I2C
 
+config SND_SOC_CS35L45_TABLES
+       tristate
+
+config SND_SOC_CS35L45
+       tristate
+
+config SND_SOC_CS35L45_SPI
+       tristate "Cirrus Logic CS35L45 CODEC (SPI)"
+       depends on SPI_MASTER
+       select REGMAP
+       select REGMAP_SPI
+       select SND_SOC_CS35L45_TABLES
+       select SND_SOC_CS35L45
+       help
+         Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+         with SPI control.
+
+config SND_SOC_CS35L45_I2C
+       tristate "Cirrus Logic CS35L45 CODEC (I2C)"
+       depends on I2C
+       select REGMAP
+       select REGMAP_I2C
+       select SND_SOC_CS35L45_TABLES
+       select SND_SOC_CS35L45
+       help
+         Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+         with I2C control.
+
 config SND_SOC_CS42L42
        tristate "Cirrus Logic CS42L42 CODEC"
        depends on I2C
@@ -953,7 +986,6 @@ config SND_SOC_MAX98095
 
 config SND_SOC_MAX98357A
        tristate "Maxim MAX98357A CODEC"
-       depends on GPIOLIB
 
 config SND_SOC_MAX98371
        tristate
@@ -1015,6 +1047,15 @@ config SND_SOC_MAX98390
        tristate "Maxim Integrated MAX98390 Speaker Amplifier"
        depends on I2C
 
+config SND_SOC_MAX98396
+       tristate "Analog Devices MAX98396 Speaker Amplifier"
+       depends on I2C
+       help
+         Enable support for Analog Devices MAX98396 audio
+         amplifier. The device provides a PCM interface for
+         audio data and a standard I2C interface for control
+         data communication.
+
 config SND_SOC_MAX9850
        tristate
        depends on I2C
@@ -1213,7 +1254,10 @@ config SND_SOC_RT1015
 
 config SND_SOC_RT1015P
        tristate
-       depends on GPIOLIB
+
+config SND_SOC_RT1016
+       tristate
+       depends on I2C
 
 config SND_SOC_RT1019
        tristate
@@ -1753,8 +1797,19 @@ config SND_SOC_WM8728
        depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8731
-       tristate "Wolfson Microelectronics WM8731 CODEC"
-       depends on SND_SOC_I2C_AND_SPI
+       tristate
+
+config SND_SOC_WM8731_I2C
+       tristate "Wolfson Microelectronics WM8731 CODEC with I2C"
+       depends on I2C
+       select REGMAP
+       select SND_SOC_WM8731
+
+config SND_SOC_WM8731_SPI
+       tristate "Wolfson Microelectronics WM8731 CODEC with SPI"
+       depends on SPI
+       select REGMAP
+       select SND_SOC_WM8731
 
 config SND_SOC_WM8737
        tristate "Wolfson Microelectronics WM8737 ADC"
@@ -1811,7 +1866,7 @@ config SND_SOC_WM8904
        depends on I2C
 
 config SND_SOC_WM8940
-       tristate
+       tristate "Wolfson Microelectronics WM8940 codec"
        depends on I2C
 
 config SND_SOC_WM8955
index 8637e9e..28dc4ed 100644 (file)
@@ -60,6 +60,10 @@ 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-cs35l45-tables-objs := cs35l45-tables.o
+snd-soc-cs35l45-objs := cs35l45.o
+snd-soc-cs35l45-spi-objs := cs35l45-spi.o
+snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
 snd-soc-cs42l42-objs := cs42l42.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -135,6 +139,7 @@ snd-soc-max98373-objs := max98373.o
 snd-soc-max98373-i2c-objs := max98373-i2c.o
 snd-soc-max98373-sdw-objs := max98373-sdw.o
 snd-soc-max98390-objs := max98390.o
+snd-soc-max98396-objs := max98396.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-max9860-objs := max9860.o
 snd-soc-mc13783-objs := mc13783.o
@@ -181,6 +186,7 @@ snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt1011-objs := rt1011.o
 snd-soc-rt1015-objs := rt1015.o
 snd-soc-rt1015p-objs := rt1015p.o
+snd-soc-rt1016-objs := rt1016.o
 snd-soc-rt1019-objs := rt1019.o
 snd-soc-rt1305-objs := rt1305.o
 snd-soc-rt1308-objs := rt1308.o
@@ -290,6 +296,8 @@ snd-soc-wm8711-objs := wm8711.o
 snd-soc-wm8727-objs := wm8727.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8731-i2c-objs := wm8731-i2c.o
+snd-soc-wm8731-spi-objs := wm8731-spi.o
 snd-soc-wm8737-objs := wm8737.o
 snd-soc-wm8741-objs := wm8741.o
 snd-soc-wm8750-objs := wm8750.o
@@ -404,6 +412,10 @@ 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_CS35L45_TABLES)   += snd-soc-cs35l45-tables.o
+obj-$(CONFIG_SND_SOC_CS35L45)  += snd-soc-cs35l45.o
+obj-$(CONFIG_SND_SOC_CS35L45_SPI)      += snd-soc-cs35l45-spi.o
+obj-$(CONFIG_SND_SOC_CS35L45_I2C)      += snd-soc-cs35l45-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L42)  += snd-soc-cs42l42.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)      += snd-soc-cs42l51-i2c.o
@@ -474,6 +486,7 @@ obj-$(CONFIG_SND_SOC_MAX98373)      += snd-soc-max98373.o
 obj-$(CONFIG_SND_SOC_MAX98373_I2C)   += snd-soc-max98373-i2c.o
 obj-$(CONFIG_SND_SOC_MAX98373_SDW)   += snd-soc-max98373-sdw.o
 obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o
+obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o
 obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MAX9860)  += snd-soc-max9860.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
@@ -520,6 +533,7 @@ obj-$(CONFIG_SND_SOC_RL6347A)       += snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT1011)   += snd-soc-rt1011.o
 obj-$(CONFIG_SND_SOC_RT1015)   += snd-soc-rt1015.o
 obj-$(CONFIG_SND_SOC_RT1015P)  += snd-soc-rt1015p.o
+obj-$(CONFIG_SND_SOC_RT1016)   += snd-soc-rt1016.o
 obj-$(CONFIG_SND_SOC_RT1019)   += snd-soc-rt1019.o
 obj-$(CONFIG_SND_SOC_RT1305)   += snd-soc-rt1305.o
 obj-$(CONFIG_SND_SOC_RT1308)   += snd-soc-rt1308.o
@@ -632,6 +646,8 @@ obj-$(CONFIG_SND_SOC_WM8711)        += snd-soc-wm8711.o
 obj-$(CONFIG_SND_SOC_WM8727)   += snd-soc-wm8727.o
 obj-$(CONFIG_SND_SOC_WM8728)   += snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8731_I2C)       += snd-soc-wm8731-i2c.o
+obj-$(CONFIG_SND_SOC_WM8731_SPI)       += snd-soc-wm8731-spi.o
 obj-$(CONFIG_SND_SOC_WM8737)   += snd-soc-wm8737.o
 obj-$(CONFIG_SND_SOC_WM8741)   += snd-soc-wm8741.o
 obj-$(CONFIG_SND_SOC_WM8750)   += snd-soc-wm8750.o
index 3d509a6..4cb8d87 100644 (file)
@@ -20,10 +20,10 @@ static const struct i2c_device_id ad193x_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ad193x_id);
 
-static int ad193x_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
+static int ad193x_i2c_probe(struct i2c_client *client)
 {
        struct regmap_config config;
+       const struct i2c_device_id *id = i2c_match_id(ad193x_id, client);
 
        config = ad193x_regmap_config;
        config.val_bits = 8;
@@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = {
        .driver = {
                .name = "ad193x",
        },
-       .probe    = ad193x_i2c_probe,
+       .probe_new = ad193x_i2c_probe,
        .id_table = ad193x_id,
 };
 module_i2c_driver(ad193x_i2c_driver);
index fc87a76..8ed0ffd 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "adau1372.h"
 
-static int adau1372_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int adau1372_i2c_probe(struct i2c_client *client)
 {
        return adau1372_probe(&client->dev,
                devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL);
@@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = {
        .driver = {
                .name = "adau1372",
        },
-       .probe = adau1372_i2c_probe,
+       .probe_new = adau1372_i2c_probe,
        .id_table = adau1372_i2c_ids,
 };
 module_i2c_driver(adau1372_i2c_driver);
index 1faa4c4..a9f89e8 100644 (file)
@@ -859,6 +859,7 @@ static const struct snd_soc_component_driver adau1372_driver = {
        .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets),
        .dapm_routes = adau1372_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes),
+       .endianness = 1,
 };
 
 static const struct snd_soc_dai_ops adau1372_dai_ops = {
index 46128aa..a9032b5 100644 (file)
@@ -1473,8 +1473,7 @@ static const struct snd_soc_component_driver adau1373_component_driver = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int adau1373_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int adau1373_i2c_probe(struct i2c_client *client)
 {
        struct adau1373 *adau1373;
        int ret;
@@ -1508,7 +1507,7 @@ static struct i2c_driver adau1373_i2c_driver = {
        .driver = {
                .name = "adau1373",
        },
-       .probe = adau1373_i2c_probe,
+       .probe_new = adau1373_i2c_probe,
        .id_table = adau1373_i2c_id,
 };
 
index dba9af7..98768e5 100644 (file)
@@ -785,8 +785,7 @@ static const struct regmap_config adau1701_regmap = {
        .reg_read               = adau1701_reg_read,
 };
 
-static int adau1701_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int adau1701_i2c_probe(struct i2c_client *client)
 {
        struct adau1701 *adau1701;
        struct device *dev = &client->dev;
@@ -878,7 +877,7 @@ static struct i2c_driver adau1701_i2c_driver = {
                .name   = "adau1701",
                .of_match_table = of_match_ptr(adau1701_dt_ids),
        },
-       .probe          = adau1701_i2c_probe,
+       .probe_new      = adau1701_i2c_probe,
        .id_table       = adau1701_i2c_id,
 };
 
index c8fce37..0683caf 100644 (file)
 
 #include "adau1761.h"
 
-static int adau1761_i2c_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
+static const struct i2c_device_id adau1761_i2c_ids[];
+
+static int adau1761_i2c_probe(struct i2c_client *client)
 {
        struct regmap_config config;
+       const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client);
 
        config = adau1761_regmap_config;
        config.val_bits = 8;
@@ -59,7 +61,7 @@ static struct i2c_driver adau1761_i2c_driver = {
                .name = "adau1761",
                .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
        },
-       .probe = adau1761_i2c_probe,
+       .probe_new = adau1761_i2c_probe,
        .remove = adau1761_i2c_remove,
        .id_table = adau1761_i2c_ids,
 };
index fb006fc..8f88722 100644 (file)
@@ -556,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
        { "Left DAC", NULL, "Interpolator Resync Clock" },
        { "Right DAC", NULL, "Interpolator Resync Clock" },
 
-       { "DSP", NULL, "Digital Clock 0" },
-
        { "Slew Clock", NULL, "Digital Clock 0" },
        { "Right Playback Mixer", NULL, "Slew Clock" },
        { "Left Playback Mixer", NULL, "Slew Clock" },
@@ -569,6 +567,56 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
        { "Digital Clock 1", NULL, "SYSCLK" },
 };
 
+static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = {
+       { "DSP", NULL, "Digital Clock 0" },
+};
+
+static int adau1761_compatibility_probe(struct device *dev)
+{
+       struct adau *adau = dev_get_drvdata(dev);
+       struct regmap *regmap = adau->regmap;
+       int val, ret = 0;
+
+       /* Only consider compatibility mode when ADAU1361 was specified. */
+       if (adau->type != ADAU1361)
+               return 0;
+
+       regcache_cache_bypass(regmap, true);
+
+       /*
+        * This will enable the core clock and bypass the PLL,
+        * so that we can access the registers for probing purposes
+        * (without having to set up the PLL).
+        */
+       regmap_write(regmap, ADAU17X1_CLOCK_CONTROL,
+               ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+       /*
+        * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips;
+        * reading it results in zero at all times, and write is a no-op.
+        * Use this register to probe for ADAU1761.
+        */
+       regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1);
+       ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+       if (ret)
+               goto exit;
+       if (val != 1)
+               goto exit;
+       regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0);
+       ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+       if (ret)
+               goto exit;
+       if (val != 0)
+               goto exit;
+
+       adau->type = ADAU1761_AS_1361;
+exit:
+       /* Disable core clock after probing. */
+       regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0);
+       regcache_cache_bypass(regmap, false);
+       return ret;
+}
+
 static int adau1761_set_bias_level(struct snd_soc_component *component,
                                 enum snd_soc_bias_level level)
 {
@@ -823,7 +871,11 @@ static int adau1761_component_probe(struct snd_soc_component *component)
        if (ret)
                return ret;
 
-       if (adau->type == ADAU1761) {
+       /*
+        * If we've got an ADAU1761, or an ADAU1761 operating as an
+        * ADAU1361, we need these non-DSP related DAPM widgets and routes.
+        */
+       if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) {
                ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
                        ARRAY_SIZE(adau1761_dapm_widgets));
                if (ret)
@@ -834,7 +886,29 @@ static int adau1761_component_probe(struct snd_soc_component *component)
                if (ret)
                        return ret;
        }
-
+       /*
+        * These routes are DSP related and only used when we have a
+        * bona fide ADAU1761.
+        */
+       if (adau->type == ADAU1761) {
+               ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes,
+                       ARRAY_SIZE(adau1761_dapm_dsp_routes));
+               if (ret)
+                       return ret;
+       }
+       /*
+        * In the ADAU1761, by default, the AIF is routed to the DSP, whereas
+        * for the ADAU1361, the AIF is permanently routed to the ADC and DAC.
+        * Thus, if we have an ADAU1761 masquerading as an ADAU1361,
+        * we need to explicitly route the AIF to the ADC and DAC.
+        * For the ADAU1761, this is normally done by set_tdm_slot, but this
+        * function is not necessarily called during stream setup, so set up
+        * the compatible AIF routings here from the start.
+        */
+       if  (adau->type == ADAU1761_AS_1361) {
+               regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01);
+               regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01);
+       }
        ret = adau17x1_add_routes(component);
        if (ret < 0)
                return ret;
@@ -919,6 +993,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
        if (ret)
                return ret;
 
+       ret = adau1761_compatibility_probe(dev);
+       if (ret)
+               return ret;
+
        /* Enable cache only mode as we could miss writes before bias level
         * reaches standby and the core clock is enabled */
        regcache_cache_only(regmap, true);
index 1c47642..e046de0 100644 (file)
 
 #include "adau1781.h"
 
-static int adau1781_i2c_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
+static const struct i2c_device_id adau1781_i2c_ids[];
+
+static int adau1781_i2c_probe(struct i2c_client *client)
 {
        struct regmap_config config;
+       const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client);
 
        config = adau1781_regmap_config;
        config.val_bits = 8;
@@ -55,7 +57,7 @@ static struct i2c_driver adau1781_i2c_driver = {
                .name = "adau1781",
                .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
        },
-       .probe = adau1781_i2c_probe,
+       .probe_new = adau1781_i2c_probe,
        .remove = adau1781_i2c_remove,
        .id_table = adau1781_i2c_ids,
 };
index af05463..c0f44ec 100644 (file)
@@ -334,6 +334,17 @@ static bool adau17x1_has_dsp(struct adau *adau)
        }
 }
 
+/* Chip has a DSP but we're pretending it doesn't. */
+static bool adau17x1_has_disused_dsp(struct adau *adau)
+{
+       switch (adau->type) {
+       case ADAU1761_AS_1361:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static bool adau17x1_has_safeload(struct adau *adau)
 {
        switch (adau->type) {
@@ -516,10 +527,11 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 
        regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
                ADAU17X1_CONVERTER0_CONVSR_MASK, div);
-       if (adau17x1_has_dsp(adau)) {
+
+       if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau))
                regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+       if (adau17x1_has_dsp(adau))
                regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
-       }
 
        if (adau->sigmadsp) {
                ret = adau17x1_setup_firmware(component, params_rate(params));
@@ -663,7 +675,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
 
        switch (slot_width * slots) {
        case 32:
-               if (adau->type == ADAU1761)
+               if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
                        return -EINVAL;
 
                ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@@ -738,7 +750,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
        regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
                ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
 
-       if (!adau17x1_has_dsp(adau))
+       if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
                return 0;
 
        if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
index 98a3b6f..5e58abf 100644 (file)
@@ -10,6 +10,7 @@
 enum adau17x1_type {
        ADAU1361,
        ADAU1761,
+       ADAU1761_AS_1361,
        ADAU1381,
        ADAU1781,
 };
index 82a49c8..9f137a0 100644 (file)
 
 #include "adau1977.h"
 
-static int adau1977_i2c_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
+static const struct i2c_device_id adau1977_i2c_ids[];
+
+static int adau1977_i2c_probe(struct i2c_client *client)
 {
        struct regmap_config config;
+       const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client);
 
        config = adau1977_regmap_config;
        config.val_bits = 8;
@@ -40,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = {
        .driver = {
                .name = "adau1977",
        },
-       .probe = adau1977_i2c_probe,
+       .probe_new = adau1977_i2c_probe,
        .id_table = adau1977_i2c_ids,
 };
 module_i2c_driver(adau1977_i2c_driver);
index aa7afb3..afed484 100644 (file)
@@ -48,8 +48,7 @@ static const struct regmap_config adau7118_regmap_config = {
        .volatile_reg = adau7118_volatile,
 };
 
-static int adau7118_probe_i2c(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+static int adau7118_probe_i2c(struct i2c_client *i2c)
 {
        struct regmap *map;
 
@@ -79,7 +78,7 @@ static struct i2c_driver adau7118_driver = {
                .name = "adau7118",
                .of_match_table = adau7118_of_match,
        },
-       .probe = adau7118_probe_i2c,
+       .probe_new = adau7118_probe_i2c,
        .id_table = adau7118_id,
 };
 module_i2c_driver(adau7118_driver);
index 0f565b8..bf181bb 100644 (file)
@@ -19,8 +19,7 @@ static const struct i2c_device_id adav803_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, adav803_id);
 
-static int adav803_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int adav803_probe(struct i2c_client *client)
 {
        return adav80x_bus_probe(&client->dev,
                devm_regmap_init_i2c(client, &adav80x_regmap_config));
@@ -30,7 +29,7 @@ static struct i2c_driver adav803_driver = {
        .driver = {
                .name = "adav803",
        },
-       .probe = adav803_probe,
+       .probe_new = adav803_probe,
        .id_table = adav803_id,
 };
 module_i2c_driver(adav803_driver);
index 2e6bafd..5c4a78c 100644 (file)
@@ -356,8 +356,7 @@ static const struct regmap_config ak4118_regmap = {
        .max_register = AK4118_REG_MAX - 1,
 };
 
-static int ak4118_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int ak4118_i2c_probe(struct i2c_client *i2c)
 {
        struct ak4118_priv *ak4118;
        int ret;
@@ -416,7 +415,7 @@ static struct i2c_driver ak4118_i2c_driver = {
                .of_match_table = of_match_ptr(ak4118_of_match),
        },
        .id_table = ak4118_id_table,
-       .probe  = ak4118_i2c_probe,
+       .probe_new = ak4118_i2c_probe,
 };
 
 module_i2c_driver(ak4118_i2c_driver);
index 91e7a57..cc803e7 100644 (file)
@@ -405,8 +405,7 @@ static const struct snd_soc_component_driver soc_component_dev_ak4535 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int ak4535_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int ak4535_i2c_probe(struct i2c_client *i2c)
 {
        struct ak4535_priv *ak4535;
        int ret;
@@ -441,7 +440,7 @@ static struct i2c_driver ak4535_i2c_driver = {
        .driver = {
                .name = "ak4535",
        },
-       .probe =    ak4535_i2c_probe,
+       .probe_new = ak4535_i2c_probe,
        .id_table = ak4535_i2c_id,
 };
 
index 034195c..55e773f 100644 (file)
 // Based on ak4535.c by Richard Purdie
 // Based on wm8753.c by Liam Girdwood
 
+/*
+ *             +-------+
+ *             |AK4613 |
+ *     SDTO1 <-|       |
+ *             |       |
+ *     SDTI1 ->|       |
+ *     SDTI2 ->|       |
+ *     SDTI3 ->|       |
+ *             +-------+
+ *
+ *       +---+
+ * clk   |   |___________________________________________...
+ *
+ * [TDM512]
+ * SDTO1  [L1][R1][L2][R2]
+ * SDTI1  [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6]
+ *
+ * [TDM256]
+ * SDTO1  [L1][R1][L2][R2]
+ * SDTI1  [L1][R1][L2][R2][L3][R3][L4][R4]
+ * SDTI2  [L5][R5][L6][R6]
+ *
+ * [TDM128]
+ * SDTO1  [L1][R1][L2][R2]
+ * SDTI1  [L1][R1][L2][R2]
+ * SDTI2  [L3][R3][L4][R4]
+ * SDTI3  [L5][R5][L6][R6]
+ *
+ * [STEREO]
+ *     Playback  2ch : SDTI1
+ *     Capture   2ch : SDTO1
+ *
+ * [TDM512]
+ *     Playback 12ch : SDTI1
+ *     Capture   4ch : SDTO1
+ *
+ * [TDM256]
+ *     Playback 12ch : SDTI1 + SDTI2
+ *     Playback  8ch : SDTI1
+ *     Capture   4ch : SDTO1
+ *
+ * [TDM128]
+ *     Playback 12ch : SDTI1 + SDTI2 + SDTI3
+ *     Playback  8ch : SDTI1 + SDTI2
+ *     Playback  4ch : SDTI1
+ *     Capture   4ch : SDTO1
+ *
+ *
+ * !!! NOTE !!!
+ *
+ * Renesas is the only user of ak4613 on upstream so far,
+ * but the chip connection is like below.
+ * Thus, Renesas can't test all connection case.
+ * Tested TDM is very limited.
+ *
+ * +-----+     +-----------+
+ * | SoC |     |  AK4613   |
+ * |     |<-----|SDTO1  IN1|<-- Mic
+ * |     |     |        IN2|
+ * |     |     |           |
+ * |     |----->|SDTI1 OUT1|--> Headphone
+ * +-----+     |SDTI2  OUT2|
+ *             |SDTI3  OUT3|
+ *             |       OUT4|
+ *             |       OUT5|
+ *             |       OUT6|
+ *             +-----------+
+ *
+ * Renesas SoC can handle [2,  6,8]    channels.
+ * Ak4613      can handle [2,4,  8,12] channels.
+ *
+ * Because of above HW connection and available channels number,
+ * Renesas could test are ...
+ *
+ *     [STEREO] Playback  2ch : SDTI1
+ *              Capture   2ch : SDTO1
+ *     [TDM256] Playback  8ch : SDTI1 (*)
+ *
+ * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode,
+ *     but could confirm is only first 2ch because only 1
+ *     Headphone is connected.
+ *
+ * see
+ *     AK4613_ENABLE_TDM_TEST
+ */
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/of_device.h>
+#include <linux/of_graph.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 /* OCTRL */
 #define OCTRL_MASK     (0x3F)
 
-struct ak4613_formats {
-       unsigned int width;
-       unsigned int fmt;
-};
+/*
+ * configs
+ *
+ * 0x000000BA
+ *
+ * B : AK4613_CONFIG_SDTI_x
+ * A : AK4613_CONFIG_MODE_x
+ */
+#define AK4613_CONFIG_SET(priv, x)      priv->configs |= AK4613_CONFIG_##x
+#define AK4613_CONFIG_GET(priv, x)     (priv->configs &  AK4613_CONFIG_##x##_MASK)
+
+/*
+ * AK4613_CONFIG_SDTI_x
+ *
+ * It indicates how many SDTIx is connected.
+ */
+#define AK4613_CONFIG_SDTI_MASK                (0xF << 4)
+#define AK4613_CONFIG_SDTI(x)          (((x) & 0xF) << 4)
+#define AK4613_CONFIG_SDTI_set(priv, x)          AK4613_CONFIG_SET(priv, SDTI(x))
+#define AK4613_CONFIG_SDTI_get(priv)   ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF)
+
+/*
+ * AK4613_CONFIG_MODE_x
+ *
+ * Same as Ctrl1 :: TDM1/TDM0
+ * No shift is requested
+ * see
+ *     AK4613_CTRL1_TO_MODE()
+ *     Table 11/12/13/14
+ */
+#define AK4613_CONFIG_MODE_MASK                (0xF)
+#define AK4613_CONFIG_MODE_STEREO      (0x0)
+#define AK4613_CONFIG_MODE_TDM512      (0x1)
+#define AK4613_CONFIG_MODE_TDM256      (0x2)
+#define AK4613_CONFIG_MODE_TDM128      (0x3)
+
+/*
+ * !!!! FIXME !!!!
+ *
+ * Because of testable HW limitation, TDM256 8ch TDM was only tested.
+ * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far.
+ * Don't hesitate to update driver, you don't need to care compatible
+ * with Renesas.
+ *
+ * #define AK4613_ENABLE_TDM_TEST
+ */
 
 struct ak4613_interface {
-       struct ak4613_formats capture;
-       struct ak4613_formats playback;
+       unsigned int width;
+       unsigned int fmt;
+       u8 dif;
 };
 
 struct ak4613_priv {
        struct mutex lock;
-       const struct ak4613_interface *iface;
-       struct snd_pcm_hw_constraint_list constraint;
+       struct snd_pcm_hw_constraint_list constraint_rates;
+       struct snd_pcm_hw_constraint_list constraint_channels;
        struct work_struct dummy_write_work;
        struct snd_soc_component *component;
        unsigned int rate;
        unsigned int sysclk;
 
        unsigned int fmt;
+       unsigned int configs;
+       int cnt;
+       u8 ctrl1;
        u8 oc;
        u8 ic;
-       int cnt;
 };
 
 /*
@@ -137,14 +268,24 @@ static const struct reg_default ak4613_reg[] = {
        { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
 };
 
-#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
-#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+/*
+ * CTRL1 register
+ * see
+ *     Table 11/12/13/14
+ */
+#define AUDIO_IFACE(_dif, _width, _fmt)                \
+       {                                       \
+               .dif    = _dif,                 \
+               .width  = _width,               \
+               .fmt    = SND_SOC_DAIFMT_##_fmt,\
+       }
 static const struct ak4613_interface ak4613_iface[] = {
-       /* capture */                           /* playback */
-       /* [0] - [2] are not supported */
-       [3] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, LEFT_J) },
-       [4] = { AUDIO_IFACE(24, I2S),           AUDIO_IFACE(24, I2S) },
+       /* It doesn't support asymmetric format */
+
+       AUDIO_IFACE(0x03, 24, LEFT_J),
+       AUDIO_IFACE(0x04, 24, I2S),
 };
+#define AK4613_CTRL1_TO_MODE(priv)     ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */
 
 static const struct regmap_config ak4613_regmap_cfg = {
        .reg_bits               = 8,
@@ -250,13 +391,14 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
                priv->cnt = 0;
        }
        if (!priv->cnt)
-               priv->iface = NULL;
+               priv->ctrl1 = 0;
        mutex_unlock(&priv->lock);
 }
 
 static void ak4613_hw_constraints(struct ak4613_priv *priv,
-                                 struct snd_pcm_runtime *runtime)
+                                 struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
        static const unsigned int ak4613_rates[] = {
                 32000,
                 44100,
@@ -267,10 +409,36 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
                176400,
                192000,
        };
-       struct snd_pcm_hw_constraint_list *constraint = &priv->constraint;
+#define AK4613_CHANNEL_2        0
+#define AK4613_CHANNEL_4        1
+#define AK4613_CHANNEL_8        2
+#define AK4613_CHANNEL_12       3
+#define AK4613_CHANNEL_NONE    -1
+       static const unsigned int ak4613_channels[] = {
+               [AK4613_CHANNEL_2]  =  2,
+               [AK4613_CHANNEL_4]  =  4,
+               [AK4613_CHANNEL_8]  =  8,
+               [AK4613_CHANNEL_12] = 12,
+       };
+#define MODE_MAX 4
+#define SDTx_MAX 4
+#define MASK(x) (1 << AK4613_CHANNEL_##x)
+       static const int mask_list[MODE_MAX][SDTx_MAX] = {
+               /*                              SDTO     SDTIx1    SDTIx2               SDTIx3 */
+               [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2),  MASK(2),             MASK(2)},
+               [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12),            MASK(12)},
+               [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8),  MASK(8)|MASK(12),    MASK(8)|MASK(12)},
+               [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4),  MASK(4)|MASK(8),     MASK(4)|MASK(8)|MASK(12)},
+       };
+       struct snd_pcm_hw_constraint_list *constraint;
+       unsigned int mask;
+       unsigned int mode;
        unsigned int fs;
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int sdti_num;
        int i;
 
+       constraint              = &priv->constraint_rates;
        constraint->list        = ak4613_rates;
        constraint->mask        = 0;
        constraint->count       = 0;
@@ -296,6 +464,41 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
 
        snd_pcm_hw_constraint_list(runtime, 0,
                                SNDRV_PCM_HW_PARAM_RATE, constraint);
+
+
+       sdti_num = AK4613_CONFIG_SDTI_get(priv);
+       if (WARN_ON(sdti_num >= SDTx_MAX))
+               return;
+
+       if (priv->cnt) {
+               /*
+                * If it was already working,
+                * the constraint is same as working mode.
+                */
+               mode = AK4613_CTRL1_TO_MODE(priv);
+               mask = 0; /* no default */
+       } else {
+               /*
+                * It is not yet working,
+                * the constraint is based on board configs.
+                * STEREO mask is default
+                */
+               mode = AK4613_CONFIG_GET(priv, MODE);
+               mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num];
+       }
+
+       if (WARN_ON(mode >= MODE_MAX))
+               return;
+
+       /* add each mode mask */
+       mask |= mask_list[mode][is_play * sdti_num];
+
+       constraint              = &priv->constraint_channels;
+       constraint->list        = ak4613_channels;
+       constraint->mask        = mask;
+       constraint->count       = sizeof(ak4613_channels);
+       snd_pcm_hw_constraint_list(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
 }
 
 static int ak4613_dai_startup(struct snd_pcm_substream *substream,
@@ -304,9 +507,10 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream,
        struct snd_soc_component *component = dai->component;
        struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
 
+       mutex_lock(&priv->lock);
+       ak4613_hw_constraints(priv, substream);
        priv->cnt++;
-
-       ak4613_hw_constraints(priv, substream->runtime);
+       mutex_unlock(&priv->lock);
 
        return 0;
 }
@@ -322,13 +526,13 @@ static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
-static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format)
 {
        struct snd_soc_component *component = dai->component;
        struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+       unsigned int fmt;
 
-       fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
-
+       fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
        switch (fmt) {
        case SND_SOC_DAIFMT_LEFT_J:
        case SND_SOC_DAIFMT_I2S:
@@ -338,24 +542,20 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                return -EINVAL;
        }
 
-       return 0;
-}
-
-static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
-                                   int is_play,
-                                   unsigned int fmt, unsigned int width)
-{
-       const struct ak4613_formats *fmts;
-
-       fmts = (is_play) ? &iface->playback : &iface->capture;
-
-       if (fmts->fmt != fmt)
-               return false;
-
-       if (fmts->width != width)
-               return false;
+       fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+       switch (fmt) {
+       case SND_SOC_DAIFMT_CBC_CFC:
+               break;
+       default:
+               /*
+                * SUPPORTME
+                *
+                * "clock provider" is not yet supperted
+                */
+               return -EINVAL;
+       }
 
-       return true;
+       return 0;
 }
 
 static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
@@ -364,14 +564,12 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
-       const struct ak4613_interface *iface;
        struct device *dev = component->dev;
        unsigned int width = params_width(params);
        unsigned int fmt = priv->fmt;
        unsigned int rate;
-       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        int i, ret;
-       u8 fmt_ctrl, ctrl2;
+       u8 ctrl2;
 
        rate = params_rate(params);
        switch (rate) {
@@ -397,40 +595,51 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
        /*
         * FIXME
         *
-        * It doesn't support TDM at this point
+        * It doesn't have full TDM suppert yet
         */
-       fmt_ctrl = NO_FMT;
        ret = -EINVAL;
-       iface = NULL;
 
        mutex_lock(&priv->lock);
-       if (priv->iface) {
-               if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
-                       iface = priv->iface;
+       if (priv->cnt > 1) {
+               /*
+                * If it was already working, use current priv->ctrl1
+                */
+               ret = 0;
        } else {
+               /*
+                * It is not yet working,
+                */
+               unsigned int channel = params_channels(params);
+               u8 tdm;
+
+               /* STEREO or TDM */
+               if (channel == 2)
+                       tdm = AK4613_CONFIG_MODE_STEREO;
+               else
+                       tdm = AK4613_CONFIG_GET(priv, MODE);
+
                for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
-                       if (!ak4613_dai_fmt_matching(ak4613_iface + i,
-                                                    is_play,
-                                                    fmt, width))
-                               continue;
-                       iface = ak4613_iface + i;
-                       break;
+                       const struct ak4613_interface *iface = ak4613_iface + i;
+
+                       if ((iface->fmt == fmt) && (iface->width == width)) {
+                               /*
+                                * Ctrl1
+                                * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0  |
+                                * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
+                                *  <  tdm  > < iface->dif >
+                                */
+                               priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
+                               ret = 0;
+                               break;
+                       }
                }
        }
-
-       if ((priv->iface == NULL) ||
-           (priv->iface == iface)) {
-               priv->iface = iface;
-               ret = 0;
-       }
        mutex_unlock(&priv->lock);
 
        if (ret < 0)
                goto hw_params_end;
 
-       fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
-
-       snd_soc_component_update_bits(component, CTRL1, FMT_MASK, fmt_ctrl);
+       snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1);
        snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2);
 
        snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic);
@@ -574,14 +783,14 @@ static struct snd_soc_dai_driver ak4613_dai = {
        .playback = {
                .stream_name    = "Playback",
                .channels_min   = 2,
-               .channels_max   = 2,
+               .channels_max   = 12,
                .rates          = AK4613_PCM_RATE,
                .formats        = AK4613_PCM_FMTBIT,
        },
        .capture = {
                .stream_name    = "Capture",
                .channels_min   = 2,
-               .channels_max   = 2,
+               .channels_max   = 4,
                .rates          = AK4613_PCM_RATE,
                .formats        = AK4613_PCM_FMTBIT,
        },
@@ -626,6 +835,7 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
 {
        struct device_node *np = dev->of_node;
        char prop[32];
+       int sdti_num;
        int i;
 
        /* Input 1 - 2 */
@@ -641,10 +851,32 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
                if (!of_get_property(np, prop, NULL))
                        priv->oc |= 1 << i;
        }
+
+       /*
+        * enable TDM256 test
+        *
+        * !!! FIXME !!!
+        *
+        * It should be configured by DT or other way
+        * if it was full supported.
+        * But it is using ifdef style for now for test
+        * purpose.
+        */
+#if defined(AK4613_ENABLE_TDM_TEST)
+       AK4613_CONFIG_SET(priv, MODE_TDM256);
+#endif
+
+       /*
+        * connected STDI
+        */
+       sdti_num = of_graph_get_endpoint_count(np);
+       if (WARN_ON((sdti_num > 3) || (sdti_num < 1)))
+               return;
+
+       AK4613_CONFIG_SDTI_set(priv, sdti_num);
 }
 
-static int ak4613_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int ak4613_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct device_node *np = dev->of_node;
@@ -655,8 +887,11 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
        regmap_cfg = NULL;
        if (np)
                regmap_cfg = of_device_get_match_data(dev);
-       else
+       else {
+               const struct i2c_device_id *id =
+                       i2c_match_id(ak4613_i2c_id, i2c);
                regmap_cfg = (const struct regmap_config *)id->driver_data;
+       }
 
        if (!regmap_cfg)
                return -EINVAL;
@@ -667,7 +902,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
 
        ak4613_parse_of(priv, dev);
 
-       priv->iface             = NULL;
+       priv->ctrl1             = 0;
        priv->cnt               = 0;
        priv->sysclk            = 0;
        INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
@@ -694,7 +929,7 @@ static struct i2c_driver ak4613_i2c_driver = {
                .name = "ak4613-codec",
                .of_match_table = ak4613_of_match,
        },
-       .probe          = ak4613_i2c_probe,
+       .probe_new      = ak4613_i2c_probe,
        .remove         = ak4613_i2c_remove,
        .id_table       = ak4613_i2c_id,
 };
index 04aef0e..d8d9cc7 100644 (file)
@@ -548,8 +548,7 @@ static const struct regmap_config ak4641_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int ak4641_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int ak4641_i2c_probe(struct i2c_client *i2c)
 {
        struct ak4641_platform_data *pdata = i2c->dev.platform_data;
        struct ak4641_priv *ak4641;
@@ -632,7 +631,7 @@ static struct i2c_driver ak4641_i2c_driver = {
        .driver = {
                .name = "ak4641",
        },
-       .probe =    ak4641_i2c_probe,
+       .probe_new = ak4641_i2c_probe,
        .remove =   ak4641_i2c_remove,
        .id_table = ak4641_i2c_id,
 };
index c284dcc..3c20ff5 100644 (file)
@@ -630,8 +630,8 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
 #endif
 
 static const struct of_device_id ak4642_of_match[];
-static int ak4642_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static const struct i2c_device_id ak4642_i2c_id[];
+static int ak4642_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct device_node *np = dev->of_node;
@@ -651,6 +651,8 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
                if (of_id)
                        drvdata = of_id->data;
        } else {
+               const struct i2c_device_id *id =
+                       i2c_match_id(ak4642_i2c_id, i2c);
                drvdata = (const struct ak4642_drvdata *)id->driver_data;
        }
 
@@ -697,7 +699,7 @@ static struct i2c_driver ak4642_i2c_driver = {
                .name = "ak4642-codec",
                .of_match_table = ak4642_of_match,
        },
-       .probe          = ak4642_i2c_probe,
+       .probe_new      = ak4642_i2c_probe,
        .id_table       = ak4642_i2c_id,
 };
 
index e9d1251..60edcbe 100644 (file)
@@ -629,8 +629,7 @@ static const struct regmap_config ak4671_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int ak4671_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
 {
        struct regmap *regmap;
        int ret;
@@ -657,7 +656,7 @@ static struct i2c_driver ak4671_i2c_driver = {
        .driver = {
                .name = "ak4671-codec",
        },
-       .probe = ak4671_i2c_probe,
+       .probe_new = ak4671_i2c_probe,
        .id_table = ak4671_i2c_id,
 };
 
index b10357a..8e6235d 100644 (file)
@@ -968,14 +968,21 @@ static const struct regmap_config alc5623_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
+static const struct i2c_device_id alc5623_i2c_table[] = {
+       {"alc5621", 0x21},
+       {"alc5622", 0x22},
+       {"alc5623", 0x23},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
+
 /*
  * ALC5623 2 wire address is determined by A1 pin
  * state during powerup.
  *    low  = 0x1a
  *    high = 0x1b
  */
-static int alc5623_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int alc5623_i2c_probe(struct i2c_client *client)
 {
        struct alc5623_platform_data *pdata;
        struct alc5623_priv *alc5623;
@@ -983,6 +990,7 @@ static int alc5623_i2c_probe(struct i2c_client *client,
        unsigned int vid1, vid2;
        int ret;
        u32 val32;
+       const struct i2c_device_id *id;
 
        alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
                               GFP_KERNEL);
@@ -1009,6 +1017,8 @@ static int alc5623_i2c_probe(struct i2c_client *client,
        }
        vid2 >>= 8;
 
+       id = i2c_match_id(alc5623_i2c_table, client);
+
        if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
                dev_err(&client->dev, "unknown or wrong codec\n");
                dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
@@ -1060,14 +1070,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
        return ret;
 }
 
-static const struct i2c_device_id alc5623_i2c_table[] = {
-       {"alc5621", 0x21},
-       {"alc5622", 0x22},
-       {"alc5623", 0x23},
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
-
 #ifdef CONFIG_OF
 static const struct of_device_id alc5623_of_match[] = {
        { .compatible = "realtek,alc5623", },
@@ -1082,7 +1084,7 @@ static struct i2c_driver alc5623_i2c_driver = {
                .name = "alc562x-codec",
                .of_match_table = of_match_ptr(alc5623_of_match),
        },
-       .probe = alc5623_i2c_probe,
+       .probe_new = alc5623_i2c_probe,
        .id_table = alc5623_i2c_table,
 };
 
index 6d7af37..641bdfd 100644 (file)
@@ -1092,18 +1092,24 @@ static const struct regmap_config alc5632_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
+static const struct i2c_device_id alc5632_i2c_table[] = {
+       {"alc5632", 0x5c},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
+
 /*
  * alc5632 2 wire address is determined by A1 pin
  * state during powerup.
  *    low  = 0x1a
  *    high = 0x1b
  */
-static int alc5632_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int alc5632_i2c_probe(struct i2c_client *client)
 {
        struct alc5632_priv *alc5632;
        int ret, ret1, ret2;
        unsigned int vid1, vid2;
+       const struct i2c_device_id *id;
 
        alc5632 = devm_kzalloc(&client->dev,
                         sizeof(struct alc5632_priv), GFP_KERNEL);
@@ -1129,6 +1135,8 @@ static int alc5632_i2c_probe(struct i2c_client *client,
 
        vid2 >>= 8;
 
+       id = i2c_match_id(alc5632_i2c_table, client);
+
        if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
                dev_err(&client->dev,
                "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
@@ -1161,12 +1169,6 @@ static int alc5632_i2c_probe(struct i2c_client *client,
        return ret;
 }
 
-static const struct i2c_device_id alc5632_i2c_table[] = {
-       {"alc5632", 0x5c},
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
-
 #ifdef CONFIG_OF
 static const struct of_device_id alc5632_of_match[] = {
        { .compatible = "realtek,alc5632", },
@@ -1181,7 +1183,7 @@ static struct i2c_driver alc5632_i2c_driver = {
                .name = "alc5632",
                .of_match_table = of_match_ptr(alc5632_of_match),
        },
-       .probe = alc5632_i2c_probe,
+       .probe_new = alc5632_i2c_probe,
        .id_table = alc5632_i2c_table,
 };
 
index 9b92e1a..8b0a9c7 100644 (file)
@@ -232,11 +232,11 @@ static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
        if (params_rate(params) != 48000)
                return -EINVAL;
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (params_width(params)) {
+       case 16:
                depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
+       case 24:
                depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
                break;
        default:
@@ -387,6 +387,7 @@ static const struct snd_soc_component_driver i2s_rx_component_driver = {
        .num_dapm_widgets       = ARRAY_SIZE(i2s_rx_dapm_widgets),
        .dapm_routes            = i2s_rx_dapm_routes,
        .num_dapm_routes        = ARRAY_SIZE(i2s_rx_dapm_routes),
+       .endianness             = 1,
 };
 
 static void *wov_map_shm(struct cros_ec_codec_priv *priv,
index 933e3d6..badfc55 100644 (file)
@@ -346,8 +346,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
        return 0;
 }
 
-static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
-                                      const struct i2c_device_id *id)
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs35l32_private *cs35l32;
        struct cs35l32_platform_data *pdata =
@@ -576,7 +575,7 @@ static struct i2c_driver cs35l32_i2c_driver = {
                   .of_match_table = cs35l32_of_match,
                   },
        .id_table = cs35l32_id,
-       .probe = cs35l32_i2c_probe,
+       .probe_new = cs35l32_i2c_probe,
        .remove = cs35l32_i2c_remove,
 };
 
index 2a6f5e4..47dc0f6 100644 (file)
@@ -1116,8 +1116,7 @@ static int cs35l33_of_get_pdata(struct device *dev,
        return 0;
 }
 
-static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
-                                      const struct i2c_device_id *id)
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs35l33_private *cs35l33;
        struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1286,7 +1285,7 @@ static struct i2c_driver cs35l33_i2c_driver = {
 
                },
        .id_table = cs35l33_id,
-       .probe = cs35l33_i2c_probe,
+       .probe_new = cs35l33_i2c_probe,
        .remove = cs35l33_i2c_remove,
 
 };
index ed67824..50d509a 100644 (file)
@@ -994,8 +994,7 @@ static const char * const cs35l34_core_supplies[] = {
        "VP",
 };
 
-static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
-                             const struct i2c_device_id *id)
+static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs35l34_private *cs35l34;
        struct cs35l34_platform_data *pdata =
@@ -1217,7 +1216,7 @@ static struct i2c_driver cs35l34_i2c_driver = {
 
                },
        .id_table = cs35l34_id,
-       .probe = cs35l34_i2c_probe,
+       .probe_new = cs35l34_i2c_probe,
        .remove = cs35l34_i2c_remove,
 
 };
index 961a3e0..6b70afb 100644 (file)
@@ -1466,8 +1466,7 @@ static const struct reg_sequence cs35l35_errata_patch[] = {
        { 0x7F, 0x00 },
 };
 
-static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
-                             const struct i2c_device_id *id)
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs35l35_private *cs35l35;
        struct device *dev = &i2c_client->dev;
@@ -1658,7 +1657,7 @@ static struct i2c_driver cs35l35_i2c_driver = {
                .of_match_table = cs35l35_of_match,
        },
        .id_table = cs35l35_id,
-       .probe = cs35l35_i2c_probe,
+       .probe_new = cs35l35_i2c_probe,
        .remove = cs35l35_i2c_remove,
 };
 
index d83c1b3..920190d 100644 (file)
@@ -1700,8 +1700,7 @@ static const struct reg_sequence cs35l36_revb0_errata_patch[] = {
        { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
 };
 
-static int cs35l36_i2c_probe(struct i2c_client *i2c_client,
-                             const struct i2c_device_id *id)
+static int cs35l36_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs35l36_private *cs35l36;
        struct device *dev = &i2c_client->dev;
@@ -1804,7 +1803,7 @@ static int cs35l36_i2c_probe(struct i2c_client *i2c_client,
        if (ret < 0) {
                dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n",
                        ret);
-               return ret;
+               goto err;
        }
 
        if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37)
@@ -1947,7 +1946,7 @@ static struct i2c_driver cs35l36_i2c_driver = {
                .of_match_table = cs35l36_of_match,
        },
        .id_table = cs35l36_id,
-       .probe = cs35l36_i2c_probe,
+       .probe_new = cs35l36_i2c_probe,
        .remove = cs35l36_i2c_remove,
 };
 module_i2c_driver(cs35l36_i2c_driver);
index 5ff0f00..37c703c 100644 (file)
@@ -29,8 +29,7 @@ static const struct i2c_device_id cs35l41_id_i2c[] = {
 
 MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
 
-static int cs35l41_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int cs35l41_i2c_probe(struct i2c_client *client)
 {
        struct cs35l41_private *cs35l41;
        struct device *dev = &client->dev;
@@ -91,7 +90,7 @@ static struct i2c_driver cs35l41_i2c_driver = {
                .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
        },
        .id_table       = cs35l41_id_i2c,
-       .probe          = cs35l41_i2c_probe,
+       .probe_new      = cs35l41_i2c_probe,
        .remove         = cs35l41_i2c_remove,
 };
 
index 3edb090..6d3070e 100644 (file)
@@ -423,7 +423,7 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
        }
 }
 
-static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = {
+static const struct cs35l41_otp_packed_element_t otp_map_1[] = {
        /* addr         shift   size */
        { 0x00002030,   0,      4 }, /*TRIM_OSC_FREQ_TRIM*/
        { 0x00002030,   7,      1 }, /*TRIM_OSC_TRIM_DONE*/
@@ -526,7 +526,7 @@ static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM]
        { 0x00017044,   0,      24 }, /*LOT_NUMBER*/
 };
 
-static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = {
+static const struct cs35l41_otp_packed_element_t otp_map_2[] = {
        /* addr         shift   size */
        { 0x00002030,   0,      4 }, /*TRIM_OSC_FREQ_TRIM*/
        { 0x00002030,   7,      1 }, /*TRIM_OSC_TRIM_DONE*/
@@ -691,35 +691,35 @@ static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
        {
                .id = 0x01,
                .map = otp_map_1,
-               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .num_elements = ARRAY_SIZE(otp_map_1),
                .bit_offset = 16,
                .word_offset = 2,
        },
        {
                .id = 0x02,
                .map = otp_map_2,
-               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .num_elements = ARRAY_SIZE(otp_map_2),
                .bit_offset = 16,
                .word_offset = 2,
        },
        {
                .id = 0x03,
                .map = otp_map_2,
-               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .num_elements = ARRAY_SIZE(otp_map_2),
                .bit_offset = 16,
                .word_offset = 2,
        },
        {
                .id = 0x06,
                .map = otp_map_2,
-               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .num_elements = ARRAY_SIZE(otp_map_2),
                .bit_offset = 16,
                .word_offset = 2,
        },
        {
                .id = 0x08,
                .map = otp_map_1,
-               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .num_elements = ARRAY_SIZE(otp_map_1),
                .bit_offset = 16,
                .word_offset = 2,
        },
@@ -842,7 +842,7 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
        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 otp_map[i].size = %d\n",
+               dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n",
                        bit_offset, word_offset, bit_sum % 32, otp_map[i].size);
                if (bit_offset + otp_map[i].size - 1 >= 32) {
                        otp_val = (otp_mem[word_offset] &
index 5f0eca2..3e68a07 100644 (file)
@@ -1025,6 +1025,8 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
        .controls = cs35l41_aud_controls,
        .num_controls = ARRAY_SIZE(cs35l41_aud_controls),
        .set_sysclk = cs35l41_component_set_sysclk,
+
+       .endianness = 1,
 };
 
 static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg)
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
new file mode 100644 (file)
index 0000000..38a4dbc
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-i2c.c -- CS35L45 I2C driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_i2c_probe(struct i2c_client *client)
+{
+       struct cs35l45_private *cs35l45;
+       struct device *dev = &client->dev;
+       int ret;
+
+       cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+       if (!cs35l45)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, cs35l45);
+       cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap);
+       if (IS_ERR(cs35l45->regmap)) {
+               ret = PTR_ERR(cs35l45->regmap);
+               dev_err(dev, "Failed to allocate register map: %d\n", ret);
+               return ret;
+       }
+
+       cs35l45->dev = dev;
+
+       return cs35l45_probe(cs35l45);
+}
+
+static int cs35l45_i2c_remove(struct i2c_client *client)
+{
+       struct cs35l45_private *cs35l45 = i2c_get_clientdata(client);
+
+       return cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+       { .compatible = "cirrus,cs35l45" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct i2c_device_id cs35l45_id_i2c[] = {
+       { "cs35l45", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
+
+static struct i2c_driver cs35l45_i2c_driver = {
+       .driver = {
+               .name           = "cs35l45",
+               .of_match_table = cs35l45_of_match,
+               .pm             = &cs35l45_pm_ops,
+       },
+       .id_table       = cs35l45_id_i2c,
+       .probe_new      = cs35l45_i2c_probe,
+       .remove         = cs35l45_i2c_remove,
+};
+module_i2c_driver(cs35l45_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
new file mode 100644 (file)
index 0000000..baaf6e0
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-spi.c -- CS35L45 SPI driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_spi_probe(struct spi_device *spi)
+{
+       struct cs35l45_private *cs35l45;
+       struct device *dev = &spi->dev;
+       int ret;
+
+       cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+       if (cs35l45 == NULL)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, cs35l45);
+       cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
+       if (IS_ERR(cs35l45->regmap)) {
+               ret = PTR_ERR(cs35l45->regmap);
+               dev_err(dev, "Failed to allocate register map: %d\n", ret);
+               return ret;
+       }
+
+       cs35l45->dev = dev;
+
+       return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_spi_remove(struct spi_device *spi)
+{
+       struct cs35l45_private *cs35l45 = spi_get_drvdata(spi);
+
+       cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+       { .compatible = "cirrus,cs35l45" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct spi_device_id cs35l45_id_spi[] = {
+       { "cs35l45", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l45_id_spi);
+
+static struct spi_driver cs35l45_spi_driver = {
+       .driver = {
+               .name           = "cs35l45",
+               .of_match_table = cs35l45_of_match,
+               .pm             = &cs35l45_pm_ops,
+       },
+       .id_table       = cs35l45_id_spi,
+       .probe          = cs35l45_spi_probe,
+       .remove         = cs35l45_spi_remove,
+};
+module_spi_driver(cs35l45_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
new file mode 100644 (file)
index 0000000..5a2c2e6
--- /dev/null
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static const struct reg_sequence cs35l45_patch[] = {
+       { 0x00000040,                   0x00000055 },
+       { 0x00000040,                   0x000000AA },
+       { 0x00000044,                   0x00000055 },
+       { 0x00000044,                   0x000000AA },
+       { 0x00006480,                   0x0830500A },
+       { 0x00007C60,                   0x1000850B },
+       { CS35L45_BOOST_OV_CFG,         0x007000D0 },
+       { CS35L45_LDPM_CONFIG,          0x0001B636 },
+       { 0x00002C08,                   0x00000009 },
+       { 0x00006850,                   0x0A30FFC4 },
+       { 0x00003820,                   0x00040100 },
+       { 0x00003824,                   0x00000000 },
+       { 0x00007CFC,                   0x62870004 },
+       { 0x00007C60,                   0x1001850B },
+       { 0x00000040,                   0x00000000 },
+       { 0x00000044,                   0x00000000 },
+       { CS35L45_BOOST_CCM_CFG,        0xF0000003 },
+       { CS35L45_BOOST_DCM_CFG,        0x08710220 },
+       { CS35L45_ERROR_RELEASE,        0x00200000 },
+};
+
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45)
+{
+       return regmap_register_patch(cs35l45->regmap, cs35l45_patch,
+                                    ARRAY_SIZE(cs35l45_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES);
+
+static const struct reg_default cs35l45_defaults[] = {
+       { CS35L45_BLOCK_ENABLES,                0x00003323 },
+       { CS35L45_BLOCK_ENABLES2,               0x00000010 },
+       { CS35L45_REFCLK_INPUT,                 0x00000510 },
+       { CS35L45_GLOBAL_SAMPLE_RATE,           0x00000003 },
+       { CS35L45_ASP_ENABLES1,                 0x00000000 },
+       { CS35L45_ASP_CONTROL1,                 0x00000028 },
+       { CS35L45_ASP_CONTROL2,                 0x18180200 },
+       { CS35L45_ASP_CONTROL3,                 0x00000002 },
+       { CS35L45_ASP_FRAME_CONTROL1,           0x03020100 },
+       { CS35L45_ASP_FRAME_CONTROL2,           0x00000004 },
+       { CS35L45_ASP_FRAME_CONTROL5,           0x00000100 },
+       { CS35L45_ASP_DATA_CONTROL1,            0x00000018 },
+       { CS35L45_ASP_DATA_CONTROL5,            0x00000018 },
+       { CS35L45_DACPCM1_INPUT,                0x00000008 },
+       { CS35L45_ASPTX1_INPUT,                 0x00000018 },
+       { CS35L45_ASPTX2_INPUT,                 0x00000019 },
+       { CS35L45_ASPTX3_INPUT,                 0x00000020 },
+       { CS35L45_ASPTX4_INPUT,                 0x00000028 },
+       { CS35L45_ASPTX5_INPUT,                 0x00000048 },
+       { CS35L45_AMP_PCM_CONTROL,              0x00100000 },
+};
+
+static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L45_DEVID ... CS35L45_OTPID:
+       case CS35L45_SFT_RESET:
+       case CS35L45_GLOBAL_ENABLES:
+       case CS35L45_BLOCK_ENABLES:
+       case CS35L45_BLOCK_ENABLES2:
+       case CS35L45_ERROR_RELEASE:
+       case CS35L45_REFCLK_INPUT:
+       case CS35L45_GLOBAL_SAMPLE_RATE:
+       case CS35L45_ASP_ENABLES1:
+       case CS35L45_ASP_CONTROL1:
+       case CS35L45_ASP_CONTROL2:
+       case CS35L45_ASP_CONTROL3:
+       case CS35L45_ASP_FRAME_CONTROL1:
+       case CS35L45_ASP_FRAME_CONTROL2:
+       case CS35L45_ASP_FRAME_CONTROL5:
+       case CS35L45_ASP_DATA_CONTROL1:
+       case CS35L45_ASP_DATA_CONTROL5:
+       case CS35L45_DACPCM1_INPUT:
+       case CS35L45_ASPTX1_INPUT:
+       case CS35L45_ASPTX2_INPUT:
+       case CS35L45_ASPTX3_INPUT:
+       case CS35L45_ASPTX4_INPUT:
+       case CS35L45_ASPTX5_INPUT:
+       case CS35L45_AMP_PCM_CONTROL:
+       case CS35L45_AMP_PCM_HPF_TST:
+       case CS35L45_IRQ1_EINT_4:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L45_DEVID ... CS35L45_OTPID:
+       case CS35L45_SFT_RESET:
+       case CS35L45_GLOBAL_ENABLES:
+       case CS35L45_ERROR_RELEASE:
+       case CS35L45_AMP_PCM_HPF_TST:   /* not cachable */
+       case CS35L45_IRQ1_EINT_4:
+               return true;
+       default:
+               return false;
+       }
+}
+
+const struct regmap_config cs35l45_i2c_regmap = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L45_LASTREG,
+       .reg_defaults = cs35l45_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+       .volatile_reg = cs35l45_volatile_reg,
+       .readable_reg = cs35l45_readable_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45_TABLES);
+
+const struct regmap_config cs35l45_spi_regmap = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .pad_bits = 16,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L45_LASTREG,
+       .reg_defaults = cs35l45_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+       .volatile_reg = cs35l45_volatile_reg,
+       .readable_reg = cs35l45_readable_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45_TABLES);
+
+static const struct {
+       u8 cfg_id;
+       u32 freq;
+} cs35l45_pll_refclk_freq[] = {
+       { 0x0C,   128000 },
+       { 0x0F,   256000 },
+       { 0x11,   384000 },
+       { 0x12,   512000 },
+       { 0x15,   768000 },
+       { 0x17,  1024000 },
+       { 0x19,  1411200 },
+       { 0x1B,  1536000 },
+       { 0x1C,  2116800 },
+       { 0x1D,  2048000 },
+       { 0x1E,  2304000 },
+       { 0x1F,  2822400 },
+       { 0x21,  3072000 },
+       { 0x23,  4233600 },
+       { 0x24,  4096000 },
+       { 0x25,  4608000 },
+       { 0x26,  5644800 },
+       { 0x27,  6000000 },
+       { 0x28,  6144000 },
+       { 0x29,  6350400 },
+       { 0x2A,  6912000 },
+       { 0x2D,  7526400 },
+       { 0x2E,  8467200 },
+       { 0x2F,  8192000 },
+       { 0x30,  9216000 },
+       { 0x31, 11289600 },
+       { 0x33, 12288000 },
+       { 0x37, 16934400 },
+       { 0x38, 18432000 },
+       { 0x39, 22579200 },
+       { 0x3B, 24576000 },
+};
+
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq)
+{
+       int i;
+
+       if (freq == 0)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) {
+               if (cs35l45_pll_refclk_freq[i].freq == freq)
+                       return cs35l45_pll_refclk_freq[i].cfg_id;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45_TABLES);
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver tables");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
new file mode 100644 (file)
index 0000000..2367c1a
--- /dev/null
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45.c - CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+       dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES,
+                            CS35L45_GLOBAL_EN_MASK);
+
+               usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100);
+
+               regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const char * const cs35l45_asp_tx_txt[] = {
+       "Zero", "ASP_RX1", "ASP_RX2",
+       "VMON", "IMON", "ERR_VOL",
+       "VDD_BATTMON", "VDD_BSTMON",
+       "Interpolator", "IL_TARGET",
+};
+
+static const unsigned int cs35l45_asp_tx_val[] = {
+       CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+       CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+       CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+       CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
+};
+
+static const struct soc_enum cs35l45_asp_tx_enums[] = {
+       SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+                             cs35l45_asp_tx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+                             cs35l45_asp_tx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+                             cs35l45_asp_tx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+                             cs35l45_asp_tx_val),
+       SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+                             cs35l45_asp_tx_val),
+};
+
+static const char * const cs35l45_dac_txt[] = {
+       "Zero", "ASP_RX1", "ASP_RX2"
+};
+
+static const unsigned int cs35l45_dac_val[] = {
+       CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2
+};
+
+static const struct soc_enum cs35l45_dacpcm_enums[] = {
+       SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+                             ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt,
+                             cs35l45_dac_val),
+};
+
+static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
+       SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]),
+       SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]),
+       SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]),
+       SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]),
+       SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
+       SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
+};
+
+static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
+                           cs35l45_global_en_ev,
+                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_SIGGEN("VMON_SRC"),
+       SND_SOC_DAPM_SIGGEN("IMON_SRC"),
+       SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"),
+       SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"),
+       SND_SOC_DAPM_SIGGEN("ERR_VOL"),
+       SND_SOC_DAPM_SIGGEN("AMP_INTP"),
+       SND_SOC_DAPM_SIGGEN("IL_TARGET"),
+       SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0),
+       SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0),
+       SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES,
+                        CS35L45_VDD_BATTMON_EN_SHIFT, 0),
+       SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES,
+                        CS35L45_VDD_BSTMON_EN_SHIFT, 0),
+
+       SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0),
+
+       SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0),
+
+       SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]),
+       SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]),
+       SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]),
+       SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
+       SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
+
+       SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
+
+       SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("SPK"),
+};
+
+#define CS35L45_ASP_MUX_ROUTE(name) \
+       { name" Source", "ASP_RX1",      "ASP_RX1" }, \
+       { name" Source", "ASP_RX2",      "ASP_RX2" }, \
+       { name" Source", "VMON",         "VMON" }, \
+       { name" Source", "IMON",         "IMON" }, \
+       { name" Source", "ERR_VOL",      "ERR_VOL" }, \
+       { name" Source", "VDD_BATTMON",  "VDD_BATTMON" }, \
+       { name" Source", "VDD_BSTMON",   "VDD_BSTMON" }, \
+       { name" Source", "Interpolator", "AMP_INTP" }, \
+       { name" Source", "IL_TARGET",    "IL_TARGET" }
+
+#define CS35L45_DAC_MUX_ROUTE(name) \
+       { name" Source", "ASP_RX1",     "ASP_RX1" }, \
+       { name" Source", "ASP_RX2",     "ASP_RX2" }
+
+static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
+       /* Feedback */
+       { "VMON", NULL, "VMON_SRC" },
+       { "IMON", NULL, "IMON_SRC" },
+       { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+       { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+       { "Capture", NULL, "ASP_TX1"},
+       { "Capture", NULL, "ASP_TX2"},
+       { "Capture", NULL, "ASP_TX3"},
+       { "Capture", NULL, "ASP_TX4"},
+       { "Capture", NULL, "ASP_TX5"},
+       { "ASP_TX1", NULL, "ASP_TX1 Source"},
+       { "ASP_TX2", NULL, "ASP_TX2 Source"},
+       { "ASP_TX3", NULL, "ASP_TX3 Source"},
+       { "ASP_TX4", NULL, "ASP_TX4 Source"},
+       { "ASP_TX5", NULL, "ASP_TX5 Source"},
+
+       { "ASP_TX1", NULL, "ASP_EN" },
+       { "ASP_TX2", NULL, "ASP_EN" },
+       { "ASP_TX3", NULL, "ASP_EN" },
+       { "ASP_TX4", NULL, "ASP_EN" },
+       { "ASP_TX1", NULL, "GLOBAL_EN" },
+       { "ASP_TX2", NULL, "GLOBAL_EN" },
+       { "ASP_TX3", NULL, "GLOBAL_EN" },
+       { "ASP_TX4", NULL, "GLOBAL_EN" },
+       { "ASP_TX5", NULL, "GLOBAL_EN" },
+
+       CS35L45_ASP_MUX_ROUTE("ASP_TX1"),
+       CS35L45_ASP_MUX_ROUTE("ASP_TX2"),
+       CS35L45_ASP_MUX_ROUTE("ASP_TX3"),
+       CS35L45_ASP_MUX_ROUTE("ASP_TX4"),
+       CS35L45_ASP_MUX_ROUTE("ASP_TX5"),
+
+       /* Playback */
+       { "ASP_RX1", NULL, "Playback" },
+       { "ASP_RX2", NULL, "Playback" },
+       { "ASP_RX1", NULL, "ASP_EN" },
+       { "ASP_RX2", NULL, "ASP_EN" },
+
+       { "AMP", NULL, "DACPCM1 Source"},
+       { "AMP", NULL, "GLOBAL_EN"},
+
+       CS35L45_DAC_MUX_ROUTE("DACPCM1"),
+
+       { "SPK", NULL, "AMP"},
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+       /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */
+       SOC_SINGLE_S_TLV("Digital PCM Volume",
+                        CS35L45_AMP_PCM_CONTROL,
+                        CS35L45_AMP_VOL_PCM_SHIFT + 1,
+                        -409, 48,
+                        (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
+                        0, cs35l45_dig_pcm_vol_tlv),
+};
+
+static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
+{
+       unsigned int val;
+       int freq_id;
+
+       freq_id = cs35l45_get_clk_freq_id(freq);
+       if (freq_id < 0) {
+               dev_err(cs35l45->dev, "Invalid freq: %u\n", freq);
+               return -EINVAL;
+       }
+
+       regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val);
+       val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT;
+       if (val == freq_id)
+               return 0;
+
+       regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+       regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT,
+                          CS35L45_PLL_REFCLK_FREQ_MASK,
+                          freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT);
+       regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+       regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+       regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+
+       return 0;
+}
+
+static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component);
+       unsigned int asp_fmt, fsync_inv, bclk_inv;
+
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+       case SND_SOC_DAIFMT_CBC_CFC:
+               break;
+       default:
+               dev_err(cs35l45->dev, "Invalid DAI clocking\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               asp_fmt = CS35l45_ASP_FMT_DSP_A;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               asp_fmt = CS35L45_ASP_FMT_I2S;
+               break;
+       default:
+               dev_err(cs35l45->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_IF:
+               fsync_inv = 1;
+               bclk_inv = 0;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               fsync_inv = 0;
+               bclk_inv = 1;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               fsync_inv = 1;
+               bclk_inv = 1;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+               fsync_inv = 0;
+               bclk_inv = 0;
+               break;
+       default:
+               dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+                          CS35L45_ASP_FMT_MASK |
+                          CS35L45_ASP_FSYNC_INV_MASK |
+                          CS35L45_ASP_BCLK_INV_MASK,
+                          (asp_fmt << CS35L45_ASP_FMT_SHIFT) |
+                          (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) |
+                          (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT));
+
+       return 0;
+}
+
+static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+       unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt;
+       int bclk;
+
+       switch (params_rate(params)) {
+       case 44100:
+               global_fs = CS35L45_44P100_KHZ;
+               break;
+       case 48000:
+               global_fs = CS35L45_48P0_KHZ;
+               break;
+       case 88200:
+               global_fs = CS35L45_88P200_KHZ;
+               break;
+       case 96000:
+               global_fs = CS35L45_96P0_KHZ;
+               break;
+       default:
+               dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n",
+                        params_rate(params));
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE,
+                          CS35L45_GLOBAL_FS_MASK,
+                          global_fs << CS35L45_GLOBAL_FS_SHIFT);
+
+       asp_wl = params_width(params);
+
+       if (cs35l45->slot_width)
+               asp_width = cs35l45->slot_width;
+       else
+               asp_width = params_width(params);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+                                  CS35L45_ASP_WIDTH_RX_MASK,
+                                  asp_width << CS35L45_ASP_WIDTH_RX_SHIFT);
+
+               regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5,
+                                  CS35L45_ASP_WL_MASK,
+                                  asp_wl << CS35L45_ASP_WL_SHIFT);
+       } else {
+               regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+                                  CS35L45_ASP_WIDTH_TX_MASK,
+                                  asp_width << CS35L45_ASP_WIDTH_TX_SHIFT);
+
+               regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1,
+                                  CS35L45_ASP_WL_MASK,
+                                  asp_wl << CS35L45_ASP_WL_SHIFT);
+       }
+
+       if (cs35l45->sysclk_set)
+               return 0;
+
+       /* I2S always has an even number of channels */
+       regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt);
+       asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT;
+       if (asp_fmt == CS35L45_ASP_FMT_I2S)
+               slot_multiple = 2;
+       else
+               slot_multiple = 1;
+
+       bclk = snd_soc_tdm_params_to_bclk(params, asp_width,
+                                         cs35l45->slot_count, slot_multiple);
+
+       return cs35l45_set_pll(cs35l45, bclk);
+}
+
+static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai,
+                                   unsigned int tx_mask, unsigned int rx_mask,
+                                   int slots, int slot_width)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+
+       if (slot_width && ((slot_width < 16) || (slot_width > 128)))
+               return -EINVAL;
+
+       cs35l45->slot_width = slot_width;
+       cs35l45->slot_count = slots;
+
+       return 0;
+}
+
+static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+       int ret;
+
+       if (clk_id != 0) {
+               dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id);
+               return -EINVAL;
+       }
+
+       cs35l45->sysclk_set = false;
+       if (freq == 0)
+               return 0;
+
+       ret = cs35l45_set_pll(cs35l45, freq);
+       if (ret < 0)
+               return -EINVAL;
+
+       cs35l45->sysclk_set = true;
+
+       return 0;
+}
+
+static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+       struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+       unsigned int global_fs, val, hpf_tune;
+
+       if (mute)
+               return 0;
+
+       regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs);
+       global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT;
+       switch (global_fs) {
+       case CS35L45_44P100_KHZ:
+               hpf_tune = CS35L45_HPF_44P1;
+               break;
+       case CS35L45_88P200_KHZ:
+               hpf_tune = CS35L45_HPF_88P2;
+               break;
+       default:
+               hpf_tune = CS35l45_HPF_DEFAULT;
+               break;
+       }
+
+       regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val);
+       if (val != hpf_tune) {
+               struct reg_sequence hpf_override_seq[] = {
+                       { 0x00000040,                   0x00000055 },
+                       { 0x00000040,                   0x000000AA },
+                       { 0x00000044,                   0x00000055 },
+                       { 0x00000044,                   0x000000AA },
+                       { CS35L45_AMP_PCM_HPF_TST,      hpf_tune },
+                       { 0x00000040,                   0x00000000 },
+                       { 0x00000044,                   0x00000000 },
+               };
+               regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq,
+                                      ARRAY_SIZE(hpf_override_seq));
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = {
+       .set_fmt = cs35l45_asp_set_fmt,
+       .hw_params = cs35l45_asp_hw_params,
+       .set_tdm_slot = cs35l45_asp_set_tdm_slot,
+       .set_sysclk = cs35l45_asp_set_sysclk,
+       .mute_stream = cs35l45_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs35l45_dai[] = {
+       {
+               .name = "cs35l45",
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = CS35L45_RATES,
+                       .formats = CS35L45_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "Capture",
+                       .channels_min = 1,
+                       .channels_max = 5,
+                       .rates = CS35L45_RATES,
+                       .formats = CS35L45_FORMATS,
+               },
+               .symmetric_rate = true,
+               .symmetric_sample_bits = true,
+               .ops = &cs35l45_asp_dai_ops,
+       },
+};
+
+static const struct snd_soc_component_driver cs35l45_component = {
+       .dapm_widgets = cs35l45_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
+
+       .dapm_routes = cs35l45_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes),
+
+       .controls = cs35l45_controls,
+       .num_controls = ARRAY_SIZE(cs35l45_controls),
+
+       .name = "cs35l45",
+};
+
+static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
+{
+       struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+       regcache_cache_only(cs35l45->regmap, true);
+
+       dev_dbg(cs35l45->dev, "Runtime suspended\n");
+
+       return 0;
+}
+
+static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
+{
+       struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(cs35l45->dev, "Runtime resume\n");
+
+       regcache_cache_only(cs35l45->regmap, false);
+       ret = regcache_sync(cs35l45->regmap);
+       if (ret != 0)
+               dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
+
+       /* Clear global error status */
+       regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+       regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+       regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+       return ret;
+}
+
+static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
+{
+       unsigned int val;
+
+       if (device_property_read_u32(cs35l45->dev,
+                                    "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
+               regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3,
+                                  CS35L45_ASP_DOUT_HIZ_CTRL_MASK,
+                                  val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT);
+       }
+
+       return 0;
+}
+
+static int cs35l45_initialize(struct cs35l45_private *cs35l45)
+{
+       struct device *dev = cs35l45->dev;
+       unsigned int dev_id[5];
+       unsigned int sts;
+       int ret;
+
+       ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts,
+                                      (sts & CS35L45_OTP_BOOT_DONE_STS_MASK),
+                                      1000, 5000);
+       if (ret < 0) {
+               dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n");
+               return ret;
+       }
+
+       ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id));
+       if (ret) {
+               dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret);
+               return ret;
+       }
+
+       switch (dev_id[0]) {
+       case 0x35A450:
+               break;
+       default:
+               dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]);
+               return -ENODEV;
+       }
+
+       dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n",
+                dev_id[1], dev_id[4]);
+
+       regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4,
+                    CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK);
+
+       ret = cs35l45_apply_patch(cs35l45);
+       if (ret < 0) {
+               dev_err(dev, "Failed to apply init patch %d\n", ret);
+               return ret;
+       }
+
+       ret = cs35l45_apply_property_config(cs35l45);
+       if (ret < 0)
+               return ret;
+
+       pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+       pm_runtime_use_autosuspend(cs35l45->dev);
+       pm_runtime_set_active(cs35l45->dev);
+       pm_runtime_enable(cs35l45->dev);
+
+       return 0;
+}
+
+int cs35l45_probe(struct cs35l45_private *cs35l45)
+{
+       struct device *dev = cs35l45->dev;
+       int ret;
+
+       cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
+       if (IS_ERR(cs35l45->vdd_batt))
+               return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt),
+                                    "Failed to request vdd-batt\n");
+
+       cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a");
+       if (IS_ERR(cs35l45->vdd_a))
+               return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a),
+                                    "Failed to request vdd-a\n");
+
+       /* VDD_BATT must always be enabled before other supplies */
+       ret = regulator_enable(cs35l45->vdd_batt);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n");
+
+       ret = regulator_enable(cs35l45->vdd_a);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to enable vdd-a\n");
+
+       /* If reset is shared only one instance can claim it */
+       cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(cs35l45->reset_gpio)) {
+               ret = PTR_ERR(cs35l45->reset_gpio);
+               cs35l45->reset_gpio = NULL;
+               if (ret == -EBUSY) {
+                       dev_dbg(dev, "Reset line busy, assuming shared reset\n");
+               } else {
+                       dev_err_probe(dev, ret, "Failed to get reset GPIO\n");
+                       goto err;
+               }
+       }
+
+       if (cs35l45->reset_gpio) {
+               usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100);
+               gpiod_set_value_cansleep(cs35l45->reset_gpio, 1);
+       }
+
+       usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100);
+
+       ret = cs35l45_initialize(cs35l45);
+       if (ret < 0)
+               goto err_reset;
+
+       ret = devm_snd_soc_register_component(dev, &cs35l45_component,
+                                             cs35l45_dai,
+                                             ARRAY_SIZE(cs35l45_dai));
+       if (ret < 0)
+               goto err_reset;
+
+       return 0;
+
+err_reset:
+       gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+err:
+       regulator_disable(cs35l45->vdd_a);
+       regulator_disable(cs35l45->vdd_batt);
+
+       return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
+
+int cs35l45_remove(struct cs35l45_private *cs35l45)
+{
+       pm_runtime_disable(cs35l45->dev);
+
+       gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+       regulator_disable(cs35l45->vdd_a);
+       /* VDD_BATT must be the last to power-off */
+       regulator_disable(cs35l45->vdd_batt);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45);
+
+const struct dev_pm_ops cs35l45_pm_ops = {
+       SET_RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45);
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644 (file)
index 0000000..4e266d1
--- /dev/null
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * cs35l45.h - CS35L45 ALSA SoC audio driver
+ *
+ * Copyright 2019-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef CS35L45_H
+#define CS35L45_H
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define CS35L45_DEVID                          0x00000000
+#define CS35L45_REVID                          0x00000004
+#define CS35L45_RELID                          0x0000000C
+#define CS35L45_OTPID                          0x00000010
+#define CS35L45_SFT_RESET                      0x00000020
+#define CS35L45_GLOBAL_ENABLES                 0x00002014
+#define CS35L45_BLOCK_ENABLES                  0x00002018
+#define CS35L45_BLOCK_ENABLES2                 0x0000201C
+#define CS35L45_ERROR_RELEASE                  0x00002034
+#define CS35L45_REFCLK_INPUT                   0x00002C04
+#define CS35L45_GLOBAL_SAMPLE_RATE             0x00002C0C
+#define CS35L45_BOOST_CCM_CFG                  0x00003808
+#define CS35L45_BOOST_DCM_CFG                  0x0000380C
+#define CS35L45_BOOST_OV_CFG                   0x0000382C
+#define CS35L45_ASP_ENABLES1                   0x00004800
+#define CS35L45_ASP_CONTROL1                   0x00004804
+#define CS35L45_ASP_CONTROL2                   0x00004808
+#define CS35L45_ASP_CONTROL3                   0x0000480C
+#define CS35L45_ASP_FRAME_CONTROL1             0x00004810
+#define CS35L45_ASP_FRAME_CONTROL2             0x00004814
+#define CS35L45_ASP_FRAME_CONTROL5             0x00004820
+#define CS35L45_ASP_DATA_CONTROL1              0x00004830
+#define CS35L45_ASP_DATA_CONTROL5              0x00004840
+#define CS35L45_DACPCM1_INPUT                  0x00004C00
+#define CS35L45_ASPTX1_INPUT                   0x00004C20
+#define CS35L45_ASPTX2_INPUT                   0x00004C24
+#define CS35L45_ASPTX3_INPUT                   0x00004C28
+#define CS35L45_ASPTX4_INPUT                   0x00004C2C
+#define CS35L45_ASPTX5_INPUT                   0x00004C30
+#define CS35L45_LDPM_CONFIG                    0x00006404
+#define CS35L45_AMP_PCM_CONTROL                        0x00007000
+#define CS35L45_AMP_PCM_HPF_TST                        0x00007004
+#define CS35L45_IRQ1_EINT_4                    0x0000E01C
+#define CS35L45_LASTREG                                0x0000E01C
+
+/* SFT_RESET */
+#define CS35L45_SOFT_RESET_TRIGGER             0x5A000000
+
+/* GLOBAL_ENABLES */
+#define CS35L45_GLOBAL_EN_SHIFT                        0
+#define CS35L45_GLOBAL_EN_MASK                 BIT(0)
+
+/* BLOCK_ENABLES */
+#define CS35L45_IMON_EN_SHIFT                  13
+#define CS35L45_VMON_EN_SHIFT                  12
+#define CS35L45_VDD_BSTMON_EN_SHIFT            9
+#define CS35L45_VDD_BATTMON_EN_SHIFT           8
+#define CS35L45_BST_EN_SHIFT                   4
+#define CS35L45_BST_EN_MASK                    GENMASK(5, 4)
+
+#define CS35L45_BST_DISABLE_FET_ON              0x01
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT                   27
+
+/* ERROR_RELEASE */
+#define CS35L45_GLOBAL_ERR_RLS_MASK            BIT(11)
+
+/* REFCLK_INPUT */
+#define CS35L45_PLL_FORCE_EN_SHIFT             16
+#define CS35L45_PLL_FORCE_EN_MASK              BIT(16)
+#define CS35L45_PLL_OPEN_LOOP_SHIFT            11
+#define CS35L45_PLL_OPEN_LOOP_MASK             BIT(11)
+#define CS35L45_PLL_REFCLK_FREQ_SHIFT          5
+#define CS35L45_PLL_REFCLK_FREQ_MASK           GENMASK(10, 5)
+#define CS35L45_PLL_REFCLK_EN_SHIFT            4
+#define CS35L45_PLL_REFCLK_EN_MASK             BIT(4)
+#define CS35L45_PLL_REFCLK_SEL_SHIFT           0
+#define CS35L45_PLL_REFCLK_SEL_MASK            GENMASK(2, 0)
+
+#define CS35L45_PLL_REFCLK_SEL_BCLK            0x0
+
+/* GLOBAL_SAMPLE_RATE */
+#define CS35L45_GLOBAL_FS_SHIFT                        0
+#define CS35L45_GLOBAL_FS_MASK                 GENMASK(4, 0)
+
+#define CS35L45_48P0_KHZ                       0x03
+#define CS35L45_96P0_KHZ                       0x04
+#define CS35L45_44P100_KHZ                     0x0B
+#define CS35L45_88P200_KHZ                     0x0C
+
+/* ASP_ENABLES_1 */
+#define CS35L45_ASP_RX2_EN_SHIFT               17
+#define CS35L45_ASP_RX1_EN_SHIFT               16
+#define CS35L45_ASP_TX5_EN_SHIFT               4
+#define CS35L45_ASP_TX4_EN_SHIFT               3
+#define CS35L45_ASP_TX3_EN_SHIFT               2
+#define CS35L45_ASP_TX2_EN_SHIFT               1
+#define CS35L45_ASP_TX1_EN_SHIFT               0
+
+/* ASP_CONTROL2 */
+#define CS35L45_ASP_WIDTH_RX_SHIFT             24
+#define CS35L45_ASP_WIDTH_RX_MASK              GENMASK(31, 24)
+#define CS35L45_ASP_WIDTH_TX_SHIFT             16
+#define CS35L45_ASP_WIDTH_TX_MASK              GENMASK(23, 16)
+#define CS35L45_ASP_FMT_SHIFT                  8
+#define CS35L45_ASP_FMT_MASK                   GENMASK(10, 8)
+#define CS35L45_ASP_BCLK_INV_SHIFT             6
+#define CS35L45_ASP_BCLK_INV_MASK              BIT(6)
+#define CS35L45_ASP_FSYNC_INV_SHIFT            2
+#define CS35L45_ASP_FSYNC_INV_MASK             BIT(2)
+
+#define CS35l45_ASP_FMT_DSP_A                  0
+#define CS35L45_ASP_FMT_I2S                    2
+
+/* ASP_CONTROL3 */
+#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT                0
+#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK         GENMASK(1, 0)
+
+/* ASP_FRAME_CONTROL1 */
+#define CS35L45_ASP_TX4_SLOT_SHIFT             24
+#define CS35L45_ASP_TX4_SLOT_MASK              GENMASK(29, 24)
+#define CS35L45_ASP_TX3_SLOT_SHIFT             16
+#define CS35L45_ASP_TX3_SLOT_MASK              GENMASK(21, 16)
+#define CS35L45_ASP_TX2_SLOT_SHIFT             8
+#define CS35L45_ASP_TX2_SLOT_MASK              GENMASK(13, 8)
+#define CS35L45_ASP_TX1_SLOT_SHIFT             0
+#define CS35L45_ASP_TX1_SLOT_MASK              GENMASK(5, 0)
+
+#define CS35L45_ASP_TX_ALL_SLOTS               (CS35L45_ASP_TX4_SLOT_MASK | \
+                                               CS35L45_ASP_TX3_SLOT_MASK  | \
+                                               CS35L45_ASP_TX2_SLOT_MASK  | \
+                                               CS35L45_ASP_TX1_SLOT_MASK)
+/* ASP_FRAME_CONTROL5 */
+#define CS35L45_ASP_RX2_SLOT_SHIFT             8
+#define CS35L45_ASP_RX2_SLOT_MASK              GENMASK(13, 8)
+#define CS35L45_ASP_RX1_SLOT_SHIFT             0
+#define CS35L45_ASP_RX1_SLOT_MASK              GENMASK(5, 0)
+
+#define CS35L45_ASP_RX_ALL_SLOTS               (CS35L45_ASP_RX2_SLOT_MASK | \
+                                               CS35L45_ASP_RX1_SLOT_MASK)
+
+/* ASP_DATA_CONTROL1 */
+/* ASP_DATA_CONTROL5 */
+#define CS35L45_ASP_WL_SHIFT                   0
+#define CS35L45_ASP_WL_MASK                    GENMASK(5, 0)
+
+/* AMP_PCM_CONTROL */
+#define CS35L45_AMP_VOL_PCM_SHIFT              0
+#define CS35L45_AMP_VOL_PCM_WIDTH              11
+
+/* AMP_PCM_HPF_TST */
+#define CS35l45_HPF_DEFAULT                    0x00000000
+#define CS35L45_HPF_44P1                       0x000108BD
+#define CS35L45_HPF_88P2                       0x0001045F
+
+/* IRQ1_EINT_4 */
+#define CS35L45_OTP_BOOT_DONE_STS_MASK         BIT(1)
+#define CS35L45_OTP_BUSY_MASK                  BIT(0)
+
+/* Mixer sources */
+#define CS35L45_PCM_SRC_MASK                   0x7F
+#define CS35L45_PCM_SRC_ZERO                   0x00
+#define CS35L45_PCM_SRC_ASP_RX1                        0x08
+#define CS35L45_PCM_SRC_ASP_RX2                        0x09
+#define CS35L45_PCM_SRC_VMON                   0x18
+#define CS35L45_PCM_SRC_IMON                   0x19
+#define CS35L45_PCM_SRC_ERR_VOL                        0x20
+#define CS35L45_PCM_SRC_CLASSH_TGT             0x21
+#define CS35L45_PCM_SRC_VDD_BATTMON            0x28
+#define CS35L45_PCM_SRC_VDD_BSTMON             0x29
+#define CS35L45_PCM_SRC_TEMPMON                        0x3A
+#define CS35L45_PCM_SRC_INTERPOLATOR           0x40
+#define CS35L45_PCM_SRC_IL_TARGET              0x48
+
+#define CS35L45_RESET_HOLD_US                  2000
+#define CS35L45_RESET_US                       2000
+#define CS35L45_POST_GLOBAL_EN_US              5000
+#define CS35L45_PRE_GLOBAL_DIS_US              3000
+
+#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                        SNDRV_PCM_FMTBIT_S24_3LE| \
+                        SNDRV_PCM_FMTBIT_S24_LE)
+
+#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \
+                      SNDRV_PCM_RATE_48000 | \
+                      SNDRV_PCM_RATE_88200 | \
+                      SNDRV_PCM_RATE_96000)
+
+struct cs35l45_private {
+       struct device *dev;
+       struct regmap *regmap;
+       struct gpio_desc *reset_gpio;
+       struct regulator *vdd_batt;
+       struct regulator *vdd_a;
+       bool initialized;
+       bool sysclk_set;
+       u8 slot_width;
+       u8 slot_count;
+};
+
+extern const struct dev_pm_ops cs35l45_pm_ops;
+extern const struct regmap_config cs35l45_i2c_regmap;
+extern const struct regmap_config cs35l45_spi_regmap;
+int cs35l45_apply_patch(struct cs35l45_private *cs43l45);
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq);
+int cs35l45_probe(struct cs35l45_private *cs35l45);
+int cs35l45_remove(struct cs35l45_private *cs35l45);
+
+#endif /* CS35L45_H */
index 20126cc..881c5ba 100644 (file)
@@ -663,6 +663,7 @@ static const struct snd_soc_component_driver soc_component_cs4234 = {
        .non_legacy_dai_naming  = 1,
        .idle_bias_on           = 1,
        .suspend_bias_off       = 1,
+       .endianness             = 1,
 };
 
 static const struct regmap_config cs4234_regmap = {
@@ -731,7 +732,7 @@ static int cs4234_powerup(struct cs4234 *cs4234)
        return 0;
 }
 
-static int cs4234_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id)
+static int cs4234_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs4234 *cs4234;
        struct device *dev = &i2c_client->dev;
@@ -908,7 +909,7 @@ static struct i2c_driver cs4234_i2c_driver = {
                .pm = &cs4234_pm,
                .of_match_table = cs4234_of_match,
        },
-       .probe =        cs4234_i2c_probe,
+       .probe_new =    cs4234_i2c_probe,
        .remove =       cs4234_i2c_remove,
 };
 module_i2c_driver(cs4234_i2c_driver);
index 4415fb3..86bfa8d 100644 (file)
@@ -568,8 +568,7 @@ static const struct regmap_config cs4265_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int cs4265_i2c_probe(struct i2c_client *i2c_client,
-                            const struct i2c_device_id *id)
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs4265_private *cs4265;
        int ret;
@@ -653,7 +652,7 @@ static struct i2c_driver cs4265_i2c_driver = {
                .of_match_table = cs4265_of_match,
        },
        .id_table = cs4265_id,
-       .probe =    cs4265_i2c_probe,
+       .probe_new = cs4265_i2c_probe,
        .remove =   cs4265_i2c_remove,
 };
 
index 2d239e9..531f63b 100644 (file)
 #include <linux/gpio/consumer.h>
 #include <linux/of_device.h>
 
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here.  That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \
-                       SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_S16_LE  | \
+                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 /* CS4270 registers addresses */
 #define CS4270_CHIPID  0x01    /* Chip ID */
@@ -677,8 +668,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client)
  * This function is called whenever the I2C subsystem finds a device that
  * matches the device ID given via a prior call to i2c_add_driver().
  */
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
-       const struct i2c_device_id *id)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs4270_private *cs4270;
        unsigned int val;
@@ -765,7 +755,7 @@ static struct i2c_driver cs4270_i2c_driver = {
                .of_match_table = cs4270_of_match,
        },
        .id_table = cs4270_id,
-       .probe = cs4270_i2c_probe,
+       .probe_new = cs4270_i2c_probe,
        .remove = cs4270_i2c_remove,
 };
 
index 0a17423..0e8a7cf 100644 (file)
@@ -11,8 +11,7 @@
 #include <sound/soc.h>
 #include "cs4271.h"
 
-static int cs4271_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int cs4271_i2c_probe(struct i2c_client *client)
 {
        struct regmap_config config;
 
@@ -35,7 +34,7 @@ static struct i2c_driver cs4271_i2c_driver = {
                .name = "cs4271",
                .of_match_table = of_match_ptr(cs4271_dt_ids),
        },
-       .probe = cs4271_i2c_probe,
+       .probe_new = cs4271_i2c_probe,
        .id_table = cs4271_i2c_id,
 };
 module_i2c_driver(cs4271_i2c_driver);
index c8409d5..4fade23 100644 (file)
@@ -2194,8 +2194,7 @@ static int __maybe_unused cs42l42_resume(struct device *dev)
        return 0;
 }
 
-static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
-                                      const struct i2c_device_id *id)
+static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs42l42_private *cs42l42;
        int ret, i, devid;
@@ -2399,7 +2398,7 @@ static struct i2c_driver cs42l42_i2c_driver = {
                .acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
                },
        .id_table = cs42l42_id,
-       .probe = cs42l42_i2c_probe,
+       .probe_new = cs42l42_i2c_probe,
        .remove = cs42l42_i2c_remove,
 };
 
index 3cb21a2..3613fb1 100644 (file)
@@ -19,8 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
 
-static int cs42l51_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int cs42l51_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap_config config;
 
@@ -46,7 +45,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
                .of_match_table = cs42l51_of_match,
                .pm = &cs42l51_pm_ops,
        },
-       .probe = cs42l51_i2c_probe,
+       .probe_new = cs42l51_i2c_probe,
        .remove = cs42l51_i2c_remove,
        .id_table = cs42l51_i2c_id,
 };
index e9c3cb4..aff6185 100644 (file)
@@ -51,11 +51,8 @@ struct cs42l51_private {
        struct regmap *regmap;
 };
 
-#define CS42L51_FORMATS ( \
-               SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-               SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-               SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-               SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S18_3LE | \
+                        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
index 8016115..9b182b5 100644 (file)
@@ -1086,8 +1086,7 @@ static const struct regmap_config cs42l52_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
-                            const struct i2c_device_id *id)
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs42l52_private *cs42l52;
        struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1226,7 +1225,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
                .of_match_table = cs42l52_of_match,
        },
        .id_table = cs42l52_id,
-       .probe =    cs42l52_i2c_probe,
+       .probe_new = cs42l52_i2c_probe,
 };
 
 module_i2c_driver(cs42l52_i2c_driver);
index 3cf8a0b..dc23007 100644 (file)
@@ -1167,8 +1167,7 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
        return 0;
 }
 
-static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
-                            const struct i2c_device_id *id)
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs42l56_private *cs42l56;
        struct cs42l56_platform_data *pdata =
@@ -1246,7 +1245,7 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
        ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
        if (ret) {
                dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
-               return ret;
+               goto err_enable;
        }
 
        devid = reg & CS42L56_CHIP_ID_MASK;
@@ -1350,7 +1349,7 @@ static struct i2c_driver cs42l56_i2c_driver = {
                .of_match_table = cs42l56_of_match,
        },
        .id_table = cs42l56_id,
-       .probe =    cs42l56_i2c_probe,
+       .probe_new = cs42l56_i2c_probe,
        .remove =   cs42l56_i2c_remove,
 };
 
index 018463f..5a91662 100644 (file)
@@ -1274,8 +1274,7 @@ static const struct regmap_config cs42l73_regmap = {
        .use_single_write = true,
 };
 
-static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
-                            const struct i2c_device_id *id)
+static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
 {
        struct cs42l73_private *cs42l73;
        struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1386,7 +1385,7 @@ static struct i2c_driver cs42l73_i2c_driver = {
                   .of_match_table = cs42l73_of_match,
                   },
        .id_table = cs42l73_id,
-       .probe = cs42l73_i2c_probe,
+       .probe_new = cs42l73_i2c_probe,
 
 };
 
index 0214e3a..cb06a06 100644 (file)
@@ -17,8 +17,7 @@
 
 #include "cs42xx8.h"
 
-static int cs42xx8_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
 {
        int ret = cs42xx8_probe(&i2c->dev,
                        devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
@@ -51,7 +50,7 @@ static struct i2c_driver cs42xx8_i2c_driver = {
                .pm = &cs42xx8_pm,
                .of_match_table = cs42xx8_of_match,
        },
-       .probe = cs42xx8_i2c_probe,
+       .probe_new = cs42xx8_i2c_probe,
        .remove = cs42xx8_i2c_remove,
        .id_table = cs42xx8_i2c_id,
 };
index 44b20c1..a2bce0f 100644 (file)
@@ -712,30 +712,30 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
        case CS43130_ASP_PCM_DAI:
        case CS43130_ASP_DOP_DAI:
                regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
-                            (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+                            (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
                             CS43130_SP_M_LSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_ASP_DEN_2,
-                            (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+                            (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
                             CS43130_SP_M_MSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_ASP_NUM_1,
-                            (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+                            (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
                             CS43130_SP_N_LSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_ASP_NUM_2,
-                            (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+                            (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
                             CS43130_SP_N_MSB_DATA_SHIFT);
                break;
        case CS43130_XSP_DOP_DAI:
                regmap_write(cs43130->regmap, CS43130_XSP_DEN_1,
-                            (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+                            (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
                             CS43130_SP_M_LSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_XSP_DEN_2,
-                            (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+                            (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
                             CS43130_SP_M_MSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_XSP_NUM_1,
-                            (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+                            (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
                             CS43130_SP_N_LSB_DATA_SHIFT);
                regmap_write(cs43130->regmap, CS43130_XSP_NUM_2,
-                            (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+                            (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
                             CS43130_SP_N_MSB_DATA_SHIFT);
                break;
        default:
@@ -2303,7 +2303,7 @@ static int cs43130_probe(struct snd_soc_component *component)
        }
 
        ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
-                                   &cs43130->jack, NULL, 0);
+                                   &cs43130->jack);
        if (ret < 0) {
                dev_err(component->dev, "Cannot create jack\n");
                return ret;
@@ -2418,8 +2418,7 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client,
        return 0;
 }
 
-static int cs43130_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int cs43130_i2c_probe(struct i2c_client *client)
 {
        struct cs43130_private *cs43130;
        int ret;
@@ -2702,7 +2701,7 @@ static struct i2c_driver cs43130_i2c_driver = {
                .pm             = &cs43130_runtime_pm,
        },
        .id_table       = cs43130_i2c_id,
-       .probe          = cs43130_i2c_probe,
+       .probe_new      = cs43130_i2c_probe,
        .remove         = cs43130_i2c_remove,
 };
 
index e62d671..1dd8936 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef __CS43130_H__
 #define __CS43130_H__
 
+#include <linux/math.h>
+
 /* CS43130 registers addresses */
 /* all reg address is shifted by a byte for control byte to be LSB */
 #define CS43130_FIRSTREG       0x010000
@@ -372,97 +374,96 @@ enum cs43130_dai_id {
 };
 
 struct cs43130_clk_gen {
-       unsigned int    mclk_int;
-       int             fs;
-       u16             den;
-       u16             num;
+       unsigned int            mclk_int;
+       int                     fs;
+       struct u16_fract        v;
 };
 
 /* frm_size = 16 */
 static const struct cs43130_clk_gen cs43130_16_clk_gen[] = {
-       {22579200,      32000,          441,            10,},
-       {22579200,      44100,          32,             1,},
-       {22579200,      48000,          147,            5,},
-       {22579200,      88200,          16,             1,},
-       {22579200,      96000,          147,            10,},
-       {22579200,      176400,         8,              1,},
-       {22579200,      192000,         147,            20,},
-       {22579200,      352800,         4,              1,},
-       {22579200,      384000,         147,            40,},
-       {24576000,      32000,          48,             1,},
-       {24576000,      44100,          5120,           147,},
-       {24576000,      48000,          32,             1,},
-       {24576000,      88200,          2560,           147,},
-       {24576000,      96000,          16,             1,},
-       {24576000,      176400,         1280,           147,},
-       {24576000,      192000,         8,              1,},
-       {24576000,      352800,         640,            147,},
-       {24576000,      384000,         4,              1,},
+       { 22579200,     32000,          .v = { 441,     10, }, },
+       { 22579200,     44100,          .v = { 32,      1, }, },
+       { 22579200,     48000,          .v = { 147,     5, }, },
+       { 22579200,     88200,          .v = { 16,      1, }, },
+       { 22579200,     96000,          .v = { 147,     10, }, },
+       { 22579200,     176400,         .v = { 8,       1, }, },
+       { 22579200,     192000,         .v = { 147,     20, }, },
+       { 22579200,     352800,         .v = { 4,       1, }, },
+       { 22579200,     384000,         .v = { 147,     40, }, },
+       { 24576000,     32000,          .v = { 48,      1, }, },
+       { 24576000,     44100,          .v = { 5120,    147, }, },
+       { 24576000,     48000,          .v = { 32,      1, }, },
+       { 24576000,     88200,          .v = { 2560,    147, }, },
+       { 24576000,     96000,          .v = { 16,      1, }, },
+       { 24576000,     176400,         .v = { 1280,    147, }, },
+       { 24576000,     192000,         .v = { 8,       1, }, },
+       { 24576000,     352800,         .v = { 640,     147, }, },
+       { 24576000,     384000,         .v = { 4,       1, }, },
 };
 
 /* frm_size = 32 */
 static const struct cs43130_clk_gen cs43130_32_clk_gen[] = {
-       {22579200,      32000,          441,            20,},
-       {22579200,      44100,          16,             1,},
-       {22579200,      48000,          147,            10,},
-       {22579200,      88200,          8,              1,},
-       {22579200,      96000,          147,            20,},
-       {22579200,      176400,         4,              1,},
-       {22579200,      192000,         147,            40,},
-       {22579200,      352800,         2,              1,},
-       {22579200,      384000,         147,            80,},
-       {24576000,      32000,          24,             1,},
-       {24576000,      44100,          2560,           147,},
-       {24576000,      48000,          16,             1,},
-       {24576000,      88200,          1280,           147,},
-       {24576000,      96000,          8,              1,},
-       {24576000,      176400,         640,            147,},
-       {24576000,      192000,         4,              1,},
-       {24576000,      352800,         320,            147,},
-       {24576000,      384000,         2,              1,},
+       { 22579200,     32000,          .v = { 441,     20, }, },
+       { 22579200,     44100,          .v = { 16,      1, }, },
+       { 22579200,     48000,          .v = { 147,     10, }, },
+       { 22579200,     88200,          .v = { 8,       1, }, },
+       { 22579200,     96000,          .v = { 147,     20, }, },
+       { 22579200,     176400,         .v = { 4,       1, }, },
+       { 22579200,     192000,         .v = { 147,     40, }, },
+       { 22579200,     352800,         .v = { 2,       1, }, },
+       { 22579200,     384000,         .v = { 147,     80, }, },
+       { 24576000,     32000,          .v = { 24,      1, }, },
+       { 24576000,     44100,          .v = { 2560,    147, }, },
+       { 24576000,     48000,          .v = { 16,      1, }, },
+       { 24576000,     88200,          .v = { 1280,    147, }, },
+       { 24576000,     96000,          .v = { 8,       1, }, },
+       { 24576000,     176400,         .v = { 640,     147, }, },
+       { 24576000,     192000,         .v = { 4,       1, }, },
+       { 24576000,     352800,         .v = { 320,     147, }, },
+       { 24576000,     384000,         .v = { 2,       1, }, },
 };
 
 /* frm_size = 48 */
 static const struct cs43130_clk_gen cs43130_48_clk_gen[] = {
-       {22579200,      32000,          147,            100,},
-       {22579200,      44100,          32,             3,},
-       {22579200,      48000,          49,             5,},
-       {22579200,      88200,          16,             3,},
-       {22579200,      96000,          49,             10,},
-       {22579200,      176400,         8,              3,},
-       {22579200,      192000,         49,             20,},
-       {22579200,      352800,         4,              3,},
-       {22579200,      384000,         49,             40,},
-       {24576000,      32000,          16,             1,},
-       {24576000,      44100,          5120,           441,},
-       {24576000,      48000,          32,             3,},
-       {24576000,      88200,          2560,           441,},
-       {24576000,      96000,          16,             3,},
-       {24576000,      176400,         1280,           441,},
-       {24576000,      192000,         8,              3,},
-       {24576000,      352800,         640,            441,},
-       {24576000,      384000,         4,              3,},
+       { 22579200,     32000,          .v = { 147,     100, }, },
+       { 22579200,     44100,          .v = { 32,      3, }, },
+       { 22579200,     48000,          .v = { 49,      5, }, },
+       { 22579200,     88200,          .v = { 16,      3, }, },
+       { 22579200,     96000,          .v = { 49,      10, }, },
+       { 22579200,     176400,         .v = { 8,       3, }, },
+       { 22579200,     192000,         .v = { 49,      20, }, },
+       { 22579200,     352800,         .v = { 4,       3, }, },
+       { 22579200,     384000,         .v = { 49,      40, }, },
+       { 24576000,     32000,          .v = { 16,      1, }, },
+       { 24576000,     44100,          .v = { 5120,    441, }, },
+       { 24576000,     48000,          .v = { 32,      3, }, },
+       { 24576000,     88200,          .v = { 2560,    441, }, },
+       { 24576000,     96000,          .v = { 16,      3, }, },
+       { 24576000,     176400,         .v = { 1280,    441, }, },
+       { 24576000,     192000,         .v = { 8,       3, }, },
+       { 24576000,     352800,         .v = { 640,     441, }, },
+       { 24576000,     384000,         .v = { 4,       3, }, },
 };
 
 /* frm_size = 64 */
 static const struct cs43130_clk_gen cs43130_64_clk_gen[] = {
-       {22579200,      32000,          441,            40,},
-       {22579200,      44100,          8,              1,},
-       {22579200,      48000,          147,            20,},
-       {22579200,      88200,          4,              1,},
-       {22579200,      96000,          147,            40,},
-       {22579200,      176400,         2,              1,},
-       {22579200,      192000,         147,            80,},
-       {22579200,      352800,         1,              1,},
-       {24576000,      32000,          12,             1,},
-       {24576000,      44100,          1280,           147,},
-       {24576000,      48000,          8,              1,},
-       {24576000,      88200,          640,            147,},
-       {24576000,      96000,          4,              1,},
-       {24576000,      176400,         320,            147,},
-       {24576000,      192000,         2,              1,},
-       {24576000,      352800,         160,            147,},
-       {24576000,      384000,         1,              1,},
+       { 22579200,     32000,          .v = { 441,     40, }, },
+       { 22579200,     44100,          .v = { 8,       1, }, },
+       { 22579200,     48000,          .v = { 147,     20, }, },
+       { 22579200,     88200,          .v = { 4,       1, }, },
+       { 22579200,     96000,          .v = { 147,     40, }, },
+       { 22579200,     176400,         .v = { 2,       1, }, },
+       { 22579200,     192000,         .v = { 147,     80, }, },
+       { 22579200,     352800,         .v = { 1,       1, }, },
+       { 24576000,     32000,          .v = { 12,      1, }, },
+       { 24576000,     44100,          .v = { 1280,    147, }, },
+       { 24576000,     48000,          .v = { 8,       1, }, },
+       { 24576000,     88200,          .v = { 640,     147, }, },
+       { 24576000,     96000,          .v = { 4,       1, }, },
+       { 24576000,     176400,         .v = { 320,     147, }, },
+       { 24576000,     192000,         .v = { 2,       1, }, },
+       { 24576000,     352800,         .v = { 160,     147, }, },
+       { 24576000,     384000,         .v = { 1,       1, }, },
 };
 
 struct cs43130_bitwidth_map {
index 29d05e3..8ac043f 100644 (file)
@@ -225,8 +225,7 @@ static int cs4341_probe(struct device *dev)
 }
 
 #if IS_ENABLED(CONFIG_I2C)
-static int cs4341_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int cs4341_i2c_probe(struct i2c_client *i2c)
 {
        struct cs4341_priv *cs4341;
 
@@ -260,7 +259,7 @@ static struct i2c_driver cs4341_i2c_driver = {
                .name = "cs4341-i2c",
                .of_match_table = of_match_ptr(cs4341_dt_ids),
        },
-       .probe = cs4341_i2c_probe,
+       .probe_new = cs4341_i2c_probe,
        .id_table = cs4341_i2c_id,
 };
 #endif
index 786c69a..7069e9b 100644 (file)
@@ -223,12 +223,9 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
        {"OutputB", NULL, "HiFi DAC"},
 };
 
-#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8  | \
-                       SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE  | \
+#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8  | SNDRV_PCM_FMTBIT_S16_LE  | \
+                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE  | \
                        SNDRV_PCM_FMTBIT_S32_LE)
 
 #define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
@@ -278,8 +275,7 @@ static const struct regmap_config cs4349_regmap = {
        .cache_type             = REGCACHE_RBTREE,
 };
 
-static int cs4349_i2c_probe(struct i2c_client *client,
-                                     const struct i2c_device_id *id)
+static int cs4349_i2c_probe(struct i2c_client *client)
 {
        struct cs4349_private *cs4349;
        int ret;
@@ -382,7 +378,7 @@ static struct i2c_driver cs4349_i2c_driver = {
                .pm = &cs4349_runtime_pm,
        },
        .id_table       = cs4349_i2c_id,
-       .probe          = cs4349_i2c_probe,
+       .probe_new      = cs4349_i2c_probe,
        .remove         = cs4349_i2c_remove,
 };
 
index f2087bd..7035452 100644 (file)
@@ -918,8 +918,7 @@ static struct regmap_config cs53l30_regmap = {
        .use_single_write = true,
 };
 
-static int cs53l30_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int cs53l30_i2c_probe(struct i2c_client *client)
 {
        const struct device_node *np = client->dev.of_node;
        struct device *dev = &client->dev;
@@ -1125,7 +1124,7 @@ static struct i2c_driver cs53l30_i2c_driver = {
                .pm = &cs53l30_runtime_pm,
        },
        .id_table = cs53l30_id,
-       .probe = cs53l30_i2c_probe,
+       .probe_new = cs53l30_i2c_probe,
        .remove = cs53l30_i2c_remove,
 };
 
index 1f5c57f..b35debb 100644 (file)
@@ -1527,6 +1527,7 @@ static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
        .num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
        .dapm_routes = cx2072x_intercon,
        .num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
+       .endianness = 1,
 };
 
 /*
@@ -1626,8 +1627,7 @@ static int __maybe_unused cx2072x_runtime_resume(struct device *dev)
        return clk_prepare_enable(cx2072x->mclk);
 }
 
-static int cx2072x_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int cx2072x_i2c_probe(struct i2c_client *i2c)
 {
        struct cx2072x_priv *cx2072x;
        unsigned int ven_id, rev_id;
@@ -1710,7 +1710,7 @@ static struct i2c_driver cx2072x_i2c_driver = {
                .acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
                .pm = &cx2072x_runtime_pm,
        },
-       .probe = cx2072x_i2c_probe,
+       .probe_new = cx2072x_i2c_probe,
        .remove = cx2072x_i2c_remove,
        .id_table = cx2072x_i2c_id,
 };
index 8af344b..3fa3042 100644 (file)
@@ -1206,8 +1206,7 @@ static const struct regmap_config da7210_regmap_config_i2c = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int da7210_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int da7210_i2c_probe(struct i2c_client *i2c)
 {
        struct da7210_priv *da7210;
        int ret;
@@ -1250,7 +1249,7 @@ static struct i2c_driver da7210_i2c_driver = {
        .driver = {
                .name = "da7210",
        },
-       .probe          = da7210_i2c_probe,
+       .probe_new      = da7210_i2c_probe,
        .id_table       = da7210_i2c_id,
 };
 #endif
index 3ab8938..2e645dc 100644 (file)
@@ -1946,8 +1946,7 @@ static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = {
        [DA7213_SUPPLY_VDDIO] = "VDDIO",
 };
 
-static int da7213_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int da7213_i2c_probe(struct i2c_client *i2c)
 {
        struct da7213_priv *da7213;
        int i, ret;
@@ -2040,7 +2039,7 @@ static struct i2c_driver da7213_i2c_driver = {
                .acpi_match_table = ACPI_PTR(da7213_acpi_match),
                .pm = &da7213_pm,
        },
-       .probe          = da7213_i2c_probe,
+       .probe_new      = da7213_i2c_probe,
        .id_table       = da7213_i2c_id,
 };
 
index ea426d9..a5d7c35 100644 (file)
@@ -3258,8 +3258,19 @@ static const struct regmap_config da7218_regmap_config = {
  * I2C layer
  */
 
-static int da7218_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static const struct i2c_device_id da7218_i2c_id[];
+
+static inline int da7218_i2c_get_id(struct i2c_client *i2c)
+{
+       const struct i2c_device_id *id = i2c_match_id(da7218_i2c_id, i2c);
+
+       if (id)
+               return (uintptr_t)id->driver_data;
+       else
+               return -EINVAL;
+}
+
+static int da7218_i2c_probe(struct i2c_client *i2c)
 {
        struct da7218_priv *da7218;
        int ret;
@@ -3273,7 +3284,7 @@ static int da7218_i2c_probe(struct i2c_client *i2c,
        if (i2c->dev.of_node)
                da7218->dev_id = da7218_of_get_id(&i2c->dev);
        else
-               da7218->dev_id = id->driver_data;
+               da7218->dev_id = da7218_i2c_get_id(i2c);
 
        if ((da7218->dev_id != DA7217_DEV_ID) &&
            (da7218->dev_id != DA7218_DEV_ID)) {
@@ -3311,7 +3322,7 @@ static struct i2c_driver da7218_i2c_driver = {
                .name = "da7218",
                .of_match_table = da7218_of_match,
        },
-       .probe          = da7218_i2c_probe,
+       .probe_new      = da7218_i2c_probe,
        .id_table       = da7218_i2c_id,
 };
 
index c749354..7fdef38 100644 (file)
@@ -2655,8 +2655,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7219 = {
  * I2C layer
  */
 
-static int da7219_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int da7219_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct da7219_priv *da7219;
@@ -2711,7 +2710,7 @@ static struct i2c_driver da7219_i2c_driver = {
                .of_match_table = of_match_ptr(da7219_of_match),
                .acpi_match_table = ACPI_PTR(da7219_acpi_match),
        },
-       .probe          = da7219_i2c_probe,
+       .probe_new      = da7219_i2c_probe,
        .remove         = da7219_i2c_remove,
        .id_table       = da7219_i2c_id,
 };
index 42d6a3f..f14cddf 100644 (file)
@@ -1506,8 +1506,7 @@ static const struct snd_soc_component_driver soc_component_dev_da732x = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int da732x_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int da732x_i2c_probe(struct i2c_client *i2c)
 {
        struct da732x_priv *da732x;
        unsigned int reg;
@@ -1562,7 +1561,7 @@ static struct i2c_driver da732x_i2c_driver = {
        .driver         = {
                .name   = "da7320",
        },
-       .probe          = da732x_i2c_probe,
+       .probe_new      = da732x_i2c_probe,
        .remove         = da732x_i2c_remove,
        .id_table       = da732x_i2c_id,
 };
index a9676b2..9d8c8ad 100644 (file)
@@ -1473,8 +1473,7 @@ static const struct regmap_config da9055_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int da9055_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int da9055_i2c_probe(struct i2c_client *i2c)
 {
        struct da9055_priv *da9055;
        struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
@@ -1533,7 +1532,7 @@ static struct i2c_driver da9055_i2c_driver = {
                .name = "da9055-codec",
                .of_match_table = of_match_ptr(da9055_of_match),
        },
-       .probe          = da9055_i2c_probe,
+       .probe_new      = da9055_i2c_probe,
        .id_table       = da9055_i2c_id,
 };
 
index 5d079d9..d1a30ca 100644 (file)
@@ -82,7 +82,10 @@ static struct snd_soc_dai_driver dmic_dai = {
                .rates = SNDRV_PCM_RATE_CONTINUOUS,
                .formats = SNDRV_PCM_FMTBIT_S32_LE
                        | SNDRV_PCM_FMTBIT_S24_LE
-                       | SNDRV_PCM_FMTBIT_S16_LE,
+                       | SNDRV_PCM_FMTBIT_S16_LE
+                       | SNDRV_PCM_FMTBIT_DSD_U8
+                       | SNDRV_PCM_FMTBIT_DSD_U16_LE
+                       | SNDRV_PCM_FMTBIT_DSD_U32_LE,
        },
        .ops    = &dmic_dai_ops,
 };
index ff33eab..4407166 100644 (file)
@@ -789,8 +789,7 @@ static const struct regmap_config es8316_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int es8316_i2c_probe(struct i2c_client *i2c_client,
-                           const struct i2c_device_id *id)
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
 {
        struct device *dev = &i2c_client->dev;
        struct es8316_priv *es8316;
@@ -852,7 +851,7 @@ static struct i2c_driver es8316_i2c_driver = {
                .acpi_match_table       = ACPI_PTR(es8316_acpi_match),
                .of_match_table         = of_match_ptr(es8316_of_match),
        },
-       .probe          = es8316_i2c_probe,
+       .probe_new      = es8316_i2c_probe,
        .id_table       = es8316_i2c_id,
 };
 module_i2c_driver(es8316_i2c_driver);
index 6b0df0d..68072e9 100644 (file)
@@ -29,8 +29,7 @@ static const struct of_device_id es8328_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, es8328_of_match);
 
-static int es8328_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int es8328_i2c_probe(struct i2c_client *i2c)
 {
        return es8328_probe(&i2c->dev,
                        devm_regmap_init_i2c(i2c, &es8328_regmap_config));
@@ -41,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = {
                .name           = "es8328",
                .of_match_table = es8328_of_match,
        },
-       .probe    = es8328_i2c_probe,
+       .probe_new = es8328_i2c_probe,
        .id_table = es8328_id,
 };
 
index a9f61c7..8debcee 100644 (file)
@@ -571,13 +571,14 @@ static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = {
 };
 
 static const struct snd_soc_component_driver hdac_hda_codec = {
-       .probe          = hdac_hda_codec_probe,
-       .remove         = hdac_hda_codec_remove,
-       .idle_bias_on   = false,
-       .dapm_widgets           = hdac_hda_dapm_widgets,
-       .num_dapm_widgets       = ARRAY_SIZE(hdac_hda_dapm_widgets),
-       .dapm_routes            = hdac_hda_dapm_routes,
-       .num_dapm_routes        = ARRAY_SIZE(hdac_hda_dapm_routes),
+       .probe                  = hdac_hda_codec_probe,
+       .remove                 = hdac_hda_codec_remove,
+       .dapm_widgets           = hdac_hda_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(hdac_hda_dapm_widgets),
+       .dapm_routes            = hdac_hda_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(hdac_hda_dapm_routes),
+       .idle_bias_on           = false,
+       .endianness             = 1,
 };
 
 static int hdac_hda_dev_probe(struct hdac_device *hdev)
index b07607a..b773466 100644 (file)
@@ -727,10 +727,8 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
                         SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
                         SNDRV_PCM_RATE_192000)
 
-#define SPDIF_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
-                        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
-                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
-                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define SPDIF_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 /*
  * This list is only for formats allowed on the I2S bus. So there is
@@ -740,12 +738,9 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
  * problems, we should add the video side driver an option to disable
  * them.
  */
-#define I2S_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
-                        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
-                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
-                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
-                        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
-                        SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+#define I2S_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+                        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
 
 static struct snd_kcontrol_new hdmi_codec_controls[] = {
        {
index 1d86b6a..39be31e 100644 (file)
@@ -1108,8 +1108,7 @@ static const struct regmap_config isabelle_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int isabelle_i2c_probe(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+static int isabelle_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *isabelle_regmap;
        int ret = 0;
@@ -1144,7 +1143,7 @@ static struct i2c_driver isabelle_i2c_driver = {
        .driver = {
                .name = "isabelle",
        },
-       .probe = isabelle_i2c_probe,
+       .probe_new = isabelle_i2c_probe,
        .id_table = isabelle_i2c_id,
 };
 
index 300b325..dba1613 100644 (file)
@@ -115,8 +115,7 @@ static const struct regmap_config lm4857_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
 };
 
-static int lm4857_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int lm4857_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
 
@@ -138,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = {
        .driver = {
                .name = "lm4857",
        },
-       .probe = lm4857_i2c_probe,
+       .probe_new = lm4857_i2c_probe,
        .id_table = lm4857_i2c_id,
 };
 
index 973d781..bd0078e 100644 (file)
@@ -1412,8 +1412,7 @@ static const struct regmap_config lm49453_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int lm49453_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int lm49453_i2c_probe(struct i2c_client *i2c)
 {
        struct lm49453_priv *lm49453;
        int ret = 0;
@@ -1458,7 +1457,7 @@ static struct i2c_driver lm49453_i2c_driver = {
        .driver = {
                .name = "lm49453",
        },
-       .probe = lm49453_i2c_probe,
+       .probe_new = lm49453_i2c_probe,
        .remove = lm49453_i2c_remove,
        .id_table = lm49453_i2c_id,
 };
index 54426a9..54a8ba7 100644 (file)
@@ -212,12 +212,13 @@ static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
 };
 
 static const struct snd_soc_component_driver lochnagar_sc_driver = {
-       .non_legacy_dai_naming = 1,
-
        .dapm_widgets = lochnagar_sc_widgets,
        .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
        .dapm_routes = lochnagar_sc_routes,
        .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
+
+       .non_legacy_dai_naming = 1,
+       .endianness = 1,
 };
 
 static int lochnagar_sc_probe(struct platform_device *pdev)
index 6cede75..1b9082d 100644 (file)
@@ -24,42 +24,45 @@ struct lpass_macro *lpass_macro_pds_init(struct device *dev)
                return ERR_PTR(-ENOMEM);
 
        l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro");
-       if (IS_ERR_OR_NULL(l_pds->macro_pd))
-               return NULL;
-
-       ret = pm_runtime_get_sync(l_pds->macro_pd);
-       if (ret < 0) {
-               pm_runtime_put_noidle(l_pds->macro_pd);
+       if (IS_ERR_OR_NULL(l_pds->macro_pd)) {
+               ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA;
                goto macro_err;
        }
 
+       ret = pm_runtime_resume_and_get(l_pds->macro_pd);
+       if (ret < 0)
+               goto macro_sync_err;
+
        l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec");
-       if (IS_ERR_OR_NULL(l_pds->dcodec_pd))
+       if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) {
+               ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA;
                goto dcodec_err;
+       }
 
-       ret = pm_runtime_get_sync(l_pds->dcodec_pd);
-       if (ret < 0) {
-               pm_runtime_put_noidle(l_pds->dcodec_pd);
+       ret = pm_runtime_resume_and_get(l_pds->dcodec_pd);
+       if (ret < 0)
                goto dcodec_sync_err;
-       }
        return l_pds;
 
 dcodec_sync_err:
        dev_pm_domain_detach(l_pds->dcodec_pd, false);
 dcodec_err:
        pm_runtime_put(l_pds->macro_pd);
-macro_err:
+macro_sync_err:
        dev_pm_domain_detach(l_pds->macro_pd, false);
+macro_err:
        return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(lpass_macro_pds_init);
 
 void lpass_macro_pds_exit(struct lpass_macro *pds)
 {
-       pm_runtime_put(pds->macro_pd);
-       dev_pm_domain_detach(pds->macro_pd, false);
-       pm_runtime_put(pds->dcodec_pd);
-       dev_pm_domain_detach(pds->dcodec_pd, false);
+       if (pds) {
+               pm_runtime_put(pds->macro_pd);
+               dev_pm_domain_detach(pds->macro_pd, false);
+               pm_runtime_put(pds->dcodec_pd);
+               dev_pm_domain_detach(pds->dcodec_pd, false);
+       }
 }
 EXPORT_SYMBOL_GPL(lpass_macro_pds_exit);
 
index 39dda1b..d711eb1 100644 (file)
@@ -167,8 +167,7 @@ static const struct regmap_config max9768_i2c_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int max9768_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int max9768_i2c_probe(struct i2c_client *client)
 {
        struct max9768 *max9768;
        struct max9768_pdata *pdata = client->dev.platform_data;
@@ -215,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = {
        .driver = {
                .name = "max9768",
        },
-       .probe = max9768_i2c_probe,
+       .probe_new = max9768_i2c_probe,
        .id_table = max9768_i2c_id,
 };
 module_i2c_driver(max9768_i2c_driver);
index 429717d..5ef2e12 100644 (file)
@@ -1737,11 +1737,18 @@ static const struct snd_soc_component_driver soc_component_dev_max98088 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int max98088_i2c_probe(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", MAX98088 },
+       { "max98089", MAX98089 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static int max98088_i2c_probe(struct i2c_client *i2c)
 {
        struct max98088_priv *max98088;
        int ret;
+       const struct i2c_device_id *id;
 
        max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
                               GFP_KERNEL);
@@ -1757,6 +1764,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
                if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
                        return PTR_ERR(max98088->mclk);
 
+       id = i2c_match_id(max98088_i2c_id, i2c);
        max98088->devtype = id->driver_data;
 
        i2c_set_clientdata(i2c, max98088);
@@ -1767,13 +1775,6 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
        return ret;
 }
 
-static const struct i2c_device_id max98088_i2c_id[] = {
-       { "max98088", MAX98088 },
-       { "max98089", MAX98089 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
-
 #if defined(CONFIG_OF)
 static const struct of_device_id max98088_of_match[] = {
        { .compatible = "maxim,max98088" },
@@ -1788,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = {
                .name = "max98088",
                .of_match_table = of_match_ptr(max98088_of_match),
        },
-       .probe  = max98088_i2c_probe,
+       .probe_new = max98088_i2c_probe,
        .id_table = max98088_i2c_id,
 };
 
index 62b41ca..576277a 100644 (file)
@@ -393,9 +393,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int mask = (1 << fls(mc->max)) - 1;
-       unsigned int sel = ucontrol->value.integer.value[0];
+       int sel_unchecked = ucontrol->value.integer.value[0];
+       unsigned int sel;
        unsigned int val = snd_soc_component_read(component, mc->reg);
        unsigned int *select;
+       int change;
 
        switch (mc->reg) {
        case M98090_REG_MIC1_INPUT_LEVEL:
@@ -413,9 +415,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
 
        val = (val >> mc->shift) & mask;
 
-       if (sel < 0 || sel > mc->max)
+       if (sel_unchecked < 0 || sel_unchecked > mc->max)
                return -EINVAL;
+       sel = sel_unchecked;
 
+       change = *select != sel;
        *select = sel;
 
        /* Setting a volume is only valid if it is already On */
@@ -430,7 +434,7 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
                mask << mc->shift,
                sel << mc->shift);
 
-       return *select != val;
+       return change;
 }
 
 static const char *max98090_perf_pwr_text[] =
@@ -2532,8 +2536,14 @@ static const struct regmap_config max98090_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int max98090_i2c_probe(struct i2c_client *i2c,
-                                const struct i2c_device_id *i2c_id)
+static const struct i2c_device_id max98090_i2c_id[] = {
+       { "max98090", MAX98090 },
+       { "max98091", MAX98091 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
+
+static int max98090_i2c_probe(struct i2c_client *i2c)
 {
        struct max98090_priv *max98090;
        const struct acpi_device_id *acpi_id;
@@ -2555,7 +2565,9 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
                        return -EINVAL;
                }
                driver_data = acpi_id->driver_data;
-       } else if (i2c_id) {
+       } else {
+               const struct i2c_device_id *i2c_id =
+                       i2c_match_id(max98090_i2c_id, i2c);
                driver_data = i2c_id->driver_data;
        }
 
@@ -2662,13 +2674,6 @@ static const struct dev_pm_ops max98090_pm = {
        SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
 };
 
-static const struct i2c_device_id max98090_i2c_id[] = {
-       { "max98090", MAX98090 },
-       { "max98091", MAX98091 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
-
 #ifdef CONFIG_OF
 static const struct of_device_id max98090_of_match[] = {
        { .compatible = "maxim,max98090", },
@@ -2693,7 +2698,7 @@ static struct i2c_driver max98090_i2c_driver = {
                .of_match_table = of_match_ptr(max98090_of_match),
                .acpi_match_table = ACPI_PTR(max98090_acpi_match),
        },
-       .probe  = max98090_i2c_probe,
+       .probe_new = max98090_i2c_probe,
        .shutdown = max98090_i2c_shutdown,
        .remove = max98090_i2c_remove,
        .id_table = max98090_i2c_id,
index 4977b00..7bca99f 100644 (file)
@@ -2106,11 +2106,17 @@ static const struct snd_soc_component_driver soc_component_dev_max98095 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int max98095_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static const struct i2c_device_id max98095_i2c_id[] = {
+       { "max98095", MAX98095 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static int max98095_i2c_probe(struct i2c_client *i2c)
 {
        struct max98095_priv *max98095;
        int ret;
+       const struct i2c_device_id *id;
 
        max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
                                GFP_KERNEL);
@@ -2126,6 +2132,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
+       id = i2c_match_id(max98095_i2c_id, i2c);
        max98095->devtype = id->driver_data;
        i2c_set_clientdata(i2c, max98095);
        max98095->pdata = i2c->dev.platform_data;
@@ -2136,12 +2143,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
        return ret;
 }
 
-static const struct i2c_device_id max98095_i2c_id[] = {
-       { "max98095", MAX98095 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
-
 #ifdef CONFIG_OF
 static const struct of_device_id max98095_of_match[] = {
        { .compatible = "maxim,max98095", },
@@ -2155,7 +2156,7 @@ static struct i2c_driver max98095_i2c_driver = {
                .name = "max98095",
                .of_match_table = of_match_ptr(max98095_of_match),
        },
-       .probe  = max98095_i2c_probe,
+       .probe_new = max98095_i2c_probe,
        .id_table = max98095_i2c_id,
 };
 
index 8d42f52..800f2bc 100644 (file)
@@ -365,8 +365,7 @@ static const struct regmap_config max98371_regmap = {
        .cache_type       = REGCACHE_RBTREE,
 };
 
-static int max98371_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int max98371_i2c_probe(struct i2c_client *i2c)
 {
        struct max98371_priv *max98371;
        int ret, reg;
@@ -421,7 +420,7 @@ static struct i2c_driver max98371_i2c_driver = {
                .name = "max98371",
                .of_match_table = of_match_ptr(max98371_of_match),
        },
-       .probe  = max98371_i2c_probe,
+       .probe_new  = max98371_i2c_probe,
        .id_table = max98371_i2c_id,
 };
 
index ddb6436..4fe065e 100644 (file)
@@ -516,8 +516,7 @@ static const struct regmap_config max98373_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int max98373_i2c_probe(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+static int max98373_i2c_probe(struct i2c_client *i2c)
 {
        int ret = 0;
        int reg = 0;
@@ -622,7 +621,7 @@ static struct i2c_driver max98373_i2c_driver = {
                .acpi_match_table = ACPI_PTR(max98373_acpi_match),
                .pm = &max98373_pm,
        },
-       .probe = max98373_i2c_probe,
+       .probe_new = max98373_i2c_probe,
        .id_table = max98373_i2c_id,
 };
 
index 40fd6f3..2a6b164 100644 (file)
@@ -1014,14 +1014,14 @@ static void max98390_slot_config(struct i2c_client *i2c,
                max98390->i_l_slot = 1;
 }
 
-static int max98390_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int max98390_i2c_probe(struct i2c_client *i2c)
 {
        int ret = 0;
        int reg = 0;
 
        struct max98390_priv *max98390 = NULL;
        struct i2c_adapter *adapter = i2c->adapter;
+       struct gpio_desc *reset_gpio;
 
        ret = i2c_check_functionality(adapter,
                I2C_FUNC_SMBUS_BYTE
@@ -1073,6 +1073,17 @@ static int max98390_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
+       reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+                                            "reset", GPIOD_OUT_HIGH);
+
+       /* Power on device */
+       if (reset_gpio) {
+               usleep_range(1000, 2000);
+               /* bring out of reset */
+               gpiod_set_value_cansleep(reset_gpio, 0);
+               usleep_range(1000, 2000);
+       }
+
        /* Check Revision ID */
        ret = regmap_read(max98390->regmap,
                MAX98390_R24FF_REV_ID, &reg);
@@ -1121,7 +1132,7 @@ static struct i2c_driver max98390_i2c_driver = {
                .acpi_match_table = ACPI_PTR(max98390_acpi_match),
                .pm = &max98390_pm,
        },
-       .probe = max98390_i2c_probe,
+       .probe_new = max98390_i2c_probe,
        .id_table = max98390_i2c_id,
 };
 
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
new file mode 100644 (file)
index 0000000..56eb62b
--- /dev/null
@@ -0,0 +1,1637 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <sound/tlv.h>
+#include "max98396.h"
+
+static struct reg_default max98396_reg[] = {
+       {MAX98396_R2000_SW_RESET, 0x00},
+       {MAX98396_R2001_INT_RAW1, 0x00},
+       {MAX98396_R2002_INT_RAW2, 0x00},
+       {MAX98396_R2003_INT_RAW3, 0x00},
+       {MAX98396_R2004_INT_RAW4, 0x00},
+       {MAX98396_R2006_INT_STATE1, 0x00},
+       {MAX98396_R2007_INT_STATE2, 0x00},
+       {MAX98396_R2008_INT_STATE3, 0x00},
+       {MAX98396_R2009_INT_STATE4, 0x00},
+       {MAX98396_R200B_INT_FLAG1, 0x00},
+       {MAX98396_R200C_INT_FLAG2, 0x00},
+       {MAX98396_R200D_INT_FLAG3, 0x00},
+       {MAX98396_R200E_INT_FLAG4, 0x00},
+       {MAX98396_R2010_INT_EN1, 0x02},
+       {MAX98396_R2011_INT_EN2, 0x00},
+       {MAX98396_R2012_INT_EN3, 0x00},
+       {MAX98396_R2013_INT_EN4, 0x00},
+       {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+       {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+       {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+       {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+       {MAX98396_R201F_IRQ_CTRL, 0x00},
+       {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+       {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+       {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+       {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+       {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+       {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+       {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+       {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+       {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+       {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+       {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+       {MAX98396_R2040_PIN_CFG, 0x55},
+       {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+       {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+       {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+       {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+       {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+       {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+       {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+       {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+       {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+       {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+       {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+       {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF},
+       {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF},
+       {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF},
+       {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF},
+       {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF},
+       {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF},
+       {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF},
+       {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF},
+       {MAX98396_R2055_PCM_RX_SRC1, 0x00},
+       {MAX98396_R2056_PCM_RX_SRC2, 0x00},
+       {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+       {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+       {MAX98396_R205E_PCM_RX_EN, 0x00},
+       {MAX98396_R205F_PCM_TX_EN, 0x00},
+       {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+       {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+       {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+       {MAX98396_R207F_ICC_EN, 0x00},
+       {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+       {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+       {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+       {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+       {MAX98396_R208F_TONE_GEN_EN, 0x00},
+       {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+       {MAX98396_R2091_AMP_PATH_GAIN, 0x0B},
+       {MAX98396_R2092_AMP_DSP_CFG, 0x23},
+       {MAX98396_R2093_SSM_CFG, 0x0D},
+       {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+       {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+       {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+       {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+       {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+       {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+       {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+       {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A},
+       {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA},
+       {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+       {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+       {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00},
+       {MAX98396_R20AF_AMP_EN, 0x00},
+       {MAX98396_R20B0_ADC_SR, 0x30},
+       {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+       {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+       {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+       {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00},
+       {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00},
+       {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+       {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+       {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00},
+       {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00},
+       {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00},
+       {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00},
+       {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00},
+       {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00},
+       {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00},
+       {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+       {MAX98396_R20C7_ADC_CFG, 0x00},
+       {MAX98396_R20D0_DHT_CFG1, 0x00},
+       {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+       {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+       {MAX98396_R20D3_DHT_CFG2, 0x14},
+       {MAX98396_R20D4_DHT_CFG3, 0x02},
+       {MAX98396_R20D5_DHT_CFG4, 0x04},
+       {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+       {MAX98396_R20DF_DHT_EN, 0x00},
+       {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+       {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+       {MAX98396_R20E5_BPE_STATE, 0x00},
+       {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+       {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+       {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+       {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+       {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+       {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+       {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+       {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+       {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+       {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+       {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+       {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+       {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+       {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+       {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+       {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+       {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+       {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+       {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+       {MAX98396_R210D_BPE_EN, 0x00},
+       {MAX98396_R210E_AUTO_RESTART, 0x00},
+       {MAX98396_R210F_GLOBAL_EN, 0x00},
+       {MAX98396_R21FF_REVISION_ID, 0x00},
+};
+
+static struct reg_default max98397_reg[] = {
+       {MAX98396_R2000_SW_RESET, 0x00},
+       {MAX98396_R2001_INT_RAW1, 0x00},
+       {MAX98396_R2002_INT_RAW2, 0x00},
+       {MAX98396_R2003_INT_RAW3, 0x00},
+       {MAX98396_R2004_INT_RAW4, 0x00},
+       {MAX98396_R2006_INT_STATE1, 0x00},
+       {MAX98396_R2007_INT_STATE2, 0x00},
+       {MAX98396_R2008_INT_STATE3, 0x00},
+       {MAX98396_R2009_INT_STATE4, 0x00},
+       {MAX98396_R200B_INT_FLAG1, 0x00},
+       {MAX98396_R200C_INT_FLAG2, 0x00},
+       {MAX98396_R200D_INT_FLAG3, 0x00},
+       {MAX98396_R200E_INT_FLAG4, 0x00},
+       {MAX98396_R2010_INT_EN1, 0x02},
+       {MAX98396_R2011_INT_EN2, 0x00},
+       {MAX98396_R2012_INT_EN3, 0x00},
+       {MAX98396_R2013_INT_EN4, 0x00},
+       {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+       {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+       {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+       {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+       {MAX98396_R201F_IRQ_CTRL, 0x00},
+       {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+       {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+       {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+       {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+       {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+       {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+       {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+       {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+       {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+       {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+       {MAX98397_R203A_SPK_MON_THRESH, 0x03},
+       {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+       {MAX98396_R2040_PIN_CFG, 0x55},
+       {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+       {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+       {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+       {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+       {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+       {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+       {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+       {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+       {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+       {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+       {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+       {MAX98397_R204C_PCM_TX_CTRL_9, 0x00},
+       {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF},
+       {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF},
+       {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF},
+       {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF},
+       {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF},
+       {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF},
+       {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF},
+       {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF},
+       {MAX98397_R2056_PCM_RX_SRC1, 0x00},
+       {MAX98397_R2057_PCM_RX_SRC2, 0x00},
+       {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+       {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+       {MAX98396_R205E_PCM_RX_EN, 0x00},
+       {MAX98396_R205F_PCM_TX_EN, 0x00},
+       {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00},
+       {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+       {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+       {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+       {MAX98396_R207F_ICC_EN, 0x00},
+       {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+       {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+       {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+       {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+       {MAX98396_R208F_TONE_GEN_EN, 0x00},
+       {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+       {MAX98396_R2091_AMP_PATH_GAIN, 0x12},
+       {MAX98396_R2092_AMP_DSP_CFG, 0x22},
+       {MAX98396_R2093_SSM_CFG, 0x08},
+       {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+       {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+       {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+       {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+       {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+       {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+       {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+       {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00},
+       {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03},
+       {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC},
+       {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+       {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+       {MAX98396_R20AF_AMP_EN, 0x00},
+       {MAX98396_R20B0_ADC_SR, 0x30},
+       {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+       {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+       {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+       {MAX98397_R20B4_ADC_VDDH_CFG, 0x00},
+       {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00},
+       {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00},
+       {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00},
+       {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00},
+       {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00},
+       {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00},
+       {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00},
+       {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00},
+       {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00},
+       {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00},
+       {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+       {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00},
+       {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00},
+       {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04},
+       {MAX98396_R20C7_ADC_CFG, 0x00},
+       {MAX98396_R20D0_DHT_CFG1, 0x00},
+       {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+       {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+       {MAX98396_R20D3_DHT_CFG2, 0x14},
+       {MAX98396_R20D4_DHT_CFG3, 0x02},
+       {MAX98396_R20D5_DHT_CFG4, 0x04},
+       {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+       {MAX98396_R20DF_DHT_EN, 0x00},
+       {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+       {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+       {MAX98396_R20E5_BPE_STATE, 0x00},
+       {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+       {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+       {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+       {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+       {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+       {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+       {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+       {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+       {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+       {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+       {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+       {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+       {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+       {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+       {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+       {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+       {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+       {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+       {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+       {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+       {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+       {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+       {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+       {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+       {MAX98396_R210D_BPE_EN, 0x00},
+       {MAX98396_R210E_AUTO_RESTART, 0x00},
+       {MAX98396_R210F_GLOBAL_EN, 0x00},
+       {MAX98397_R22FF_REVISION_ID, 0x00},
+};
+
+static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff)
+{
+       regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0);
+       usleep_range(11000, 12000);
+}
+
+static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_component *component = codec_dai->component;
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       unsigned int format = 0;
+       unsigned int bclk_pol = 0;
+       int ret, status;
+       int reg;
+       bool update = false;
+
+       dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+               format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+               break;
+
+       default:
+               dev_err(component->dev, "DAI invert mode unsupported\n");
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               format |= MAX98396_PCM_FORMAT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               format |= MAX98396_PCM_FORMAT_LJ;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               format |= MAX98396_PCM_FORMAT_TDM_MODE1;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               format |= MAX98396_PCM_FORMAT_TDM_MODE0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+       if (ret < 0)
+               return -EINVAL;
+
+       if (status) {
+               ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+               if (ret < 0)
+                       return -EINVAL;
+               if (format != (reg & MAX98396_PCM_BCLKEDGE_BSEL_MASK)) {
+                       update = true;
+               } else {
+                       ret = regmap_read(max98396->regmap,
+                                         MAX98396_R2042_PCM_CLK_SETUP, &reg);
+                       if (ret < 0)
+                               return -EINVAL;
+                       if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE))
+                               update = true;
+               }
+               /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */
+               if (update)
+                       max98396_global_enable_onoff(max98396->regmap, false);
+       }
+
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2041_PCM_MODE_CFG,
+                          MAX98396_PCM_BCLKEDGE_BSEL_MASK,
+                          format);
+
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2042_PCM_CLK_SETUP,
+                          MAX98396_PCM_MODE_CFG_BCLKEDGE,
+                          bclk_pol);
+
+       if (status && update)
+               max98396_global_enable_onoff(max98396->regmap, true);
+
+       return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+       32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98396_get_bclk_sel(int bclk)
+{
+       int i;
+       /* match BCLKs per LRCLK */
+       for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+               if (bclk_sel_table[i] == bclk)
+                       return i + 2;
+       }
+       return 0;
+}
+
+static int max98396_set_clock(struct snd_soc_component *component,
+                             struct snd_pcm_hw_params *params)
+{
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       /* BCLK/LRCLK ratio calculation */
+       int blr_clk_ratio = params_channels(params) * max98396->ch_size;
+       int value;
+
+       if (!max98396->tdm_mode) {
+               /* BCLK configuration */
+               value = max98396_get_bclk_sel(blr_clk_ratio);
+               if (!value) {
+                       dev_err(component->dev, "format unsupported %d\n",
+                               params_format(params));
+                       return -EINVAL;
+               }
+
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2042_PCM_CLK_SETUP,
+                                  MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+                                  value);
+       }
+
+       return 0;
+}
+
+static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       unsigned int sampling_rate = 0;
+       unsigned int chan_sz = 0;
+       int ret, reg;
+       int status;
+       bool update = false;
+
+       /* pcm mode configuration */
+       switch (snd_pcm_format_width(params_format(params))) {
+       case 16:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+               break;
+       case 24:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+               break;
+       case 32:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+               break;
+       default:
+               dev_err(component->dev, "format unsupported %d\n",
+                       params_format(params));
+               goto err;
+       }
+
+       max98396->ch_size = snd_pcm_format_width(params_format(params));
+
+       dev_dbg(component->dev, "format supported %d",
+               params_format(params));
+
+       /* sampling rate configuration */
+       switch (params_rate(params)) {
+       case 8000:
+               sampling_rate = MAX98396_PCM_SR_8000;
+               break;
+       case 11025:
+               sampling_rate = MAX98396_PCM_SR_11025;
+               break;
+       case 12000:
+               sampling_rate = MAX98396_PCM_SR_12000;
+               break;
+       case 16000:
+               sampling_rate = MAX98396_PCM_SR_16000;
+               break;
+       case 22050:
+               sampling_rate = MAX98396_PCM_SR_22050;
+               break;
+       case 24000:
+               sampling_rate = MAX98396_PCM_SR_24000;
+               break;
+       case 32000:
+               sampling_rate = MAX98396_PCM_SR_32000;
+               break;
+       case 44100:
+               sampling_rate = MAX98396_PCM_SR_44100;
+               break;
+       case 48000:
+               sampling_rate = MAX98396_PCM_SR_48000;
+               break;
+       case 88200:
+               sampling_rate = MAX98396_PCM_SR_88200;
+               break;
+       case 96000:
+               sampling_rate = MAX98396_PCM_SR_96000;
+               break;
+       case 192000:
+               sampling_rate = MAX98396_PCM_SR_192000;
+               break;
+       default:
+               dev_err(component->dev, "rate %d not supported\n",
+                       params_rate(params));
+               goto err;
+       }
+
+       ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+       if (ret < 0)
+               goto err;
+
+       if (status) {
+               ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+               if (ret < 0)
+                       goto err;
+               if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) {
+                       update = true;
+               } else {
+                       ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, &reg);
+                       if (ret < 0)
+                               goto err;
+                       if (sampling_rate != (reg & MAX98396_PCM_SR_MASK))
+                               update = true;
+               }
+
+               /* GLOBAL_EN OFF prior to channel size and sampling rate change */
+               if (update)
+                       max98396_global_enable_onoff(max98396->regmap, false);
+       }
+
+       /* set channel size */
+       regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG,
+                          MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+       /* set DAI_SR to correct LRCLK frequency */
+       regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP,
+                          MAX98396_PCM_SR_MASK, sampling_rate);
+
+       /* set sampling rate of IV */
+       if (max98396->interleave_mode &&
+           sampling_rate > MAX98396_PCM_SR_16000)
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2043_PCM_SR_SETUP,
+                                  MAX98396_IVADC_SR_MASK,
+                                  (sampling_rate - 3)
+                                  << MAX98396_IVADC_SR_SHIFT);
+       else
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2043_PCM_SR_SETUP,
+                                  MAX98396_IVADC_SR_MASK,
+                                  sampling_rate << MAX98396_IVADC_SR_SHIFT);
+
+       ret = max98396_set_clock(component, params);
+
+       if (status && update)
+               max98396_global_enable_onoff(max98396->regmap, true);
+
+       return ret;
+
+err:
+       return -EINVAL;
+}
+
+static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
+                                unsigned int tx_mask, unsigned int rx_mask,
+                                int slots, int slot_width)
+{
+       struct snd_soc_component *component = dai->component;
+       struct max98396_priv *max98396 =
+               snd_soc_component_get_drvdata(component);
+       int bsel;
+       unsigned int chan_sz = 0;
+       int ret, status;
+       int reg;
+       bool update = false;
+
+       if (!tx_mask && !rx_mask && !slots && !slot_width)
+               max98396->tdm_mode = false;
+       else
+               max98396->tdm_mode = true;
+
+       /* BCLK configuration */
+       bsel = max98396_get_bclk_sel(slots * slot_width);
+       if (bsel == 0) {
+               dev_err(component->dev, "BCLK %d not supported\n",
+                       slots * slot_width);
+               return -EINVAL;
+       }
+
+       /* Channel size configuration */
+       switch (slot_width) {
+       case 16:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+               break;
+       case 24:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+               break;
+       case 32:
+               chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+               break;
+       default:
+               dev_err(component->dev, "format unsupported %d\n",
+                       slot_width);
+               return -EINVAL;
+       }
+
+       ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+       if (ret < 0)
+               return -EINVAL;
+
+       if (status) {
+               ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, &reg);
+               if (ret < 0)
+                       return -EINVAL;
+               if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) {
+                       update = true;
+               } else {
+                       ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+                       if (ret < 0)
+                               return -EINVAL;
+                       if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK))
+                               update = true;
+               }
+
+               /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */
+               if (update)
+                       max98396_global_enable_onoff(max98396->regmap, false);
+       }
+
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2042_PCM_CLK_SETUP,
+                          MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+                          bsel);
+
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2041_PCM_MODE_CFG,
+                          MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+       /* Rx slot configuration */
+       if (max98396->device_id == CODEC_TYPE_MAX98396) {
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2056_PCM_RX_SRC2,
+                                  MAX98396_PCM_DMIX_CH0_SRC_MASK,
+                                  rx_mask);
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2056_PCM_RX_SRC2,
+                                  MAX98396_PCM_DMIX_CH1_SRC_MASK,
+                                  rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+       } else {
+               regmap_update_bits(max98396->regmap,
+                                  MAX98397_R2057_PCM_RX_SRC2,
+                                  MAX98396_PCM_DMIX_CH0_SRC_MASK,
+                                  rx_mask);
+               regmap_update_bits(max98396->regmap,
+                                  MAX98397_R2057_PCM_RX_SRC2,
+                                  MAX98396_PCM_DMIX_CH1_SRC_MASK,
+                                  rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+       }
+
+       /* Tx slot Hi-Z configuration */
+       if (max98396->device_id == CODEC_TYPE_MAX98396) {
+               regmap_write(max98396->regmap,
+                            MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+                            ~tx_mask & 0xFF);
+               regmap_write(max98396->regmap,
+                            MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+                            (~tx_mask & 0xFF00) >> 8);
+       } else {
+               regmap_write(max98396->regmap,
+                            MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+                            ~tx_mask & 0xFF);
+               regmap_write(max98396->regmap,
+                            MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+                            (~tx_mask & 0xFF00) >> 8);
+       }
+
+       if (status && update)
+               max98396_global_enable_onoff(max98396->regmap, true);
+
+       return 0;
+}
+
+#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98396_dai_ops = {
+       .set_fmt = max98396_dai_set_fmt,
+       .hw_params = max98396_dai_hw_params,
+       .set_tdm_slot = max98396_dai_tdm_slot,
+};
+
+static int max98396_dac_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *component =
+               snd_soc_dapm_to_component(w->dapm);
+       struct max98396_priv *max98396 =
+               snd_soc_component_get_drvdata(component);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               max98396_global_enable_onoff(max98396->regmap, true);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               max98396_global_enable_onoff(max98396->regmap, false);
+
+               max98396->tdm_mode = false;
+               break;
+       default:
+               return 0;
+       }
+       return 0;
+}
+
+static bool max98396_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+       case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+       case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+       case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+       case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+       case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+       case MAX98396_R2027_THERM_FOLDBACK_EN:
+       case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+       case MAX98396_R2033_NOISEGATE_MODE_EN:
+       case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL:
+       case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8:
+       case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2:
+       case MAX98396_R2058_PCM_BYPASS_SRC:
+       case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN:
+       case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+       case MAX98396_R207F_ICC_EN:
+       case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+       case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL:
+       case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL:
+       case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+       case MAX98396_R20C7_ADC_CFG:
+       case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+       case MAX98396_R20DF_DHT_EN:
+       case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+       case MAX98396_R20E4_IV_SENSE_PATH_EN
+               ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+       case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+       case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+       case MAX98396_R21FF_REVISION_ID:
+               return true;
+       default:
+               return false;
+       }
+};
+
+static bool max98396_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98396_R2000_SW_RESET:
+       case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+       case MAX98396_R2041_PCM_MODE_CFG:
+       case MAX98396_R20B6_ADC_PVDD_READBACK_MSB
+               ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+       case MAX98396_R20E5_BPE_STATE:
+       case MAX98396_R2109_BPE_LOW_STATE
+               ... MAX98396_R210B_BPE_LOW_LIMITER:
+       case MAX98396_R210F_GLOBAL_EN:
+       case MAX98396_R21FF_REVISION_ID:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool max98397_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+       case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+       case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+       case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+       case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+       case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+       case MAX98396_R2027_THERM_FOLDBACK_EN:
+       case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+       case MAX98396_R2033_NOISEGATE_MODE_EN:
+       case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH:
+       case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8:
+       case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC:
+       case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL:
+       case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+       case MAX98396_R207F_ICC_EN:
+       case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+       case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG:
+       case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE:
+       case MAX98396_R20C7_ADC_CFG:
+       case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+       case MAX98396_R20DF_DHT_EN:
+       case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+       case MAX98396_R20E4_IV_SENSE_PATH_EN
+               ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+       case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+       case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+       case MAX98397_R22FF_REVISION_ID:
+               return true;
+       default:
+               return false;
+       }
+};
+
+static bool max98397_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+       case MAX98396_R2041_PCM_MODE_CFG:
+       case MAX98397_R20B7_ADC_PVDD_READBACK_MSB
+               ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB:
+       case MAX98396_R20E5_BPE_STATE:
+       case MAX98396_R2109_BPE_LOW_STATE
+               ... MAX98396_R210B_BPE_LOW_LIMITER:
+       case MAX98396_R210F_GLOBAL_EN:
+       case MAX98397_R22FF_REVISION_ID:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const char * const max98396_op_mod_text[] = {
+       "DG", "PVDD", "VBAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum,
+                           MAX98396_R2098_SPK_CLS_DG_MODE,
+                           0, max98396_op_mod_text);
+
+static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv,
+       0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0),
+);
+static DECLARE_TLV_DB_RANGE(max98397_digital_tlv,
+       0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+       0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv,
+       0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0),
+);
+
+static int max98396_mux_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component =
+               snd_soc_dapm_kcontrol_component(kcontrol);
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       int reg, val;
+
+       if (max98396->device_id == CODEC_TYPE_MAX98396)
+               reg = MAX98396_R2055_PCM_RX_SRC1;
+       else
+               reg = MAX98397_R2056_PCM_RX_SRC1;
+
+       regmap_read(max98396->regmap, reg, &val);
+
+       ucontrol->value.enumerated.item[0] = val;
+
+       return 0;
+}
+
+static int max98396_mux_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component =
+               snd_soc_dapm_kcontrol_component(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       int reg, val;
+       int change;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+
+       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+       if (max98396->device_id == CODEC_TYPE_MAX98396)
+               reg = MAX98396_R2055_PCM_RX_SRC1;
+       else
+               reg = MAX98397_R2056_PCM_RX_SRC1;
+
+       change = snd_soc_component_test_bits(component, reg,
+                                            MAX98396_PCM_RX_MASK, val);
+
+       if (change)
+               regmap_update_bits(max98396->regmap, reg,
+                                  MAX98396_PCM_RX_MASK, val);
+
+       snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+       return change;
+}
+
+static const char * const max98396_switch_text[] = {
+       "Left", "Right", "LeftRight"};
+
+static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0,
+                           max98396_switch_text);
+
+static const struct snd_kcontrol_new max98396_dai_mux =
+       SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum,
+                         max98396_mux_get, max98396_mux_put);
+
+static const struct snd_kcontrol_new max98396_vi_control =
+       SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+                          MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+                        &max98396_dai_mux),
+       SND_SOC_DAPM_OUTPUT("BE_OUT"),
+       SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+                            MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+                            MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0),
+       SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+                           &max98396_vi_control),
+       SND_SOC_DAPM_SIGGEN("VMON"),
+       SND_SOC_DAPM_SIGGEN("IMON"),
+       SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static const char * const max98396_thermal_thresh_text[] = {
+       "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C",
+       "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C",
+       "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C",
+       "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C",
+       "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C",
+       "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C",
+       "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C",
+       "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C",
+       "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C",
+       "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C",
+       "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C",
+       "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C",
+       "146C", "147C", "148C", "149C", "150C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum,
+                           MAX98396_R2020_THERM_WARN_THRESH, 0,
+                           max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum,
+                           MAX98396_R2021_THERM_WARN_THRESH2, 0,
+                           max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum,
+                           MAX98396_R2022_THERM_SHDN_THRESH, 0,
+                           max98396_thermal_thresh_text);
+
+static const char * const max98396_thermal_hyteresis_text[] = {
+       "2C", "5C", "7C", "10C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum,
+                           MAX98396_R2023_THERM_HYSTERESIS, 0,
+                           max98396_thermal_hyteresis_text);
+
+static const char * const max98396_foldback_slope_text[] = {
+       "0.25", "0.5", "1.0", "2.0"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum,
+                           MAX98396_R2024_THERM_FOLDBACK_SET,
+                           MAX98396_THERM_FB_SLOPE1_SHIFT,
+                           max98396_foldback_slope_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum,
+                           MAX98396_R2024_THERM_FOLDBACK_SET,
+                           MAX98396_THERM_FB_SLOPE2_SHIFT,
+                           max98396_foldback_slope_text);
+
+static const char * const max98396_foldback_reltime_text[] = {
+       "3ms", "10ms", "100ms", "300ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum,
+                           MAX98396_R2024_THERM_FOLDBACK_SET,
+                           MAX98396_THERM_FB_REL_SHIFT,
+                           max98396_foldback_reltime_text);
+
+static const char * const max98396_foldback_holdtime_text[] = {
+       "0ms", "20ms", "40ms", "80ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum,
+                           MAX98396_R2024_THERM_FOLDBACK_SET,
+                           MAX98396_THERM_FB_HOLD_SHIFT,
+                           max98396_foldback_holdtime_text);
+
+static int max98396_adc_value_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+       int ret;
+       u8 val[2];
+       int reg = mc->reg;
+
+       /* ADC value is not available if the device is powered down */
+       if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+               goto exit;
+
+       if (max98396->device_id == CODEC_TYPE_MAX98397) {
+               switch (mc->reg) {
+               case MAX98396_R20B6_ADC_PVDD_READBACK_MSB:
+                       reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB;
+                       break;
+               case MAX98396_R20B8_ADC_VBAT_READBACK_MSB:
+                       reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB;
+                       break;
+               case MAX98396_R20BA_ADC_TEMP_READBACK_MSB:
+                       reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB;
+                       break;
+               default:
+                       goto exit;
+               }
+       }
+
+       ret = regmap_raw_read(max98396->regmap, reg, &val, 2);
+       if (ret)
+               goto exit;
+
+       /* ADC readback bits[8:0] rearrangement */
+       ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1);
+       return 0;
+
+exit:
+       ucontrol->value.integer.value[0] = 0;
+       return 0;
+}
+
+static const struct snd_kcontrol_new max98396_snd_controls[] = {
+       /* Volume */
+       SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+                      0, 0x7F, 1, max98396_digital_tlv),
+       SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+                      0, 0x11, 0, max98396_spk_tlv),
+       /* Volume Ramp Up/Down Enable*/
+       SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+       SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+       /* Clock Monitor Enable */
+       SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+                  MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+       /* Dither Enable */
+       SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+       /* DC Blocker Enable */
+       SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+       /* Speaker Safe Mode Enable */
+       SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+       /* Wideband Filter Enable */
+       SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+       /* Dynamic Headroom Tracking */
+       SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+       /* Brownout Protection Engine */
+       SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+       SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+       /* Bypass Path Enable */
+       SOC_SINGLE("Bypass Path Switch",
+                  MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+       /* Speaker Operation Mode */
+       SOC_ENUM("OP Mode", max98396_op_mod_enum),
+       /* Auto Restart functions */
+       SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+                  MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+       SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_OVC_RESTART_SHFT, 1, 0),
+       /* Thermal Threshold */
+       SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+       SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+       SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+       SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+       SOC_SINGLE("THERM Foldback Switch",
+                  MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+       SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+       SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+       SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+       SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+       /* ADC */
+       SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+       SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+       SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+};
+
+static const struct snd_kcontrol_new max98397_snd_controls[] = {
+       /* Volume */
+       SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+                      0, 0xFF, 1, max98397_digital_tlv),
+       SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+                      0, 0x15, 0, max98397_spk_tlv),
+       /* Volume Ramp Up/Down Enable*/
+       SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+       SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+       /* Clock Monitor Enable */
+       SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+                  MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+       /* Dither Enable */
+       SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+       /* DC Blocker Enable */
+       SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+       /* Speaker Safe Mode Enable */
+       SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+       /* Wideband Filter Enable */
+       SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+                  MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+       SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                  MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+       /* Dynamic Headroom Tracking */
+       SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+       /* Brownout Protection Engine */
+       SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+       SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+       /* Bypass Path Enable */
+       SOC_SINGLE("Bypass Path Switch",
+                  MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+       /* Speaker Operation Mode */
+       SOC_ENUM("OP Mode", max98396_op_mod_enum),
+       /* Auto Restart functions */
+       SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+                  MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+       SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+       SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+                  MAX98396_OVC_RESTART_SHFT, 1, 0),
+       /* Thermal Threshold */
+       SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+       SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+       SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+       SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+       SOC_SINGLE("THERM Foldback Switch",
+                  MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+       SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+       SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+       SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+       SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+       /* ADC */
+       SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+       SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+       SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+                      max98396_adc_value_get, NULL),
+};
+
+static const struct snd_soc_dapm_route max98396_audio_map[] = {
+       /* Plabyack */
+       {"DAI Sel Mux", "Left", "Amp Enable"},
+       {"DAI Sel Mux", "Right", "Amp Enable"},
+       {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+       {"BE_OUT", NULL, "DAI Sel Mux"},
+       /* Capture */
+       { "VI Sense", "Switch", "VMON" },
+       { "VI Sense", "Switch", "IMON" },
+       { "Voltage Sense", NULL, "VI Sense" },
+       { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98396_dai[] = {
+       {
+               .name = "max98396-aif1",
+               .playback = {
+                       .stream_name = "HiFi Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = MAX98396_RATES,
+                       .formats = MAX98396_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "HiFi Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = MAX98396_RATES,
+                       .formats = MAX98396_FORMATS,
+               },
+               .ops = &max98396_dai_ops,
+       }
+};
+
+static struct snd_soc_dai_driver max98397_dai[] = {
+       {
+               .name = "max98397-aif1",
+               .playback = {
+                       .stream_name = "HiFi Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = MAX98396_RATES,
+                       .formats = MAX98396_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "HiFi Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = MAX98396_RATES,
+                       .formats = MAX98396_FORMATS,
+               },
+               .ops = &max98396_dai_ops,
+       }
+};
+
+static void max98396_reset(struct max98396_priv *max98396, struct device *dev)
+{
+       int ret, reg, count;
+
+       /* Software Reset */
+       ret = regmap_write(max98396->regmap,
+                          MAX98396_R2000_SW_RESET, 1);
+       if (ret)
+               dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+       count = 0;
+       while (count < 3) {
+               usleep_range(5000, 6000);
+               /* Software Reset Verification */
+               ret = regmap_read(max98396->regmap,
+                                 GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+               if (!ret) {
+                       dev_info(dev, "Reset completed (retry:%d)\n", count);
+                       return;
+               }
+               count++;
+       }
+       dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+
+static int max98396_probe(struct snd_soc_component *component)
+{
+       struct max98396_priv *max98396 =
+               snd_soc_component_get_drvdata(component);
+
+       /* Software Reset */
+       max98396_reset(max98396, component->dev);
+
+       /* L/R mix configuration */
+       if (max98396->device_id == CODEC_TYPE_MAX98396) {
+               regmap_write(max98396->regmap,
+                            MAX98396_R2055_PCM_RX_SRC1, 0x02);
+               regmap_write(max98396->regmap,
+                            MAX98396_R2056_PCM_RX_SRC2, 0x10);
+       } else {
+               regmap_write(max98396->regmap,
+                            MAX98397_R2056_PCM_RX_SRC1, 0x02);
+               regmap_write(max98396->regmap,
+                            MAX98397_R2057_PCM_RX_SRC2, 0x10);
+       }
+       /* Enable DC blocker */
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2092_AMP_DSP_CFG, 1, 1);
+       /* Enable IV Monitor DC blocker */
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R20E0_IV_SENSE_PATH_CFG,
+                          MAX98396_IV_SENSE_DCBLK_EN_MASK,
+                          MAX98396_IV_SENSE_DCBLK_EN_MASK);
+       /* Configure default data output sources */
+       regmap_write(max98396->regmap,
+                    MAX98396_R205D_PCM_TX_SRC_EN, 3);
+       /* Enable Wideband Filter */
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40);
+       /* Enable IV Wideband Filter */
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8);
+
+       /* Enable Bypass Source */
+       regmap_write(max98396->regmap,
+                    MAX98396_R2058_PCM_BYPASS_SRC,
+                    max98396->bypass_slot);
+       /* Voltage, current slot configuration */
+       regmap_write(max98396->regmap,
+                    MAX98396_R2044_PCM_TX_CTRL_1,
+                    max98396->v_slot);
+       regmap_write(max98396->regmap,
+                    MAX98396_R2045_PCM_TX_CTRL_2,
+                    max98396->i_slot);
+
+       if (max98396->v_slot < 8)
+               if (max98396->device_id == CODEC_TYPE_MAX98396)
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+                                          1 << max98396->v_slot, 0);
+               else
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+                                          1 << max98396->v_slot, 0);
+       else
+               if (max98396->device_id == CODEC_TYPE_MAX98396)
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+                                          1 << (max98396->v_slot - 8), 0);
+               else
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+                                          1 << (max98396->v_slot - 8), 0);
+
+       if (max98396->i_slot < 8)
+               if (max98396->device_id == CODEC_TYPE_MAX98396)
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+                                          1 << max98396->i_slot, 0);
+               else
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+                                          1 << max98396->i_slot, 0);
+       else
+               if (max98396->device_id == CODEC_TYPE_MAX98396)
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+                                          1 << (max98396->i_slot - 8), 0);
+               else
+                       regmap_update_bits(max98396->regmap,
+                                          MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+                                          1 << (max98396->i_slot - 8), 0);
+
+       /* Set interleave mode */
+       if (max98396->interleave_mode)
+               regmap_update_bits(max98396->regmap,
+                                  MAX98396_R2041_PCM_MODE_CFG,
+                                  MAX98396_PCM_TX_CH_INTERLEAVE_MASK,
+                                  MAX98396_PCM_TX_CH_INTERLEAVE_MASK);
+
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R2038_CLK_MON_CTRL,
+                          MAX98396_CLK_MON_AUTO_RESTART_MASK,
+                          MAX98396_CLK_MON_AUTO_RESTART_MASK);
+
+       /* Speaker Amplifier PCM RX Enable by default */
+       regmap_update_bits(max98396->regmap,
+                          MAX98396_R205E_PCM_RX_EN,
+                          MAX98396_PCM_RX_EN_MASK, 1);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98396_suspend(struct device *dev)
+{
+       struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+       regcache_cache_only(max98396->regmap, true);
+       regcache_mark_dirty(max98396->regmap);
+       return 0;
+}
+
+static int max98396_resume(struct device *dev)
+{
+       struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+       regcache_cache_only(max98396->regmap, false);
+       max98396_reset(max98396, dev);
+       regcache_sync(max98396->regmap);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98396_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98396 = {
+       .probe                  = max98396_probe,
+       .controls               = max98396_snd_controls,
+       .num_controls           = ARRAY_SIZE(max98396_snd_controls),
+       .dapm_widgets           = max98396_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(max98396_dapm_widgets),
+       .dapm_routes            = max98396_audio_map,
+       .num_dapm_routes        = ARRAY_SIZE(max98396_audio_map),
+       .idle_bias_on           = 1,
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+       .non_legacy_dai_naming  = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98397 = {
+       .probe                  = max98396_probe,
+       .controls               = max98397_snd_controls,
+       .num_controls           = ARRAY_SIZE(max98397_snd_controls),
+       .dapm_widgets           = max98396_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(max98396_dapm_widgets),
+       .dapm_routes            = max98396_audio_map,
+       .num_dapm_routes        = ARRAY_SIZE(max98396_audio_map),
+       .idle_bias_on           = 1,
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+       .non_legacy_dai_naming  = 1,
+};
+
+static const struct regmap_config max98396_regmap = {
+       .reg_bits = 16,
+       .val_bits = 8,
+       .max_register = MAX98396_R21FF_REVISION_ID,
+       .reg_defaults  = max98396_reg,
+       .num_reg_defaults = ARRAY_SIZE(max98396_reg),
+       .readable_reg = max98396_readable_register,
+       .volatile_reg = max98396_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config max98397_regmap = {
+       .reg_bits = 16,
+       .val_bits = 8,
+       .max_register = MAX98397_R22FF_REVISION_ID,
+       .reg_defaults  = max98397_reg,
+       .num_reg_defaults = ARRAY_SIZE(max98397_reg),
+       .readable_reg = max98397_readable_register,
+       .volatile_reg = max98397_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98396_read_device_property(struct device *dev,
+                                         struct max98396_priv *max98396)
+{
+       int value;
+
+       if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+               max98396->v_slot = value & 0xF;
+       else
+               max98396->v_slot = 0;
+
+       if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+               max98396->i_slot = value & 0xF;
+       else
+               max98396->i_slot = 1;
+
+       if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value))
+               max98396->bypass_slot = value & 0xF;
+       else
+               max98396->bypass_slot = 0;
+}
+
+static int max98396_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct max98396_priv *max98396 = NULL;
+       int ret, reg;
+
+       max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
+
+       if (!max98396) {
+               ret = -ENOMEM;
+               return ret;
+       }
+       i2c_set_clientdata(i2c, max98396);
+
+       max98396->device_id =  id->driver_data;
+
+       /* regmap initialization */
+       if (max98396->device_id == CODEC_TYPE_MAX98396)
+               max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap);
+
+       else
+               max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap);
+
+       if (IS_ERR(max98396->regmap)) {
+               ret = PTR_ERR(max98396->regmap);
+               dev_err(&i2c->dev,
+                       "Failed to allocate regmap: %d\n", ret);
+               return ret;
+       }
+
+       /* update interleave mode info */
+       if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
+               max98396->interleave_mode = true;
+       else
+               max98396->interleave_mode = false;
+
+       /* voltage/current slot & gpio configuration */
+       max98396_read_device_property(&i2c->dev, max98396);
+
+       /* Reset the Device */
+       max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+                                                      "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(max98396->reset_gpio)) {
+               ret = PTR_ERR(max98396->reset_gpio);
+               dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+               return ret;
+       }
+
+       if (max98396->reset_gpio) {
+               usleep_range(5000, 6000);
+               gpiod_set_value_cansleep(max98396->reset_gpio, 0);
+               /* Wait for the hw reset done */
+               usleep_range(5000, 6000);
+       }
+
+       ret = regmap_read(max98396->regmap,
+                         GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "%s: failed to read revision of the device.\n",  id->name);
+               return ret;
+       }
+       dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg);
+
+       /* codec registration */
+       if (max98396->device_id == CODEC_TYPE_MAX98396)
+               ret = devm_snd_soc_register_component(&i2c->dev,
+                                                     &soc_codec_dev_max98396,
+                                                     max98396_dai,
+                                                     ARRAY_SIZE(max98396_dai));
+       else
+               ret = devm_snd_soc_register_component(&i2c->dev,
+                                                     &soc_codec_dev_max98397,
+                                                     max98397_dai,
+                                                     ARRAY_SIZE(max98397_dai));
+       if (ret < 0)
+               dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+       return ret;
+}
+
+static const struct i2c_device_id max98396_i2c_id[] = {
+       { "max98396", CODEC_TYPE_MAX98396},
+       { "max98397", CODEC_TYPE_MAX98397},
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98396_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98396_of_match[] = {
+       { .compatible = "adi,max98396", },
+       { .compatible = "adi,max98397", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max98396_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98396_acpi_match[] = {
+       { "ADS8396", 0 },
+       { "ADS8397", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, max98396_acpi_match);
+#endif
+
+static struct i2c_driver max98396_i2c_driver = {
+       .driver = {
+               .name = "max98396",
+               .of_match_table = of_match_ptr(max98396_of_match),
+               .acpi_match_table = ACPI_PTR(max98396_acpi_match),
+               .pm = &max98396_pm,
+       },
+       .probe = max98396_i2c_probe,
+       .id_table = max98396_i2c_id,
+};
+
+module_i2c_driver(max98396_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98396 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h
new file mode 100644 (file)
index 0000000..6944110
--- /dev/null
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98396.h -- MAX98396 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98396_H
+#define _MAX98396_H
+
+#define MAX98396_R2000_SW_RESET                        0x2000
+#define MAX98396_R2001_INT_RAW1                        0x2001
+#define MAX98396_R2002_INT_RAW2                        0x2002
+#define MAX98396_R2003_INT_RAW3                        0x2003
+#define MAX98396_R2004_INT_RAW4                        0x2004
+#define MAX98396_R2006_INT_STATE1              0x2006
+#define MAX98396_R2007_INT_STATE2              0x2007
+#define MAX98396_R2008_INT_STATE3              0x2008
+#define MAX98396_R2009_INT_STATE4              0x2009
+#define MAX98396_R200B_INT_FLAG1               0x200B
+#define MAX98396_R200C_INT_FLAG2               0x200C
+#define MAX98396_R200D_INT_FLAG3               0x200D
+#define MAX98396_R200E_INT_FLAG4               0x200E
+#define MAX98396_R2010_INT_EN1                 0x2010
+#define MAX98396_R2011_INT_EN2                 0x2011
+#define MAX98396_R2012_INT_EN3                 0x2012
+#define MAX98396_R2013_INT_EN4                 0x2013
+#define MAX98396_R2015_INT_FLAG_CLR1           0x2015
+#define MAX98396_R2016_INT_FLAG_CLR2           0x2016
+#define MAX98396_R2017_INT_FLAG_CLR3           0x2017
+#define MAX98396_R2018_INT_FLAG_CLR4           0x2018
+#define MAX98396_R201F_IRQ_CTRL                        0x201F
+#define MAX98396_R2020_THERM_WARN_THRESH       0x2020
+#define MAX98396_R2021_THERM_WARN_THRESH2      0x2021
+#define MAX98396_R2022_THERM_SHDN_THRESH       0x2022
+#define MAX98396_R2023_THERM_HYSTERESIS                0x2023
+#define MAX98396_R2024_THERM_FOLDBACK_SET      0x2024
+#define MAX98396_R2027_THERM_FOLDBACK_EN       0x2027
+#define MAX98396_R2030_NOISEGATE_MODE_CTRL     0x2030
+#define MAX98396_R2033_NOISEGATE_MODE_EN       0x2033
+#define MAX98396_R2038_CLK_MON_CTRL            0x2038
+#define MAX98396_R2039_DATA_MON_CTRL           0x2039
+#define MAX98396_R203F_ENABLE_CTRLS            0x203F
+#define MAX98396_R2040_PIN_CFG                 0x2040
+#define MAX98396_R2041_PCM_MODE_CFG            0x2041
+#define MAX98396_R2042_PCM_CLK_SETUP           0x2042
+#define MAX98396_R2043_PCM_SR_SETUP            0x2043
+#define MAX98396_R2044_PCM_TX_CTRL_1           0x2044
+#define MAX98396_R2045_PCM_TX_CTRL_2           0x2045
+#define MAX98396_R2046_PCM_TX_CTRL_3           0x2046
+#define MAX98396_R2047_PCM_TX_CTRL_4           0x2047
+#define MAX98396_R2048_PCM_TX_CTRL_5           0x2048
+#define MAX98396_R2049_PCM_TX_CTRL_6           0x2049
+#define MAX98396_R204A_PCM_TX_CTRL_7           0x204A
+#define MAX98396_R204B_PCM_TX_CTRL_8           0x204B
+#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1       0x204C
+#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2       0x204D
+#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3       0x204E
+#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4       0x204F
+#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5       0x2050
+#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6       0x2051
+#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7       0x2052
+#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8       0x2053
+#define MAX98396_R2055_PCM_RX_SRC1             0x2055
+#define MAX98396_R2056_PCM_RX_SRC2             0x2056
+#define MAX98396_R2058_PCM_BYPASS_SRC          0x2058
+#define MAX98396_R205D_PCM_TX_SRC_EN           0x205D
+#define MAX98396_R205E_PCM_RX_EN               0x205E
+#define MAX98396_R205F_PCM_TX_EN               0x205F
+#define MAX98396_R2070_ICC_RX_EN_A             0x2070
+#define MAX98396_R2071_ICC_RX_EN_B             0x2071
+#define MAX98396_R2072_ICC_TX_CTRL             0x2072
+#define MAX98396_R207F_ICC_EN                  0x207F
+#define MAX98396_R2083_TONE_GEN_DC_CFG         0x2083
+#define MAX98396_R2084_TONE_GEN_DC_LVL1                0x2084
+#define MAX98396_R2085_TONE_GEN_DC_LVL2                0x2085
+#define MAX98396_R2086_TONE_GEN_DC_LVL3                0x2086
+#define MAX98396_R208F_TONE_GEN_EN             0x208F
+#define MAX98396_R2090_AMP_VOL_CTRL            0x2090
+#define MAX98396_R2091_AMP_PATH_GAIN           0x2091
+#define MAX98396_R2092_AMP_DSP_CFG             0x2092
+#define MAX98396_R2093_SSM_CFG                 0x2093
+#define MAX98396_R2094_SPK_CLS_DG_THRESH       0x2094
+#define MAX98396_R2095_SPK_CLS_DG_HDR          0x2095
+#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME    0x2096
+#define MAX98396_R2097_SPK_CLS_DG_DELAY                0x2097
+#define MAX98396_R2098_SPK_CLS_DG_MODE         0x2098
+#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL     0x2099
+#define MAX98396_R209A_SPK_EDGE_CTRL           0x209A
+#define MAX98396_R209C_SPK_EDGE_CTRL1          0x209C
+#define MAX98396_R209D_SPK_EDGE_CTRL2          0x209D
+#define MAX98396_R209E_AMP_CLIP_GAIN           0x209E
+#define MAX98396_R209F_BYPASS_PATH_CFG         0x209F
+#define MAX98396_R20A0_AMP_SUPPLY_CTL          0x20A0
+#define MAX98396_R20AF_AMP_EN                  0x20AF
+#define MAX98396_R20B0_ADC_SR                  0x20B0
+#define MAX98396_R20B1_ADC_PVDD_CFG            0x20B1
+#define MAX98396_R20B2_ADC_VBAT_CFG            0x20B2
+#define MAX98396_R20B3_ADC_THERMAL_CFG         0x20B3
+#define MAX98396_R20B4_ADC_READBACK_CTRL1      0x20B4
+#define MAX98396_R20B5_ADC_READBACK_CTRL2      0x20B5
+#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB   0x20B6
+#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB   0x20B7
+#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB   0x20B8
+#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB   0x20B9
+#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB   0x20BA
+#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB   0x20BB
+#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB        0x20BC
+#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB        0x20BD
+#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB        0x20BE
+#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB        0x20BF
+#define MAX98396_R20C7_ADC_CFG                 0x20C7
+#define MAX98396_R20D0_DHT_CFG1                        0x20D0
+#define MAX98396_R20D1_LIMITER_CFG1            0x20D1
+#define MAX98396_R20D2_LIMITER_CFG2            0x20D2
+#define MAX98396_R20D3_DHT_CFG2                        0x20D3
+#define MAX98396_R20D4_DHT_CFG3                        0x20D4
+#define MAX98396_R20D5_DHT_CFG4                        0x20D5
+#define MAX98396_R20D6_DHT_HYSTERESIS_CFG      0x20D6
+#define MAX98396_R20DF_DHT_EN                  0x20DF
+#define MAX98396_R20E0_IV_SENSE_PATH_CFG       0x20E0
+#define MAX98396_R20E4_IV_SENSE_PATH_EN                0x20E4
+#define MAX98396_R20E5_BPE_STATE               0x20E5
+#define MAX98396_R20E6_BPE_L3_THRESH_MSB       0x20E6
+#define MAX98396_R20E7_BPE_L3_THRESH_LSB       0x20E7
+#define MAX98396_R20E8_BPE_L2_THRESH_MSB       0x20E8
+#define MAX98396_R20E9_BPE_L2_THRESH_LSB       0x20E9
+#define MAX98396_R20EA_BPE_L1_THRESH_MSB       0x20EA
+#define MAX98396_R20EB_BPE_L1_THRESH_LSB       0x20EB
+#define MAX98396_R20EC_BPE_L0_THRESH_MSB       0x20EC
+#define MAX98396_R20ED_BPE_L0_THRESH_LSB       0x20ED
+#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME  0x20EE
+#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME  0x20EF
+#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME  0x20F0
+#define MAX98396_R20F1_BPE_L0_HOLD_TIME                0x20F1
+#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP  0x20F2
+#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP  0x20F3
+#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP  0x20F4
+#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP  0x20F5
+#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN    0x20F6
+#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN    0x20F7
+#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN    0x20F8
+#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN    0x20F9
+#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE     0x20FA
+#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE     0x20FB
+#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE     0x20FC
+#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE     0x20FD
+#define MAX98396_R20FE_BPE_L3_LIMITER_CFG      0x20FE
+#define MAX98396_R20FF_BPE_L2_LIMITER_CFG      0x20FF
+#define MAX98396_R2100_BPE_L1_LIMITER_CFG      0x2100
+#define MAX98396_R2101_BPE_L0_LIMITER_CFG      0x2101
+#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102
+#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103
+#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104
+#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105
+#define MAX98396_R2106_BPE_THRESH_HYSTERESIS   0x2106
+#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR   0x2107
+#define MAX98396_R2108_BPE_SUPPLY_SRC          0x2108
+#define MAX98396_R2109_BPE_LOW_STATE           0x2109
+#define MAX98396_R210A_BPE_LOW_GAIN            0x210A
+#define MAX98396_R210B_BPE_LOW_LIMITER         0x210B
+#define MAX98396_R210D_BPE_EN                  0x210D
+#define MAX98396_R210E_AUTO_RESTART            0x210E
+#define MAX98396_R210F_GLOBAL_EN               0x210F
+#define MAX98396_R21FF_REVISION_ID             0x21FF
+
+/* MAX98927 Registers */
+#define MAX98397_R203A_SPK_MON_THRESH          0x203A
+#define MAX98397_R204C_PCM_TX_CTRL_9           0x204C
+#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1       0x204D
+#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2       0x204E
+#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3       0x204F
+#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4       0x2050
+#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5       0x2051
+#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6       0x2052
+#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7       0x2053
+#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8       0x2054
+#define MAX98397_R2056_PCM_RX_SRC1             0x2056
+#define MAX98397_R2057_PCM_RX_SRC2             0x2057
+#define MAX98397_R2060_PCM_TX_SUPPLY_SEL       0x2060
+#define MAX98397_R209B_SPK_PATH_WB_ONLY                0x209B
+#define MAX98397_R20B4_ADC_VDDH_CFG            0x20B4
+#define MAX98397_R20B5_ADC_READBACK_CTRL1      0x20B5
+#define MAX98397_R20B6_ADC_READBACK_CTRL2      0x20B6
+#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB   0x20B7
+#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB   0x20B8
+#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB   0x20B9
+#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB   0x20BA
+#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB   0x20BB
+#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB   0x20BC
+#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB  0x20BD
+#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB   0x20BE
+#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB        0x20BF
+#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB        0x20C0
+#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB        0x20C1
+#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB        0x20C2
+#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB        0x20C3
+#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB        0x20C4
+#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE   0x20C5
+#define MAX98397_R22FF_REVISION_ID             0x22FF
+
+#define GET_REG_ADDR_REV_ID(x)\
+       ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID)
+
+/* MAX98396_R2024_THERM_FOLDBACK_SET */
+#define MAX98396_THERM_FB_SLOPE1_SHIFT         (0)
+#define MAX98396_THERM_FB_SLOPE2_SHIFT         (2)
+#define MAX98396_THERM_FB_REL_SHIFT            (4)
+#define MAX98396_THERM_FB_HOLD_SHIFT           (6)
+
+/* MAX98396_R2038_CLK_MON_CTRL */
+#define MAX98396_CLK_MON_AUTO_RESTART_MASK     (0x1 << 0)
+#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT    (0)
+
+/* MAX98396_R203F_ENABLE_CTRLS */
+#define MAX98396_CTRL_CMON_EN_SHIFT            (0)
+
+/* MAX98396_R2041_PCM_MODE_CFG */
+#define MAX98396_PCM_MODE_CFG_FORMAT_MASK      (0x7 << 3)
+#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK     (0x1 << 2)
+#define MAX98396_PCM_FORMAT_I2S                        (0x0 << 3)
+#define MAX98396_PCM_FORMAT_LJ                 (0x1 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE0          (0x3 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE1          (0x4 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE2          (0x5 << 3)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK      (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_16                (0x1 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_24                (0x2 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_32                (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_LRCLKEDGE                (0x1 << 1)
+
+/* MAX98396_R2042_PCM_CLK_SETUP */
+#define MAX98396_PCM_MODE_CFG_BCLKEDGE         (0x1 << 4)
+#define MAX98396_PCM_CLK_SETUP_BSEL_MASK       (0xF << 0)
+#define MAX98396_PCM_BCLKEDGE_BSEL_MASK                (0x1F)
+
+/* MAX98396_R2043_PCM_SR_SETUP */
+#define MAX98396_PCM_SR_SHIFT                  (0)
+#define MAX98396_IVADC_SR_SHIFT                        (4)
+#define MAX98396_PCM_SR_MASK                   (0xF << MAX98396_PCM_SR_SHIFT)
+#define MAX98396_IVADC_SR_MASK                 (0xF << MAX98396_IVADC_SR_SHIFT)
+#define MAX98396_PCM_SR_8000                   (0)
+#define MAX98396_PCM_SR_11025                  (1)
+#define MAX98396_PCM_SR_12000                  (2)
+#define MAX98396_PCM_SR_16000                  (3)
+#define MAX98396_PCM_SR_22050                  (4)
+#define MAX98396_PCM_SR_24000                  (5)
+#define MAX98396_PCM_SR_32000                  (6)
+#define MAX98396_PCM_SR_44100                  (7)
+#define MAX98396_PCM_SR_48000                  (8)
+#define MAX98396_PCM_SR_88200                  (9)
+#define MAX98396_PCM_SR_96000                  (10)
+#define MAX98396_PCM_SR_176400                 (11)
+#define MAX98396_PCM_SR_192000                 (12)
+
+/* MAX98396_R2055_PCM_RX_SRC1 */
+#define MAX98396_PCM_RX_MASK                   (0x3 << 0)
+
+/* MAX98396_R2056_PCM_RX_SRC2 */
+#define MAX98396_PCM_DMIX_CH1_SHIFT            (0xF << 0)
+#define MAX98396_PCM_DMIX_CH0_SRC_MASK         (0xF << 0)
+#define MAX98396_PCM_DMIX_CH1_SRC_MASK         (0xF << MAX98396_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98396_R205E_PCM_RX_EN */
+#define MAX98396_PCM_RX_EN_MASK                        (0x1 << 0)
+#define MAX98396_PCM_RX_BYP_EN_MASK            (0x1 << 1)
+
+/* MAX98396_R2092_AMP_DSP_CFG */
+#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT                (0)
+#define MAX98396_DSP_SPK_DITH_EN_SHIFT         (1)
+#define MAX98396_DSP_SPK_INVERT_SHIFT          (2)
+#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT       (3)
+#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT       (4)
+#define MAX98396_DSP_SPK_SAFE_EN_SHIFT         (5)
+#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT       (6)
+
+/* MAX98396_R20E0_IV_SENSE_PATH_CFG */
+#define MAX98396_IV_SENSE_DCBLK_EN_MASK                (0x3 << 0)
+#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT       (0)
+#define MAX98396_IV_SENSE_DITH_EN_SHIFT                (2)
+#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT      (3)
+
+/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */
+#define MAX98396_PVDD_UVLO_RESTART_SHFT                (0)
+#define MAX98396_VBAT_UVLO_RESTART_SHFT                (1)
+#define MAX98396_THEM_SHDN_RESTART_SHFT                (2)
+#define MAX98396_OVC_RESTART_SHFT              (3)
+
+enum {
+       CODEC_TYPE_MAX98396,
+       CODEC_TYPE_MAX98397,
+};
+
+struct max98396_priv {
+       struct regmap *regmap;
+       struct gpio_desc *reset_gpio;
+       unsigned int v_slot;
+       unsigned int i_slot;
+       unsigned int bypass_slot;
+       bool interleave_mode;
+       unsigned int ch_size;
+       bool tdm_mode;
+       int device_id;
+};
+#endif
index e073f0e..9ca6fc2 100644 (file)
@@ -299,8 +299,7 @@ static const struct snd_soc_component_driver soc_component_dev_max9850 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int max9850_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int max9850_i2c_probe(struct i2c_client *i2c)
 {
        struct max9850_priv *max9850;
        int ret;
@@ -331,7 +330,7 @@ static struct i2c_driver max9850_i2c_driver = {
        .driver = {
                .name = "max9850",
        },
-       .probe = max9850_i2c_probe,
+       .probe_new = max9850_i2c_probe,
        .id_table = max9850_i2c_id,
 };
 
index a5aa124..0daa2a3 100644 (file)
@@ -291,6 +291,7 @@ static const struct snd_soc_component_driver max98504_component_driver = {
        .num_dapm_widgets       = ARRAY_SIZE(max98504_dapm_widgets),
        .dapm_routes            = max98504_dapm_routes,
        .num_dapm_routes        = ARRAY_SIZE(max98504_dapm_routes),
+       .endianness             = 1,
 };
 
 static const struct regmap_config max98504_regmap = {
@@ -304,8 +305,7 @@ static const struct regmap_config max98504_regmap = {
        .cache_type             = REGCACHE_RBTREE,
 };
 
-static int max98504_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int max98504_i2c_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device_node *node = dev->of_node;
@@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = {
                .name = "max98504",
                .of_match_table = of_match_ptr(max98504_of_match),
        },
-       .probe = max98504_i2c_probe,
+       .probe_new = max98504_i2c_probe,
        .id_table = max98504_i2c_id,
 };
 module_i2c_driver(max98504_i2c_driver);
index bb8649c..f0f085e 100644 (file)
@@ -677,7 +677,7 @@ static void max98520_power_on(struct max98520_priv *max98520, bool poweron)
                gpiod_set_value_cansleep(max98520->reset_gpio, !poweron);
 }
 
-static int max98520_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+static int max98520_i2c_probe(struct i2c_client *i2c)
 {
        int ret;
        int reg = 0;
@@ -757,7 +757,7 @@ static struct i2c_driver max98520_i2c_driver = {
                .of_match_table = of_match_ptr(max98520_of_match),
                .pm = &max98520_pm,
        },
-       .probe = max98520_i2c_probe,
+       .probe_new = max98520_i2c_probe,
        .id_table = max98520_i2c_id,
 };
 
index c2b1151..eb628b7 100644 (file)
@@ -613,8 +613,7 @@ static const struct regmap_config max9867_regmap = {
        .cache_type     = REGCACHE_RBTREE,
 };
 
-static int max9867_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int max9867_i2c_probe(struct i2c_client *i2c)
 {
        struct max9867_priv *max9867;
        int ret, reg;
@@ -662,7 +661,7 @@ static struct i2c_driver max9867_i2c_driver = {
                .name = "max9867",
                .of_match_table = of_match_ptr(max9867_of_match),
        },
-       .probe  = max9867_i2c_probe,
+       .probe_new  = max9867_i2c_probe,
        .id_table = max9867_i2c_id,
 };
 
index 71fede9..3bf8632 100644 (file)
@@ -133,8 +133,7 @@ static const struct regmap_config max9877_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int max9877_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int max9877_i2c_probe(struct i2c_client *client)
 {
        struct regmap *regmap;
        int i;
@@ -161,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = {
        .driver = {
                .name = "max9877",
        },
-       .probe = max9877_i2c_probe,
+       .probe_new = max9877_i2c_probe,
        .id_table = max9877_i2c_id,
 };
 
index f34fa27..63849eb 100644 (file)
@@ -558,8 +558,7 @@ static const struct regmap_config max98925_regmap = {
        .cache_type       = REGCACHE_RBTREE,
 };
 
-static int max98925_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int max98925_i2c_probe(struct i2c_client *i2c)
 {
        int ret, reg;
        u32 value;
@@ -637,7 +636,7 @@ static struct i2c_driver max98925_i2c_driver = {
                .name = "max98925",
                .of_match_table = of_match_ptr(max98925_of_match),
        },
-       .probe  = max98925_i2c_probe,
+       .probe_new  = max98925_i2c_probe,
        .id_table = max98925_i2c_id,
 };
 
index 1fbbc62..56e0a87 100644 (file)
@@ -510,8 +510,7 @@ static const struct regmap_config max98926_regmap = {
        .cache_type             = REGCACHE_RBTREE,
 };
 
-static int max98926_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int max98926_i2c_probe(struct i2c_client *i2c)
 {
        int ret, reg;
        u32 value;
@@ -584,7 +583,7 @@ static struct i2c_driver max98926_i2c_driver = {
                .name = "max98926",
                .of_match_table = of_match_ptr(max98926_of_match),
        },
-       .probe  = max98926_i2c_probe,
+       .probe_new = max98926_i2c_probe,
        .id_table = max98926_i2c_id,
 };
 
index bf78d3c..b7cff76 100644 (file)
@@ -863,8 +863,7 @@ static void max98927_slot_config(struct i2c_client *i2c,
                max98927->i_l_slot = 1;
 }
 
-static int max98927_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int max98927_i2c_probe(struct i2c_client *i2c)
 {
 
        int ret = 0, value;
@@ -977,7 +976,7 @@ static struct i2c_driver max98927_i2c_driver = {
                .acpi_match_table = ACPI_PTR(max98927_acpi_match),
                .pm = &max98927_pm,
        },
-       .probe  = max98927_i2c_probe,
+       .probe_new = max98927_i2c_probe,
        .remove = max98927_i2c_remove,
        .id_table = max98927_i2c_id,
 };
index 0823527..de8fcbd 100644 (file)
@@ -550,8 +550,7 @@ static const struct regmap_config ml26124_i2c_regmap = {
        .write_flag_mask = 0x01,
 };
 
-static int ml26124_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int ml26124_i2c_probe(struct i2c_client *i2c)
 {
        struct ml26124_priv *priv;
        int ret;
@@ -583,7 +582,7 @@ static struct i2c_driver ml26124_i2c_driver = {
        .driver = {
                .name = "ml26124",
        },
-       .probe = ml26124_i2c_probe,
+       .probe_new = ml26124_i2c_probe,
        .id_table = ml26124_i2c_id,
 };
 
index 5c0536e..d2cf484 100644 (file)
@@ -282,12 +282,9 @@ static const struct snd_soc_dai_ops mt6351_codec_dai_ops = {
        .hw_params = mt6351_codec_dai_hw_params,
 };
 
-#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
-                       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
-                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
-                       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
-                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
-                       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
 
 static struct snd_soc_dai_driver mt6351_dai_driver[] = {
        {
@@ -1448,6 +1445,7 @@ static const struct snd_soc_component_driver mt6351_soc_component_driver = {
        .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets),
        .dapm_routes = mt6351_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes),
+       .endianness = 1,
 };
 
 static int mt6351_codec_driver_probe(struct platform_device *pdev)
index 4c7b5d9..60b209e 100644 (file)
@@ -2340,12 +2340,9 @@ static const struct snd_soc_dai_ops mt6358_codec_dai_ops = {
        .hw_params = mt6358_codec_dai_hw_params,
 };
 
-#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
-                       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
-                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
-                       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
-                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
-                       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
 
 static struct snd_soc_dai_driver mt6358_dai_driver[] = {
        {
@@ -2433,6 +2430,7 @@ static const struct snd_soc_component_driver mt6358_soc_component_driver = {
        .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets),
        .dapm_routes = mt6358_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes),
+       .endianness = 1,
 };
 
 static void mt6358_parse_dt(struct mt6358_priv *priv)
index f8532aa..23709b1 100644 (file)
@@ -2576,12 +2576,9 @@ static const struct snd_soc_dai_ops mt6359_codec_dai_ops = {
        .shutdown = mt6359_codec_dai_shutdown,
 };
 
-#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
-                       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
-                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
-                       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
-                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
-                       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
 
 static struct snd_soc_dai_driver mt6359_dai_driver[] = {
        {
@@ -2739,6 +2736,7 @@ static const struct snd_soc_component_driver mt6359_soc_component_driver = {
        .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets),
        .dapm_routes = mt6359_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes),
+       .endianness = 1,
 };
 
 static int mt6359_parse_dt(struct mt6359_priv *priv)
index 3a88152..ba11555 100644 (file)
@@ -323,6 +323,7 @@ static const struct snd_soc_component_driver mt6660_component_driver = {
        .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
 
        .idle_bias_on = false, /* idle_bias_off = true */
+       .endianness = 1,
 };
 
 static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
@@ -456,8 +457,7 @@ static int _mt6660_read_chip_revision(struct mt6660_chip *chip)
        return 0;
 }
 
-static int mt6660_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
+static int mt6660_i2c_probe(struct i2c_client *client)
 {
        struct mt6660_chip *chip = NULL;
        int ret;
@@ -567,7 +567,7 @@ static struct i2c_driver mt6660_i2c_driver = {
                .of_match_table = of_match_ptr(mt6660_of_id),
                .pm = &mt6660_dev_pm_ops,
        },
-       .probe = mt6660_i2c_probe,
+       .probe_new = mt6660_i2c_probe,
        .remove = mt6660_i2c_remove,
        .id_table = mt6660_i2c_id,
 };
index ace9699..347c715 100644 (file)
@@ -823,8 +823,7 @@ static const struct regmap_config nau8540_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
 };
 
-static int nau8540_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int nau8540_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8540 *nau8540 = dev_get_platdata(dev);
@@ -874,7 +873,7 @@ static struct i2c_driver nau8540_i2c_driver = {
                .name = "nau8540",
                .of_match_table = of_match_ptr(nau8540_of_ids),
        },
-       .probe = nau8540_i2c_probe,
+       .probe_new = nau8540_i2c_probe,
        .id_table = nau8540_i2c_ids,
 };
 module_i2c_driver(nau8540_i2c_driver);
index 13676b5..7b3b1e4 100644 (file)
@@ -869,8 +869,7 @@ static const struct snd_soc_component_driver nau8810_component_driver = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int nau8810_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int nau8810_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8810 *nau8810 = dev_get_platdata(dev);
@@ -916,7 +915,7 @@ static struct i2c_driver nau8810_i2c_driver = {
                .name = "nau8810",
                .of_match_table = of_match_ptr(nau8810_of_match),
        },
-       .probe =    nau8810_i2c_probe,
+       .probe_new = nau8810_i2c_probe,
        .id_table = nau8810_i2c_id,
 };
 
index d67dc27..ce4e7f4 100644 (file)
@@ -1626,8 +1626,7 @@ static int nau8821_setup_irq(struct nau8821 *nau8821)
        return 0;
 }
 
-static int nau8821_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int nau8821_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev);
@@ -1703,7 +1702,7 @@ static struct i2c_driver nau8821_driver = {
                .of_match_table = of_match_ptr(nau8821_of_ids),
                .acpi_match_table = ACPI_PTR(nau8821_acpi_match),
        },
-       .probe = nau8821_i2c_probe,
+       .probe_new = nau8821_i2c_probe,
        .remove = nau8821_i2c_remove,
        .id_table = nau8821_i2c_ids,
 };
index 5812339..66bbd8f 100644 (file)
@@ -1083,8 +1083,7 @@ static const struct regmap_config nau8822_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults),
 };
 
-static int nau8822_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int nau8822_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8822 *nau8822 = dev_get_platdata(dev);
@@ -1141,7 +1140,7 @@ static struct i2c_driver nau8822_i2c_driver = {
                .name = "nau8822",
                .of_match_table = of_match_ptr(nau8822_of_match),
        },
-       .probe =    nau8822_i2c_probe,
+       .probe_new = nau8822_i2c_probe,
        .id_table = nau8822_i2c_id,
 };
 module_i2c_driver(nau8822_i2c_driver);
index d0dd154..2a7c935 100644 (file)
@@ -1910,8 +1910,7 @@ const char *nau8824_components(void)
 }
 EXPORT_SYMBOL_GPL(nau8824_components);
 
-static int nau8824_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int nau8824_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8824 *nau8824 = dev_get_platdata(dev);
@@ -1985,7 +1984,7 @@ static struct i2c_driver nau8824_i2c_driver = {
                .of_match_table = of_match_ptr(nau8824_of_ids),
                .acpi_match_table = ACPI_PTR(nau8824_acpi_match),
        },
-       .probe = nau8824_i2c_probe,
+       .probe_new = nau8824_i2c_probe,
        .id_table = nau8824_i2c_ids,
 };
 module_i2c_driver(nau8824_i2c_driver);
index 7734bc3..20e45a3 100644 (file)
@@ -2613,8 +2613,7 @@ static int nau8825_setup_irq(struct nau8825 *nau8825)
        return 0;
 }
 
-static int nau8825_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int nau8825_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
@@ -2703,7 +2702,7 @@ static struct i2c_driver nau8825_driver = {
                .of_match_table = of_match_ptr(nau8825_of_ids),
                .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
        },
-       .probe = nau8825_i2c_probe,
+       .probe_new = nau8825_i2c_probe,
        .remove = nau8825_i2c_remove,
        .id_table = nau8825_i2c_ids,
 };
index 9eb65f9..20eb04c 100644 (file)
@@ -299,8 +299,7 @@ static const struct i2c_device_id pcm1681_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
 
-static int pcm1681_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int pcm1681_i2c_probe(struct i2c_client *client)
 {
        int ret;
        struct pcm1681_private *priv;
@@ -329,7 +328,7 @@ static struct i2c_driver pcm1681_i2c_driver = {
                .of_match_table = of_match_ptr(pcm1681_dt_ids),
        },
        .id_table       = pcm1681_i2c_id,
-       .probe          = pcm1681_i2c_probe,
+       .probe_new      = pcm1681_i2c_probe,
 };
 
 module_i2c_driver(pcm1681_i2c_driver);
index 7a6be45..1d2f748 100644 (file)
@@ -12,8 +12,7 @@
 
 #include "pcm1789.h"
 
-static int pcm1789_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int pcm1789_i2c_probe(struct i2c_client *client)
 {
        struct regmap *regmap;
        int ret;
@@ -30,7 +29,9 @@ static int pcm1789_i2c_probe(struct i2c_client *client,
 
 static int pcm1789_i2c_remove(struct i2c_client *client)
 {
-       return pcm1789_common_exit(&client->dev);
+       pcm1789_common_exit(&client->dev);
+
+       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -53,7 +54,7 @@ static struct i2c_driver pcm1789_i2c_driver = {
                .of_match_table = of_match_ptr(pcm1789_of_match),
        },
        .id_table       = pcm1789_i2c_ids,
-       .probe          = pcm1789_i2c_probe,
+       .probe_new      = pcm1789_i2c_probe,
        .remove = pcm1789_i2c_remove,
 };
 
index 620dec1..35788b5 100644 (file)
@@ -259,13 +259,11 @@ int pcm1789_common_init(struct device *dev, struct regmap *regmap)
 }
 EXPORT_SYMBOL_GPL(pcm1789_common_init);
 
-int pcm1789_common_exit(struct device *dev)
+void pcm1789_common_exit(struct device *dev)
 {
        struct pcm1789_private *priv = dev_get_drvdata(dev);
 
        flush_work(&priv->work);
-
-       return 0;
 }
 EXPORT_SYMBOL_GPL(pcm1789_common_exit);
 
index c446d78..79439c8 100644 (file)
@@ -12,6 +12,6 @@
 extern const struct regmap_config pcm1789_regmap_config;
 
 int pcm1789_common_init(struct device *dev, struct regmap *regmap);
-int pcm1789_common_exit(struct device *dev);
+void pcm1789_common_exit(struct device *dev);
 
 #endif
index 34a3d59..e20fe3a 100644 (file)
@@ -14,8 +14,7 @@
 
 #include "pcm179x.h"
 
-static int pcm179x_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int pcm179x_i2c_probe(struct i2c_client *client)
 {
        struct regmap *regmap;
        int ret;
@@ -50,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = {
                .of_match_table = of_match_ptr(pcm179x_of_match),
        },
        .id_table       = pcm179x_i2c_ids,
-       .probe          = pcm179x_i2c_probe,
+       .probe_new      = pcm179x_i2c_probe,
 };
 
 module_i2c_driver(pcm179x_i2c_driver);
index f8382b7..932c8d4 100644 (file)
@@ -22,9 +22,18 @@ static const struct of_device_id pcm186x_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, pcm186x_of_match);
 
-static int pcm186x_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static const struct i2c_device_id pcm186x_i2c_id[] = {
+       { "pcm1862", PCM1862 },
+       { "pcm1863", PCM1863 },
+       { "pcm1864", PCM1864 },
+       { "pcm1865", PCM1865 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
+
+static int pcm186x_i2c_probe(struct i2c_client *i2c)
 {
+       const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c);
        const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
        int irq = i2c->irq;
        struct regmap *regmap;
@@ -36,17 +45,8 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c,
        return pcm186x_probe(&i2c->dev, type, irq, regmap);
 }
 
-static const struct i2c_device_id pcm186x_i2c_id[] = {
-       { "pcm1862", PCM1862 },
-       { "pcm1863", PCM1863 },
-       { "pcm1864", PCM1864 },
-       { "pcm1865", PCM1865 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
-
 static struct i2c_driver pcm186x_i2c_driver = {
-       .probe          = pcm186x_i2c_probe,
+       .probe_new      = pcm186x_i2c_probe,
        .id_table       = pcm186x_i2c_id,
        .driver         = {
                .name   = "pcm186x",
index 2c78dcc..fda9d7e 100644 (file)
@@ -534,19 +534,14 @@ static int pcm186x_power_on(struct snd_soc_component *component)
 static int pcm186x_power_off(struct snd_soc_component *component)
 {
        struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
-       int ret;
 
        snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
                            PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
 
        regcache_cache_only(priv->regmap, true);
 
-       ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+       return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
                                     priv->supplies);
-       if (ret)
-               return ret;
-
-       return 0;
 }
 
 static int pcm186x_set_bias_level(struct snd_soc_component *component,
index abcdeb9..2388098 100644 (file)
@@ -10,8 +10,7 @@
 
 #include "pcm3060.h"
 
-static int pcm3060_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int pcm3060_i2c_probe(struct i2c_client *i2c)
 {
        struct pcm3060_priv *priv;
 
@@ -50,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = {
 #endif /* CONFIG_OF */
        },
        .id_table = pcm3060_i2c_id,
-       .probe = pcm3060_i2c_probe,
+       .probe_new = pcm3060_i2c_probe,
 };
 
 module_i2c_driver(pcm3060_i2c_driver);
index 4e3bfb9..586ec8c 100644 (file)
@@ -255,6 +255,7 @@ static const struct snd_soc_component_driver pcm3060_soc_comp_driver = {
        .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets),
        .dapm_routes = pcm3060_dapm_map,
        .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map),
+       .endianness = 1,
 };
 
 /* regmap */
index 1f75933..c0fa0dc 100644 (file)
@@ -15,8 +15,7 @@
 
 #include "pcm3168a.h"
 
-static int pcm3168a_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int pcm3168a_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
 
@@ -47,7 +46,7 @@ static const struct of_device_id pcm3168a_of_match[] = {
 MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
 
 static struct i2c_driver pcm3168a_i2c_driver = {
-       .probe          = pcm3168a_i2c_probe,
+       .probe_new      = pcm3168a_i2c_probe,
        .remove         = pcm3168a_i2c_remove,
        .id_table       = pcm3168a_i2c_id,
        .driver         = {
index 633f7eb..81754e1 100644 (file)
@@ -13,8 +13,7 @@
 
 #include "pcm512x.h"
 
-static int pcm512x_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
        struct regmap_config config = pcm512x_regmap;
@@ -68,7 +67,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match);
 #endif
 
 static struct i2c_driver pcm512x_i2c_driver = {
-       .probe          = pcm512x_i2c_probe,
+       .probe_new      = pcm512x_i2c_probe,
        .remove         = pcm512x_i2c_remove,
        .id_table       = pcm512x_i2c_id,
        .driver         = {
index 758d439..86b679c 100644 (file)
@@ -481,7 +481,7 @@ static int rk3328_platform_probe(struct platform_device *pdev)
        ret = clk_prepare_enable(rk3328->pclk);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to enable acodec pclk\n");
-               return ret;
+               goto err_unprepare_mclk;
        }
 
        base = devm_platform_ioremap_resource(pdev, 0);
index b62301a..08dbaef 100644 (file)
@@ -2433,8 +2433,7 @@ static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
        return 0;
 }
 
-static int rt1011_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt1011_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1011_priv *rt1011;
        int ret;
@@ -2485,7 +2484,7 @@ static struct i2c_driver rt1011_i2c_driver = {
                .of_match_table = of_match_ptr(rt1011_of_match),
                .acpi_match_table = ACPI_PTR(rt1011_acpi_match)
        },
-       .probe = rt1011_i2c_probe,
+       .probe_new = rt1011_i2c_probe,
        .shutdown = rt1011_i2c_shutdown,
        .id_table = rt1011_i2c_id,
 };
index 6a27dfa..7a06f26 100644 (file)
@@ -1113,8 +1113,7 @@ static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev)
                &rt1015->pdata.power_up_delay_ms);
 }
 
-static int rt1015_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int rt1015_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt1015_priv *rt1015;
@@ -1172,7 +1171,7 @@ static struct i2c_driver rt1015_i2c_driver = {
                .of_match_table = of_match_ptr(rt1015_of_match),
                .acpi_match_table = ACPI_PTR(rt1015_acpi_match),
        },
-       .probe = rt1015_i2c_probe,
+       .probe_new = rt1015_i2c_probe,
        .shutdown = rt1015_i2c_shutdown,
        .id_table = rt1015_i2c_id,
 };
index 9845cdd..e31c473 100644 (file)
@@ -631,8 +631,7 @@ static const struct acpi_device_id rt1016_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match);
 #endif
 
-static int rt1016_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int rt1016_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1016_priv *rt1016;
        int ret;
@@ -685,7 +684,7 @@ static struct i2c_driver rt1016_i2c_driver = {
                .of_match_table = of_match_ptr(rt1016_of_match),
                .acpi_match_table = ACPI_PTR(rt1016_acpi_match),
        },
-       .probe = rt1016_i2c_probe,
+       .probe_new = rt1016_i2c_probe,
        .shutdown = rt1016_i2c_shutdown,
        .id_table = rt1016_i2c_id,
 };
index 80b7ca0..f3f15fb 100644 (file)
@@ -515,7 +515,7 @@ static struct snd_soc_dai_driver rt1019_dai[] = {
 };
 
 static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
-       .probe = rt1019_probe,
+       .probe                  = rt1019_probe,
        .controls               = rt1019_snd_controls,
        .num_controls           = ARRAY_SIZE(rt1019_snd_controls),
        .dapm_widgets           = rt1019_dapm_widgets,
@@ -523,6 +523,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
        .dapm_routes            = rt1019_dapm_routes,
        .num_dapm_routes        = ARRAY_SIZE(rt1019_dapm_routes),
        .non_legacy_dai_naming  = 1,
+       .endianness             = 1,
 };
 
 static const struct regmap_config rt1019_regmap = {
@@ -558,8 +559,7 @@ static const struct acpi_device_id rt1019_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match);
 #endif
 
-static int rt1019_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int rt1019_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1019_priv *rt1019;
        int ret;
@@ -599,7 +599,7 @@ static struct i2c_driver rt1019_i2c_driver = {
                .of_match_table = of_match_ptr(rt1019_of_match),
                .acpi_match_table = ACPI_PTR(rt1019_acpi_match),
        },
-       .probe = rt1019_i2c_probe,
+       .probe_new = rt1019_i2c_probe,
        .id_table = rt1019_i2c_id,
 };
 module_i2c_driver(rt1019_i2c_driver);
index a9c4735..58d97e3 100644 (file)
@@ -1117,8 +1117,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305)
        regcache_cache_bypass(rt1305->regmap, false);
 }
 
-static int rt1305_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt1305_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1305_priv *rt1305;
        int ret;
@@ -1172,7 +1171,7 @@ static struct i2c_driver rt1305_i2c_driver = {
                .acpi_match_table = ACPI_PTR(rt1305_acpi_match)
 #endif
        },
-       .probe = rt1305_i2c_probe,
+       .probe_new = rt1305_i2c_probe,
        .shutdown = rt1305_i2c_shutdown,
        .id_table = rt1305_i2c_id,
 };
index 1ef836a..1c11b42 100644 (file)
@@ -615,6 +615,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
        .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
        .dapm_routes = rt1308_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+       .endianness = 1,
 };
 
 static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
index c5ce756..6668e19 100644 (file)
@@ -140,6 +140,7 @@ static const struct reg_default rt1308_reg_defaults[] = {
        { 0x3008, 0x02 },
        { 0x300a, 0x00 },
        { 0xc003 | (RT1308_DAC_SET << 4), 0x00 },
+       { 0xc000 | (RT1308_POWER << 4), 0x00 },
        { 0xc001 | (RT1308_POWER << 4), 0x00 },
        { 0xc002 | (RT1308_POWER << 4), 0x00 },
 };
index c555b77..eec2b17 100644 (file)
@@ -814,8 +814,7 @@ static void rt1308_efuse(struct rt1308_priv *rt1308)
        regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000);
 }
 
-static int rt1308_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt1308_i2c_probe(struct i2c_client *i2c)
 {
        struct rt1308_priv *rt1308;
        int ret;
@@ -864,7 +863,7 @@ static struct i2c_driver rt1308_i2c_driver = {
                .of_match_table = of_match_ptr(rt1308_of_match),
                .acpi_match_table = ACPI_PTR(rt1308_acpi_match),
        },
-       .probe = rt1308_i2c_probe,
+       .probe_new = rt1308_i2c_probe,
        .shutdown = rt1308_i2c_shutdown,
        .id_table = rt1308_i2c_id,
 };
index c66d7b2..60baa9f 100644 (file)
@@ -597,6 +597,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
        .num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets),
        .dapm_routes = rt1316_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes),
+       .endianness = 1,
 };
 
 static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
index 0d3773c..ab093bd 100644 (file)
@@ -1114,8 +1114,7 @@ static const struct acpi_device_id rt274_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
 #endif
 
-static int rt274_i2c_probe(struct i2c_client *i2c,
-                          const struct i2c_device_id *id)
+static int rt274_i2c_probe(struct i2c_client *i2c)
 {
        struct rt274_priv *rt274;
 
@@ -1227,7 +1226,7 @@ static struct i2c_driver rt274_i2c_driver = {
                   .of_match_table = of_match_ptr(rt274_of_match),
 #endif
                   },
-       .probe = rt274_i2c_probe,
+       .probe_new = rt274_i2c_probe,
        .remove = rt274_i2c_remove,
        .id_table = rt274_i2c_id,
 };
index caa7208..ad8ea1f 100644 (file)
@@ -1134,8 +1134,7 @@ static const struct dmi_system_id dmi_dell[] = {
        { }
 };
 
-static int rt286_i2c_probe(struct i2c_client *i2c,
-                          const struct i2c_device_id *id)
+static int rt286_i2c_probe(struct i2c_client *i2c)
 {
        struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt286_priv *rt286;
@@ -1271,7 +1270,7 @@ static struct i2c_driver rt286_i2c_driver = {
                   .name = "rt286",
                   .acpi_match_table = ACPI_PTR(rt286_acpi_match),
                   },
-       .probe = rt286_i2c_probe,
+       .probe_new = rt286_i2c_probe,
        .remove = rt286_i2c_remove,
        .id_table = rt286_i2c_id,
 };
index c592c40..c291786 100644 (file)
@@ -1176,8 +1176,7 @@ static const struct dmi_system_id force_combo_jack_table[] = {
        { }
 };
 
-static int rt298_i2c_probe(struct i2c_client *i2c,
-                          const struct i2c_device_id *id)
+static int rt298_i2c_probe(struct i2c_client *i2c)
 {
        struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt298_priv *rt298;
@@ -1314,7 +1313,7 @@ static struct i2c_driver rt298_i2c_driver = {
                   .name = "rt298",
                   .acpi_match_table = ACPI_PTR(rt298_acpi_match),
                   },
-       .probe = rt298_i2c_probe,
+       .probe_new = rt298_i2c_probe,
        .remove = rt298_i2c_remove,
        .id_table = rt298_i2c_id,
 };
index 577680d..be8ece4 100644 (file)
@@ -419,7 +419,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
                }
        }
 
-       return 0;
+       return 1;
 }
 
 static const struct snd_kcontrol_new rt5514_snd_controls[] = {
@@ -1252,8 +1252,7 @@ static __maybe_unused int rt5514_i2c_resume(struct device *dev)
        return 0;
 }
 
-static int rt5514_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5514_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5514_priv *rt5514;
@@ -1330,7 +1329,7 @@ static struct i2c_driver rt5514_i2c_driver = {
                .of_match_table = of_match_ptr(rt5514_of_match),
                .pm = &rt5514_i2_pm_ops,
        },
-       .probe = rt5514_i2c_probe,
+       .probe_new = rt5514_i2c_probe,
        .id_table = rt5514_i2c_id,
 };
 module_i2c_driver(rt5514_i2c_driver);
index 8e64144..37f1bf5 100644 (file)
@@ -1337,8 +1337,7 @@ static const struct of_device_id rt5616_of_match[] = {
 MODULE_DEVICE_TABLE(of, rt5616_of_match);
 #endif
 
-static int rt5616_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int rt5616_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5616_priv *rt5616;
        unsigned int val;
@@ -1408,7 +1407,7 @@ static struct i2c_driver rt5616_i2c_driver = {
                .name = "rt5616",
                .of_match_table = of_match_ptr(rt5616_of_match),
        },
-       .probe = rt5616_i2c_probe,
+       .probe_new = rt5616_i2c_probe,
        .remove = rt5616_i2c_remove,
        .shutdown = rt5616_i2c_shutdown,
        .id_table = rt5616_i2c_id,
index 38356ea..c941e87 100644 (file)
@@ -1699,8 +1699,7 @@ static const struct regmap_config rt5631_regmap_config = {
        .use_single_write = true,
 };
 
-static int rt5631_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5631_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5631_priv *rt5631;
        int ret;
@@ -1732,7 +1731,7 @@ static struct i2c_driver rt5631_i2c_driver = {
                .name = "rt5631",
                .of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
        },
-       .probe = rt5631_i2c_probe,
+       .probe_new = rt5631_i2c_probe,
        .remove   = rt5631_i2c_remove,
        .id_table = rt5631_i2c_id,
 };
index 30c2e7c..12da2be 100644 (file)
@@ -2927,8 +2927,7 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
        return 0;
 }
 
-static int rt5640_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5640_priv *rt5640;
        int ret;
@@ -3006,7 +3005,7 @@ static struct i2c_driver rt5640_i2c_driver = {
                .acpi_match_table = ACPI_PTR(rt5640_acpi_match),
                .of_match_table = of_match_ptr(rt5640_of_match),
        },
-       .probe = rt5640_i2c_probe,
+       .probe_new = rt5640_i2c_probe,
        .id_table = rt5640_i2c_id,
 };
 module_i2c_driver(rt5640_i2c_driver);
index 197c560..507aba8 100644 (file)
@@ -3855,8 +3855,7 @@ static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
        return 0;
 }
 
-static int rt5645_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5645_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5645_platform_data *pdata = NULL;
        const struct dmi_system_id *dmi_data;
@@ -3944,7 +3943,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
                ret = PTR_ERR(regmap);
                dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
                        ret);
-               return ret;
+               goto err_enable;
        }
 
        /*
@@ -3975,7 +3974,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
                ret = PTR_ERR(rt5645->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
                        ret);
-               return ret;
+               goto err_enable;
        }
 
        regmap_write(rt5645->regmap, RT5645_RESET, 0);
@@ -4154,9 +4153,14 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
        if (i2c->irq)
                free_irq(i2c->irq, rt5645);
 
+       /*
+        * Since the rt5645_btn_check_callback() can queue jack_detect_work,
+        * the timer need to be delted first
+        */
+       del_timer_sync(&rt5645->btn_check_timer);
+
        cancel_delayed_work_sync(&rt5645->jack_detect_work);
        cancel_delayed_work_sync(&rt5645->rcclock_work);
-       del_timer_sync(&rt5645->btn_check_timer);
 
        regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
 
@@ -4183,7 +4187,7 @@ static struct i2c_driver rt5645_i2c_driver = {
                .of_match_table = of_match_ptr(rt5645_of_match),
                .acpi_match_table = ACPI_PTR(rt5645_acpi_match),
        },
-       .probe = rt5645_i2c_probe,
+       .probe_new = rt5645_i2c_probe,
        .remove = rt5645_i2c_remove,
        .shutdown = rt5645_i2c_shutdown,
        .id_table = rt5645_i2c_id,
index f302c25..d11d201 100644 (file)
@@ -2209,8 +2209,7 @@ MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
  * Note this function MUST not look at device-properties, see the comment
  * above rt5651_apply_properties().
  */
-static int rt5651_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5651_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5651_priv *rt5651;
        int ret;
@@ -2281,7 +2280,7 @@ static struct i2c_driver rt5651_i2c_driver = {
                .acpi_match_table = ACPI_PTR(rt5651_acpi_match),
                .of_match_table = of_match_ptr(rt5651_of_match),
        },
-       .probe = rt5651_i2c_probe,
+       .probe_new = rt5651_i2c_probe,
        .id_table = rt5651_i2c_id,
 };
 module_i2c_driver(rt5651_i2c_driver);
index e1503c2..6efa90f 100644 (file)
@@ -4093,8 +4093,7 @@ static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659)
                RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
 }
 
-static int rt5659_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5659_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5659_priv *rt5659;
@@ -4342,7 +4341,7 @@ static struct i2c_driver rt5659_i2c_driver = {
                .of_match_table = of_match_ptr(rt5659_of_match),
                .acpi_match_table = ACPI_PTR(rt5659_acpi_match),
        },
-       .probe = rt5659_i2c_probe,
+       .probe_new = rt5659_i2c_probe,
        .shutdown = rt5659_i2c_shutdown,
        .id_table = rt5659_i2c_id,
 };
index 3b50fb2..d5f9926 100644 (file)
@@ -1266,8 +1266,7 @@ static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev)
        return 0;
 }
 
-static int rt5660_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5660_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5660_priv *rt5660;
@@ -1343,7 +1342,7 @@ static struct i2c_driver rt5660_i2c_driver = {
                .acpi_match_table = ACPI_PTR(rt5660_acpi_match),
                .of_match_table = of_match_ptr(rt5660_of_match),
        },
-       .probe = rt5660_i2c_probe,
+       .probe_new = rt5660_i2c_probe,
        .id_table = rt5660_i2c_id,
 };
 module_i2c_driver(rt5660_i2c_driver);
index 3a8fba1..e51eed8 100644 (file)
@@ -3490,8 +3490,7 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
        return 0;
 }
 
-static int rt5663_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5663_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5663_priv *rt5663;
@@ -3737,7 +3736,7 @@ static struct i2c_driver rt5663_i2c_driver = {
                .acpi_match_table = ACPI_PTR(rt5663_acpi_match),
                .of_match_table = of_match_ptr(rt5663_of_match),
        },
-       .probe = rt5663_i2c_probe,
+       .probe_new = rt5663_i2c_probe,
        .remove = rt5663_i2c_remove,
        .shutdown = rt5663_i2c_shutdown,
        .id_table = rt5663_i2c_id,
index 33e8898..4a8d62e 100644 (file)
@@ -4757,8 +4757,7 @@ static void rt5665_calibrate_handler(struct work_struct *work)
        rt5665_calibrate(rt5665);
 }
 
-static int rt5665_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5665_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5665_priv *rt5665;
@@ -4970,7 +4969,7 @@ static struct i2c_driver rt5665_i2c_driver = {
                .of_match_table = of_match_ptr(rt5665_of_match),
                .acpi_match_table = ACPI_PTR(rt5665_acpi_match),
        },
-       .probe = rt5665_i2c_probe,
+       .probe_new = rt5665_i2c_probe,
        .shutdown = rt5665_i2c_shutdown,
        .id_table = rt5665_i2c_id,
 };
index 5b12cbf..01566f0 100644 (file)
@@ -2453,8 +2453,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668)
 
 }
 
-static int rt5668_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5668_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5668_priv *rt5668;
@@ -2620,7 +2619,7 @@ static struct i2c_driver rt5668_i2c_driver = {
                .of_match_table = of_match_ptr(rt5668_of_match),
                .acpi_match_table = ACPI_PTR(rt5668_acpi_match),
        },
-       .probe = rt5668_i2c_probe,
+       .probe_new = rt5668_i2c_probe,
        .shutdown = rt5668_i2c_shutdown,
        .id_table = rt5668_i2c_id,
 };
index ce76847..8a97f6d 100644 (file)
@@ -3046,8 +3046,7 @@ const char *rt5670_components(void)
 }
 EXPORT_SYMBOL_GPL(rt5670_components);
 
-static int rt5670_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+static int rt5670_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5670_priv *rt5670;
        int ret;
@@ -3334,7 +3333,7 @@ static struct i2c_driver rt5670_i2c_driver = {
                .name = "rt5670",
                .acpi_match_table = ACPI_PTR(rt5670_acpi_match),
        },
-       .probe = rt5670_i2c_probe,
+       .probe_new = rt5670_i2c_probe,
        .remove   = rt5670_i2c_remove,
        .id_table = rt5670_i2c_id,
 };
index 20fc0f3..3f72f60 100644 (file)
@@ -118,8 +118,7 @@ static void rt5682_i2c_disable_regulators(void *data)
        regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies);
 }
 
-static int rt5682_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int rt5682_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5682_priv *rt5682;
@@ -335,7 +334,7 @@ static struct i2c_driver rt5682_i2c_driver = {
                .acpi_match_table = rt5682_acpi_match,
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        },
-       .probe = rt5682_i2c_probe,
+       .probe_new = rt5682_i2c_probe,
        .remove = rt5682_i2c_remove,
        .shutdown = rt5682_i2c_shutdown,
        .id_table = rt5682_i2c_id,
index b55f3ac..4d44edd 100644 (file)
@@ -42,8 +42,8 @@ static const struct rt5682s_platform_data i2s_default_platform_data = {
 };
 
 static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = {
-       "AVDD",
-       "MICVDD",
+       [RT5682S_SUPPLY_AVDD] = "AVDD",
+       [RT5682S_SUPPLY_MICVDD] = "MICVDD",
 };
 
 static const struct reg_sequence patch_list[] = {
@@ -3022,12 +3022,21 @@ static struct snd_soc_dai_driver rt5682s_dai[] = {
 static void rt5682s_i2c_disable_regulators(void *data)
 {
        struct rt5682s_priv *rt5682s = data;
+       struct device *dev = regmap_get_device(rt5682s->regmap);
+       int ret;
+
+       ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+       if (ret)
+               dev_err(dev, "Failed to disable supply AVDD: %d\n", ret);
 
-       regulator_bulk_disable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+       usleep_range(1000, 1500);
+
+       ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+       if (ret)
+               dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret);
 }
 
-static int rt5682s_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int rt5682s_i2c_probe(struct i2c_client *i2c)
 {
        struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt5682s_priv *rt5682s;
@@ -3068,9 +3077,16 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c,
        if (ret)
                return ret;
 
-       ret = regulator_bulk_enable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+       ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+       if (ret) {
+               dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret);
+               return ret;
+       }
+       usleep_range(1000, 1500);
+
+       ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
        if (ret) {
-               dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+               dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret);
                return ret;
        }
 
@@ -3211,7 +3227,7 @@ static struct i2c_driver rt5682s_i2c_driver = {
                .acpi_match_table = rt5682s_acpi_match,
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        },
-       .probe = rt5682s_i2c_probe,
+       .probe_new = rt5682s_i2c_probe,
        .remove = rt5682s_i2c_remove,
        .shutdown = rt5682s_i2c_shutdown,
        .id_table = rt5682s_i2c_id,
index 397a253..7353831 100644 (file)
@@ -1434,7 +1434,11 @@ struct pll_calc_map {
        bool sel_ps;
 };
 
-#define RT5682S_NUM_SUPPLIES 2
+enum {
+       RT5682S_SUPPLY_AVDD,
+       RT5682S_SUPPLY_MICVDD,
+       RT5682S_NUM_SUPPLIES,
+};
 
 struct rt5682s_priv {
        struct snd_soc_component *component;
index e61a825..af32295 100644 (file)
@@ -853,6 +853,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
        .dapm_routes = rt700_audio_map,
        .num_dapm_routes = ARRAY_SIZE(rt700_audio_map),
        .set_jack = rt700_set_jack_detect,
+       .endianness = 1,
 };
 
 static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index bdb1375..57629c1 100644 (file)
@@ -1208,6 +1208,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
        .num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
        .set_jack = rt711_sdca_set_jack_detect,
        .remove = rt711_sdca_remove,
+       .endianness = 1,
 };
 
 static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index ea25fd5..9838fb4 100644 (file)
@@ -950,6 +950,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
        .num_dapm_routes = ARRAY_SIZE(rt711_audio_map),
        .set_jack = rt711_set_jack_detect,
        .remove = rt711_remove,
+       .endianness = 1,
 };
 
 static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index a5c673f..0ecd294 100644 (file)
@@ -181,8 +181,6 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
 {
        struct regmap *mbq_regmap, *regmap;
 
-       slave->ops = &rt715_sdca_slave_ops;
-
        /* Regmap Initialization */
        mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
        if (IS_ERR(mbq_regmap))
index bfa536b..5857d08 100644 (file)
@@ -765,6 +765,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
        .num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets),
        .dapm_routes = rt715_sdca_audio_map,
        .num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map),
+       .endianness = 1,
 };
 
 static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index a64d11a..418e006 100644 (file)
@@ -745,6 +745,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715 = {
        .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
        .dapm_routes = rt715_audio_map,
        .num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
+       .endianness = 1,
 };
 
 static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index 6e0d7cf..da495bd 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <sound/pcm.h>
@@ -75,6 +76,7 @@ enum {
 struct rt9120_data {
        struct device *dev;
        struct regmap *regmap;
+       struct gpio_desc *pwdnn_gpio;
        int chip_idx;
 };
 
@@ -160,6 +162,8 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
 
        snd_soc_component_init_regmap(comp, data->regmap);
 
+       pm_runtime_get_sync(comp->dev);
+
        /* Internal setting */
        if (data->chip_idx == CHIP_IDX_RT9120S) {
                snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde);
@@ -167,6 +171,9 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
        } else
                snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04);
 
+       pm_runtime_mark_last_busy(comp->dev);
+       pm_runtime_put(comp->dev);
+
        return 0;
 }
 
@@ -178,6 +185,7 @@ static const struct snd_soc_component_driver rt9120_component_driver = {
        .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets),
        .dapm_routes = rt9120_dapm_routes,
        .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes),
+       .endianness = 1,
 };
 
 static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -337,6 +345,18 @@ static const struct regmap_access_table rt9120_wr_table = {
        .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges),
 };
 
+static bool rt9120_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x00 ... 0x01:
+       case 0x10:
+       case 0x30 ... 0x40:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int rt9120_get_reg_size(unsigned int reg)
 {
        switch (reg) {
@@ -371,7 +391,7 @@ static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val)
                *val = be32_to_cpup((__be32 *)raw);
                break;
        case 3:
-               *val = raw[0] << 16 | raw[1] << 8 | raw[0];
+               *val = raw[0] << 16 | raw[1] << 8 | raw[2];
                break;
        case 2:
                *val = be16_to_cpup((__be16 *)raw);
@@ -396,14 +416,49 @@ static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
        return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs);
 }
 
+static const struct reg_default rt9120_reg_defaults[] = {
+       { .reg = 0x02, .def = 0x02 },
+       { .reg = 0x03, .def = 0xf2 },
+       { .reg = 0x04, .def = 0x01 },
+       { .reg = 0x05, .def = 0xc0 },
+       { .reg = 0x06, .def = 0x28 },
+       { .reg = 0x07, .def = 0x04 },
+       { .reg = 0x08, .def = 0xff },
+       { .reg = 0x09, .def = 0x01 },
+       { .reg = 0x0a, .def = 0x01 },
+       { .reg = 0x0b, .def = 0x00 },
+       { .reg = 0x0c, .def = 0x04 },
+       { .reg = 0x11, .def = 0x30 },
+       { .reg = 0x12, .def = 0x08 },
+       { .reg = 0x13, .def = 0x12 },
+       { .reg = 0x14, .def = 0x09 },
+       { .reg = 0x15, .def = 0x00 },
+       { .reg = 0x20, .def = 0x7ff },
+       { .reg = 0x21, .def = 0x180 },
+       { .reg = 0x22, .def = 0x180 },
+       { .reg = 0x23, .def = 0x00 },
+       { .reg = 0x24, .def = 0x80 },
+       { .reg = 0x25, .def = 0x180 },
+       { .reg = 0x26, .def = 0x640 },
+       { .reg = 0x27, .def = 0x180 },
+       { .reg = 0x63, .def = 0x5e },
+       { .reg = 0x65, .def = 0x66 },
+       { .reg = 0x6c, .def = 0xe0 },
+       { .reg = 0xf8, .def = 0x44 },
+};
+
 static const struct regmap_config rt9120_regmap_config = {
        .reg_bits = 8,
        .val_bits = 32,
        .max_register = RT9120_REG_DIGCFG,
+       .reg_defaults = rt9120_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
 
        .reg_read = rt9120_reg_read,
        .reg_write = rt9120_reg_write,
 
+       .volatile_reg = rt9120_volatile_reg,
        .wr_table = &rt9120_wr_table,
        .rd_table = &rt9120_rd_table,
 };
@@ -449,7 +504,6 @@ static int rt9120_do_register_reset(struct rt9120_data *data)
 static int rt9120_probe(struct i2c_client *i2c)
 {
        struct rt9120_data *data;
-       struct gpio_desc *pwdnn_gpio;
        struct regulator *dvdd_supply;
        int dvdd_supply_volt, ret;
 
@@ -460,12 +514,12 @@ static int rt9120_probe(struct i2c_client *i2c)
        data->dev = &i2c->dev;
        i2c_set_clientdata(i2c, data);
 
-       pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
-                                            GPIOD_OUT_HIGH);
-       if (IS_ERR(pwdnn_gpio)) {
+       data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
+                                                  GPIOD_OUT_HIGH);
+       if (IS_ERR(data->pwdnn_gpio)) {
                dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n");
-               return PTR_ERR(pwdnn_gpio);
-       } else if (pwdnn_gpio) {
+               return PTR_ERR(data->pwdnn_gpio);
+       } else if (data->pwdnn_gpio) {
                dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n");
                msleep(RT9120_CHIPON_WAITMS);
        }
@@ -507,11 +561,55 @@ static int rt9120_probe(struct i2c_client *i2c)
                }
        }
 
+       pm_runtime_set_autosuspend_delay(&i2c->dev, 1000);
+       pm_runtime_use_autosuspend(&i2c->dev);
+       pm_runtime_set_active(&i2c->dev);
+       pm_runtime_mark_last_busy(&i2c->dev);
+       pm_runtime_enable(&i2c->dev);
+
        return devm_snd_soc_register_component(&i2c->dev,
                                               &rt9120_component_driver,
                                               &rt9120_dai, 1);
 }
 
+static int rt9120_remove(struct i2c_client *i2c)
+{
+       pm_runtime_disable(&i2c->dev);
+       pm_runtime_set_suspended(&i2c->dev);
+       return 0;
+}
+
+static int __maybe_unused rt9120_runtime_suspend(struct device *dev)
+{
+       struct rt9120_data *data = dev_get_drvdata(dev);
+
+       if (data->pwdnn_gpio) {
+               regcache_cache_only(data->regmap, true);
+               regcache_mark_dirty(data->regmap);
+               gpiod_set_value(data->pwdnn_gpio, 0);
+       }
+
+       return 0;
+}
+
+static int __maybe_unused rt9120_runtime_resume(struct device *dev)
+{
+       struct rt9120_data *data = dev_get_drvdata(dev);
+
+       if (data->pwdnn_gpio) {
+               gpiod_set_value(data->pwdnn_gpio, 1);
+               msleep(RT9120_CHIPON_WAITMS);
+               regcache_cache_only(data->regmap, false);
+               regcache_sync(data->regmap);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops rt9120_pm_ops = {
+       SET_RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL)
+};
+
 static const struct of_device_id __maybe_unused rt9120_device_table[] = {
        { .compatible = "richtek,rt9120", },
        { }
@@ -522,8 +620,10 @@ static struct i2c_driver rt9120_driver = {
        .driver = {
                .name = "rt9120",
                .of_match_table = rt9120_device_table,
+               .pm = &rt9120_pm_ops,
        },
        .probe_new = rt9120_probe,
+       .remove = rt9120_remove,
 };
 module_i2c_driver(rt9120_driver);
 
index 7c612aa..288b553 100644 (file)
@@ -38,6 +38,7 @@ static void sdw_mockup_component_remove(struct snd_soc_component *component)
 static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
        .probe = sdw_mockup_component_probe,
        .remove = sdw_mockup_component_remove,
+       .endianness = 1,
 };
 
 static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
index 8eebf27..2aa48ae 100644 (file)
@@ -1579,8 +1579,7 @@ static void sgtl5000_fill_defaults(struct i2c_client *client)
        }
 }
 
-static int sgtl5000_i2c_probe(struct i2c_client *client,
-                             const struct i2c_device_id *id)
+static int sgtl5000_i2c_probe(struct i2c_client *client)
 {
        struct sgtl5000_priv *sgtl5000;
        int ret, reg, rev;
@@ -1821,7 +1820,7 @@ static struct i2c_driver sgtl5000_i2c_driver = {
                .name = "sgtl5000",
                .of_match_table = sgtl5000_dt_ids,
        },
-       .probe = sgtl5000_i2c_probe,
+       .probe_new = sgtl5000_i2c_probe,
        .remove = sgtl5000_i2c_remove,
        .id_table = sgtl5000_id,
 };
index 09449c6..83acbdb 100644 (file)
@@ -735,8 +735,7 @@ static const struct regmap_config ssm2518_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
 };
 
-static int ssm2518_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int ssm2518_i2c_probe(struct i2c_client *i2c)
 {
        struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
        struct ssm2518 *ssm2518;
@@ -815,7 +814,7 @@ static struct i2c_driver ssm2518_driver = {
                .name = "ssm2518",
                .of_match_table = of_match_ptr(ssm2518_dt_ids),
        },
-       .probe = ssm2518_i2c_probe,
+       .probe_new = ssm2518_i2c_probe,
        .id_table = ssm2518_i2c_ids,
 };
 module_i2c_driver(ssm2518_driver);
index afab813..3c85772 100644 (file)
 
 #include "ssm2602.h"
 
+static const struct i2c_device_id ssm2602_i2c_id[];
+
 /*
  * ssm2602 2 wire address is determined by GPIO5
  * state during powerup.
  *    low  = 0x1a
  *    high = 0x1b
  */
-static int ssm2602_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int ssm2602_i2c_probe(struct i2c_client *client)
 {
+       const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client);
        return ssm2602_probe(&client->dev, id->driver_data,
                devm_regmap_init_i2c(client, &ssm2602_regmap_config));
 }
@@ -47,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = {
                .name = "ssm2602",
                .of_match_table = ssm2602_of_match,
        },
-       .probe = ssm2602_i2c_probe,
+       .probe_new = ssm2602_i2c_probe,
        .id_table = ssm2602_i2c_id,
 };
 module_i2c_driver(ssm2602_i2c_driver);
index 811b1a2..08ced09 100644 (file)
@@ -444,8 +444,7 @@ static const struct regmap_config ssm4567_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
 };
 
-static int ssm4567_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+static int ssm4567_i2c_probe(struct i2c_client *i2c)
 {
        struct ssm4567 *ssm4567;
        int ret;
@@ -502,7 +501,7 @@ static struct i2c_driver ssm4567_driver = {
                .of_match_table = of_match_ptr(ssm4567_of_match),
                .acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
        },
-       .probe = ssm4567_i2c_probe,
+       .probe_new = ssm4567_i2c_probe,
        .id_table = ssm4567_i2c_ids,
 };
 module_i2c_driver(ssm4567_driver);
index 86528b9..8585cbe 100644 (file)
                      SNDRV_PCM_RATE_192000)
 
 #define STA32X_FORMATS \
-       (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-        SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
-        SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE  | \
-        SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_S32_BE)
+       (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S18_3LE | \
+        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+        SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S32_LE)
 
 /* Power-up register defaults */
 static const struct reg_default sta32x_regs[] = {
@@ -1094,8 +1091,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
 }
 #endif
 
-static int sta32x_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int sta32x_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct sta32x_priv *sta32x;
@@ -1175,7 +1171,7 @@ static struct i2c_driver sta32x_i2c_driver = {
                .name = "sta32x",
                .of_match_table = of_match_ptr(st32x_dt_ids),
        },
-       .probe =    sta32x_i2c_probe,
+       .probe_new = sta32x_i2c_probe,
        .id_table = sta32x_i2c_id,
 };
 
index 75d3b06..9189fb3 100644 (file)
                      SNDRV_PCM_RATE_192000)
 
 #define STA350_FORMATS \
-       (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-        SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
-        SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE  | \
-        SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_S32_BE)
+       (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S18_3LE | \
+        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+        SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S32_LE)
 
 /* Power-up register defaults */
 static const struct reg_default sta350_regs[] = {
@@ -1187,8 +1184,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
 }
 #endif
 
-static int sta350_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int sta350_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct sta350_priv *sta350;
@@ -1263,7 +1259,7 @@ static struct i2c_driver sta350_i2c_driver = {
                .name = "sta350",
                .of_match_table = of_match_ptr(st350_dt_ids),
        },
-       .probe =    sta350_i2c_probe,
+       .probe_new = sta350_i2c_probe,
        .remove =   sta350_i2c_remove,
        .id_table = sta350_i2c_id,
 };
index 97b5f34..d90e551 100644 (file)
@@ -337,8 +337,7 @@ static const struct regmap_config sta529_regmap = {
        .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults),
 };
 
-static int sta529_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int sta529_i2c_probe(struct i2c_client *i2c)
 {
        struct sta529 *sta529;
        int ret;
@@ -381,7 +380,7 @@ static struct i2c_driver sta529_i2c_driver = {
                .name = "sta529",
                .of_match_table = sta529_of_match,
        },
-       .probe          = sta529_i2c_probe,
+       .probe_new      = sta529_i2c_probe,
        .id_table       = sta529_i2c_id,
 };
 
index 700baa6..b5c9c61 100644 (file)
@@ -681,8 +681,7 @@ static const struct regmap_config tas2552_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int tas2552_probe(struct i2c_client *client,
-                          const struct i2c_device_id *id)
+static int tas2552_probe(struct i2c_client *client)
 {
        struct device *dev;
        struct tas2552_data *data;
@@ -764,7 +763,7 @@ static struct i2c_driver tas2552_i2c_driver = {
                .of_match_table = of_match_ptr(tas2552_of_match),
                .pm = &tas2552_pm,
        },
-       .probe = tas2552_probe,
+       .probe_new = tas2552_probe,
        .remove = tas2552_i2c_remove,
        .id_table = tas2552_id,
 };
index 1030255..e62a3da 100644 (file)
@@ -754,17 +754,27 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
        return ret;
 }
 
-static int tas2562_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
+static const struct i2c_device_id tas2562_id[] = {
+       { "tas2562", TAS2562 },
+       { "tas2563", TAS2563 },
+       { "tas2564", TAS2564 },
+       { "tas2110", TAS2110 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2562_id);
+
+static int tas2562_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct tas2562_data *data;
        int ret;
+       const struct i2c_device_id *id;
 
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
+       id = i2c_match_id(tas2562_id, client);
        data->client = client;
        data->dev = &client->dev;
        data->model_id = id->driver_data;
@@ -792,15 +802,6 @@ static int tas2562_probe(struct i2c_client *client,
 
 }
 
-static const struct i2c_device_id tas2562_id[] = {
-       { "tas2562", TAS2562 },
-       { "tas2563", TAS2563 },
-       { "tas2564", TAS2564 },
-       { "tas2110", TAS2110 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, tas2562_id);
-
 #ifdef CONFIG_OF
 static const struct of_device_id tas2562_of_match[] = {
        { .compatible = "ti,tas2562", },
@@ -817,7 +818,7 @@ static struct i2c_driver tas2562_i2c_driver = {
                .name = "tas2562",
                .of_match_table = of_match_ptr(tas2562_of_match),
        },
-       .probe = tas2562_probe,
+       .probe_new = tas2562_probe,
        .id_table = tas2562_id,
 };
 
index 9265af4..d395fef 100644 (file)
@@ -621,8 +621,7 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
        return 0;
 }
 
-static int tas2764_i2c_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+static int tas2764_i2c_probe(struct i2c_client *client)
 {
        struct tas2764_priv *tas2764;
        int result;
@@ -678,7 +677,7 @@ static struct i2c_driver tas2764_i2c_driver = {
                .name   = "tas2764",
                .of_match_table = of_match_ptr(tas2764_of_match),
        },
-       .probe      = tas2764_i2c_probe,
+       .probe_new  = tas2764_i2c_probe,
        .id_table   = tas2764_i2c_id,
 };
 module_i2c_driver(tas2764_i2c_driver);
index c5ea3b1..c1dbd97 100644 (file)
@@ -672,8 +672,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
        return 0;
 }
 
-static int tas2770_i2c_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+static int tas2770_i2c_probe(struct i2c_client *client)
 {
        struct tas2770_priv *tas2770;
        int result;
@@ -739,7 +738,7 @@ static struct i2c_driver tas2770_i2c_driver = {
                .name   = "tas2770",
                .of_match_table = of_match_ptr(tas2770_of_match),
        },
-       .probe      = tas2770_i2c_probe,
+       .probe_new  = tas2770_i2c_probe,
        .id_table   = tas2770_i2c_id,
 };
 module_i2c_driver(tas2770_i2c_driver);
index 7831c96..5c0df3c 100644 (file)
@@ -911,8 +911,7 @@ static const struct regmap_config tas5086_regmap = {
        .reg_write              = tas5086_reg_write,
 };
 
-static int tas5086_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int tas5086_i2c_probe(struct i2c_client *i2c)
 {
        struct tas5086_private *priv;
        struct device *dev = &i2c->dev;
@@ -994,7 +993,7 @@ static struct i2c_driver tas5086_i2c_driver = {
                .of_match_table = of_match_ptr(tas5086_dt_ids),
        },
        .id_table       = tas5086_i2c_id,
-       .probe          = tas5086_i2c_probe,
+       .probe_new      = tas5086_i2c_probe,
        .remove         = tas5086_i2c_remove,
 };
 
index a3e6823..7b59966 100644 (file)
@@ -774,9 +774,9 @@ static struct snd_soc_dai_driver tas571x_dai = {
 };
 
 static const struct of_device_id tas571x_of_match[] __maybe_unused;
+static const struct i2c_device_id tas571x_i2c_id[];
 
-static int tas571x_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int tas571x_i2c_probe(struct i2c_client *client)
 {
        struct tas571x_private *priv;
        struct device *dev = &client->dev;
@@ -791,8 +791,11 @@ static int tas571x_i2c_probe(struct i2c_client *client,
        of_id = of_match_device(tas571x_of_match, dev);
        if (of_id)
                priv->chip = of_id->data;
-       else
+       else {
+               const struct i2c_device_id *id =
+                       i2c_match_id(tas571x_i2c_id, client);
                priv->chip = (void *) id->driver_data;
+       }
 
        priv->mclk = devm_clk_get(dev, "mclk");
        if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
@@ -830,7 +833,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
        if (IS_ERR(priv->pdn_gpio)) {
                dev_err(dev, "error requesting pdn_gpio: %ld\n",
                        PTR_ERR(priv->pdn_gpio));
-               return PTR_ERR(priv->pdn_gpio);
+               ret = PTR_ERR(priv->pdn_gpio);
+               goto disable_regs;
        }
 
        priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -838,7 +842,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
        if (IS_ERR(priv->reset_gpio)) {
                dev_err(dev, "error requesting reset_gpio: %ld\n",
                        PTR_ERR(priv->reset_gpio));
-               return PTR_ERR(priv->reset_gpio);
+               ret = PTR_ERR(priv->reset_gpio);
+               goto disable_regs;
        } else if (priv->reset_gpio) {
                /* pulse the active low reset line for ~100us */
                usleep_range(100, 200);
@@ -914,7 +919,7 @@ static struct i2c_driver tas571x_i2c_driver = {
                .name = "tas571x",
                .of_match_table = of_match_ptr(tas571x_of_match),
        },
-       .probe = tas571x_i2c_probe,
+       .probe_new = tas571x_i2c_probe,
        .remove = tas571x_i2c_remove,
        .id_table = tas571x_i2c_id,
 };
index 9ff644d..17034ab 100644 (file)
@@ -633,12 +633,19 @@ static struct snd_soc_dai_driver tas5720_dai[] = {
        },
 };
 
-static int tas5720_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
+static const struct i2c_device_id tas5720_id[] = {
+       { "tas5720", TAS5720 },
+       { "tas5722", TAS5722 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5720_id);
+
+static int tas5720_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct tas5720_data *data;
        const struct regmap_config *regmap_config;
+       const struct i2c_device_id *id;
        int ret;
        int i;
 
@@ -646,6 +653,7 @@ static int tas5720_probe(struct i2c_client *client,
        if (!data)
                return -ENOMEM;
 
+       id = i2c_match_id(tas5720_id, client);
        data->tas5720_client = client;
        data->devtype = id->driver_data;
 
@@ -704,13 +712,6 @@ static int tas5720_probe(struct i2c_client *client,
        return 0;
 }
 
-static const struct i2c_device_id tas5720_id[] = {
-       { "tas5720", TAS5720 },
-       { "tas5722", TAS5722 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, tas5720_id);
-
 #if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id tas5720_of_match[] = {
        { .compatible = "ti,tas5720", },
@@ -725,7 +726,7 @@ static struct i2c_driver tas5720_i2c_driver = {
                .name = "tas5720",
                .of_match_table = of_match_ptr(tas5720_of_match),
        },
-       .probe = tas5720_probe,
+       .probe_new = tas5720_probe,
        .id_table = tas5720_id,
 };
 
index 59543d3..22b5385 100644 (file)
@@ -682,8 +682,7 @@ static const struct of_device_id tas6424_of_ids[] = {
 MODULE_DEVICE_TABLE(of, tas6424_of_ids);
 #endif
 
-static int tas6424_i2c_probe(struct i2c_client *client,
-                            const struct i2c_device_id *id)
+static int tas6424_i2c_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct tas6424_data *tas6424;
@@ -757,7 +756,7 @@ static int tas6424_i2c_probe(struct i2c_client *client,
                                 TAS6424_RESET, TAS6424_RESET);
        if (ret) {
                dev_err(dev, "unable to reset device: %d\n", ret);
-               return ret;
+               goto disable_regs;
        }
 
        INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work);
@@ -766,10 +765,14 @@ static int tas6424_i2c_probe(struct i2c_client *client,
                                     tas6424_dai, ARRAY_SIZE(tas6424_dai));
        if (ret < 0) {
                dev_err(dev, "unable to register codec: %d\n", ret);
-               return ret;
+               goto disable_regs;
        }
 
        return 0;
+
+disable_regs:
+       regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies);
+       return ret;
 }
 
 static int tas6424_i2c_remove(struct i2c_client *client)
@@ -786,10 +789,8 @@ static int tas6424_i2c_remove(struct i2c_client *client)
 
        ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
                                     tas6424->supplies);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(dev, "unable to disable supplies: %d\n", ret);
-               return ret;
-       }
 
        return 0;
 }
@@ -805,7 +806,7 @@ static struct i2c_driver tas6424_i2c_driver = {
                .name = "tas6424",
                .of_match_table = of_match_ptr(tas6424_of_ids),
        },
-       .probe = tas6424_i2c_probe,
+       .probe_new = tas6424_i2c_probe,
        .remove = tas6424_i2c_remove,
        .id_table = tas6424_i2c_ids,
 };
index 83d2200..d964e52 100644 (file)
@@ -571,8 +571,7 @@ static const struct snd_soc_component_driver tda7419_component_driver = {
        .num_dapm_routes        = ARRAY_SIZE(tda7419_dapm_routes),
 };
 
-static int tda7419_probe(struct i2c_client *i2c,
-                        const struct i2c_device_id *id)
+static int tda7419_probe(struct i2c_client *i2c)
 {
        struct tda7419_data *tda7419;
        int i, ret;
@@ -630,7 +629,7 @@ static struct i2c_driver tda7419_driver = {
                .name   = "tda7419",
                .of_match_table = tda7419_of_match,
        },
-       .probe          = tda7419_probe,
+       .probe_new      = tda7419_probe,
        .id_table       = tda7419_i2c_id,
 };
 
index ae18982..82532ad 100644 (file)
@@ -1152,20 +1152,20 @@ static int adc3xxx_hw_params(struct snd_pcm_substream *substream,
                return i;
 
        /* select data word length */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (params_width(params)) {
+       case 16:
                iface_len = ADC3XXX_IFACE_16BITS;
                width = 16;
                break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
+       case 20:
                iface_len = ADC3XXX_IFACE_20BITS;
                width = 20;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
+       case 24:
                iface_len = ADC3XXX_IFACE_24BITS;
                width = 24;
                break;
-       case SNDRV_PCM_FORMAT_S32_LE:
+       case 32:
                iface_len = ADC3XXX_IFACE_32BITS;
                width = 32;
                break;
@@ -1335,13 +1335,21 @@ static const struct snd_soc_component_driver soc_component_dev_adc3xxx = {
        .num_dapm_widgets       = ARRAY_SIZE(adc3xxx_dapm_widgets),
        .dapm_routes            = adc3xxx_intercon,
        .num_dapm_routes        = ARRAY_SIZE(adc3xxx_intercon),
+       .endianness             = 1,
 };
 
-static int adc3xxx_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static const struct i2c_device_id adc3xxx_i2c_id[] = {
+       { "tlv320adc3001", ADC3001 },
+       { "tlv320adc3101", ADC3101 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
+
+static int adc3xxx_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct adc3xxx *adc3xxx = NULL;
+       const struct i2c_device_id *id;
        int ret;
 
        adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
@@ -1394,6 +1402,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, adc3xxx);
 
+       id = i2c_match_id(adc3xxx_i2c_id, i2c);
        adc3xxx->type = id->driver_data;
 
        /* Reset codec chip */
@@ -1436,19 +1445,12 @@ static const struct of_device_id tlv320adc3xxx_of_match[] = {
 };
 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,
+       .probe_new = adc3xxx_i2c_probe,
        .remove = adc3xxx_i2c_remove,
        .id_table = adc3xxx_i2c_id,
 };
index 32b120d..b55f0b8 100644 (file)
@@ -1083,8 +1083,14 @@ static const struct of_device_id tlv320adcx140_of_match[] = {
 MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match);
 #endif
 
-static int adcx140_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static void adcx140_disable_regulator(void *arg)
+{
+       struct adcx140_priv *adcx140 = arg;
+
+       regulator_disable(adcx140->supply_areg);
+}
+
+static int adcx140_i2c_probe(struct i2c_client *i2c)
 {
        struct adcx140_priv *adcx140;
        int ret;
@@ -1113,6 +1119,10 @@ static int adcx140_i2c_probe(struct i2c_client *i2c,
                        dev_err(adcx140->dev, "Failed to enable areg\n");
                        return ret;
                }
+
+               ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140);
+               if (ret)
+                       return ret;
        }
 
        adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap);
@@ -1143,7 +1153,7 @@ static struct i2c_driver adcx140_i2c_driver = {
                .name   = "tlv320adcx140-codec",
                .of_match_table = of_match_ptr(tlv320adcx140_of_match),
        },
-       .probe          = adcx140_i2c_probe,
+       .probe_new      = adcx140_i2c_probe,
        .id_table       = adcx140_i2c_id,
 };
 module_i2c_driver(adcx140_i2c_driver);
index dbb8f96..1f97673 100644 (file)
@@ -16,8 +16,7 @@
 
 #include "tlv320aic23.h"
 
-static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
-                                const struct i2c_device_id *i2c_id)
+static int tlv320aic23_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
 
@@ -48,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = {
                   .name = "tlv320aic23-codec",
                   .of_match_table = of_match_ptr(tlv320aic23_of_match),
                   },
-       .probe = tlv320aic23_i2c_probe,
+       .probe_new = tlv320aic23_i2c_probe,
        .id_table = tlv320aic23_id,
 };
 
index 8331dc2..b2e5958 100644 (file)
@@ -1628,11 +1628,24 @@ static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
        }
 }
 
-static int aic31xx_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static const struct i2c_device_id aic31xx_i2c_id[] = {
+       { "tlv320aic310x", AIC3100 },
+       { "tlv320aic311x", AIC3110 },
+       { "tlv320aic3100", AIC3100 },
+       { "tlv320aic3110", AIC3110 },
+       { "tlv320aic3120", AIC3120 },
+       { "tlv320aic3111", AIC3111 },
+       { "tlv320dac3100", DAC3100 },
+       { "tlv320dac3101", DAC3101 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
+
+static int aic31xx_i2c_probe(struct i2c_client *i2c)
 {
        struct aic31xx_priv *aic31xx;
        unsigned int micbias_value = MICBIAS_2_0V;
+       const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c);
        int i, ret;
 
        dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
@@ -1729,26 +1742,13 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
                                ARRAY_SIZE(aic31xx_dai_driver));
 }
 
-static const struct i2c_device_id aic31xx_i2c_id[] = {
-       { "tlv320aic310x", AIC3100 },
-       { "tlv320aic311x", AIC3110 },
-       { "tlv320aic3100", AIC3100 },
-       { "tlv320aic3110", AIC3110 },
-       { "tlv320aic3120", AIC3120 },
-       { "tlv320aic3111", AIC3111 },
-       { "tlv320dac3100", DAC3100 },
-       { "tlv320dac3101", DAC3101 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
-
 static struct i2c_driver aic31xx_i2c_driver = {
        .driver = {
                .name   = "tlv320aic31xx-codec",
                .of_match_table = of_match_ptr(tlv320aic31xx_of_match),
                .acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
        },
-       .probe          = aic31xx_i2c_probe,
+       .probe_new      = aic31xx_i2c_probe,
        .id_table       = aic31xx_i2c_id,
 };
 module_i2c_driver(aic31xx_i2c_driver);
index ed70e3d..0645239 100644 (file)
@@ -17,9 +17,9 @@
 #include "tlv320aic32x4.h"
 
 static const struct of_device_id aic32x4_of_id[];
+static const struct i2c_device_id aic32x4_i2c_id[];
 
-static int aic32x4_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int aic32x4_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
        struct regmap_config config;
@@ -35,7 +35,10 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
 
                oid = of_match_node(aic32x4_of_id, i2c->dev.of_node);
                dev_set_drvdata(&i2c->dev, (void *)oid->data);
-       } else if (id) {
+       } else {
+               const struct i2c_device_id *id;
+
+               id = i2c_match_id(aic32x4_i2c_id, i2c);
                dev_set_drvdata(&i2c->dev, (void *)id->driver_data);
        }
 
@@ -70,7 +73,7 @@ static struct i2c_driver aic32x4_i2c_driver = {
                .name = "tlv320aic32x4",
                .of_match_table = aic32x4_of_id,
        },
-       .probe =    aic32x4_i2c_probe,
+       .probe_new = aic32x4_i2c_probe,
        .remove =   aic32x4_i2c_remove,
        .id_table = aic32x4_i2c_id,
 };
index 2f272bc..7bd9ce0 100644 (file)
 
 #include "tlv320aic3x.h"
 
-static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+static const struct i2c_device_id aic3x_i2c_id[] = {
+       { "tlv320aic3x", AIC3X_MODEL_3X },
+       { "tlv320aic33", AIC3X_MODEL_33 },
+       { "tlv320aic3007", AIC3X_MODEL_3007 },
+       { "tlv320aic3104", AIC3X_MODEL_3104 },
+       { "tlv320aic3106", AIC3X_MODEL_3106 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
+
+static int aic3x_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
        struct regmap_config config;
+       const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c);
 
        config = aic3x_regmap;
        config.reg_bits = 8;
@@ -37,16 +48,6 @@ static int aic3x_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-static const struct i2c_device_id aic3x_i2c_id[] = {
-       { "tlv320aic3x", AIC3X_MODEL_3X },
-       { "tlv320aic33", AIC3X_MODEL_33 },
-       { "tlv320aic3007", AIC3X_MODEL_3007 },
-       { "tlv320aic3104", AIC3X_MODEL_3104 },
-       { "tlv320aic3106", AIC3X_MODEL_3106 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
-
 static const struct of_device_id aic3x_of_id[] = {
        { .compatible = "ti,tlv320aic3x", },
        { .compatible = "ti,tlv320aic33" },
@@ -62,7 +63,7 @@ static struct i2c_driver aic3x_i2c_driver = {
                .name = "tlv320aic3x",
                .of_match_table = aic3x_of_id,
        },
-       .probe = aic3x_i2c_probe,
+       .probe_new = aic3x_i2c_probe,
        .remove = aic3x_i2c_remove,
        .id_table = aic3x_i2c_id,
 };
index 48572d6..66f1d1c 100644 (file)
@@ -1463,8 +1463,7 @@ static struct snd_soc_dai_driver dac33_dai = {
        .ops = &dac33_dai_ops,
 };
 
-static int dac33_i2c_probe(struct i2c_client *client,
-                          const struct i2c_device_id *id)
+static int dac33_i2c_probe(struct i2c_client *client)
 {
        struct tlv320dac33_platform_data *pdata;
        struct tlv320dac33_priv *dac33;
@@ -1566,7 +1565,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = {
        .driver = {
                .name = "tlv320dac33-codec",
        },
-       .probe          = dac33_i2c_probe,
+       .probe_new      = dac33_i2c_probe,
        .remove         = dac33_i2c_remove,
        .id_table       = tlv320dac33_i2c_id,
 };
index e2d7ae6..8c00db3 100644 (file)
@@ -209,13 +209,20 @@ static const struct regmap_config tpa6130a2_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int tpa6130a2_probe(struct i2c_client *client,
-                          const struct i2c_device_id *id)
+static const struct i2c_device_id tpa6130a2_id[] = {
+       { "tpa6130a2", TPA6130A2 },
+       { "tpa6140a2", TPA6140A2 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static int tpa6130a2_probe(struct i2c_client *client)
 {
        struct device *dev;
        struct tpa6130a2_data *data;
        struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
        struct device_node *np = client->dev.of_node;
+       const struct i2c_device_id *id;
        const char *regulator;
        unsigned int version;
        int ret;
@@ -244,6 +251,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, data);
 
+       id = i2c_match_id(tpa6130a2_id, client);
        data->id = id->driver_data;
 
        if (data->power_gpio >= 0) {
@@ -297,13 +305,6 @@ static int tpa6130a2_probe(struct i2c_client *client,
                        &tpa6130a2_component_driver, NULL, 0);
 }
 
-static const struct i2c_device_id tpa6130a2_id[] = {
-       { "tpa6130a2", TPA6130A2 },
-       { "tpa6140a2", TPA6140A2 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
-
 #if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id tpa6130a2_of_match[] = {
        { .compatible = "ti,tpa6130a2", },
@@ -318,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
                .name = "tpa6130a2",
                .of_match_table = of_match_ptr(tpa6130a2_of_match),
        },
-       .probe = tpa6130a2_probe,
+       .probe_new = tpa6130a2_probe,
        .id_table = tpa6130a2_id,
 };
 
index 962f5d4..d8ab081 100644 (file)
@@ -282,8 +282,7 @@ static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e,
        return 0;
 }
 
-static int ts3a227e_i2c_probe(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+static int ts3a227e_i2c_probe(struct i2c_client *i2c)
 {
        struct ts3a227e *ts3a227e;
        struct device *dev = &i2c->dev;
@@ -389,7 +388,7 @@ static struct i2c_driver ts3a227e_driver = {
                .of_match_table = of_match_ptr(ts3a227e_of_match),
                .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match),
        },
-       .probe = ts3a227e_i2c_probe,
+       .probe_new = ts3a227e_i2c_probe,
        .id_table = ts3a227e_i2c_ids,
 };
 module_i2c_driver(ts3a227e_driver);
index 5b63e01..4fb0bb0 100644 (file)
@@ -1409,8 +1409,7 @@ static const struct reg_sequence tscs42xx_patch[] = {
 static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = {
        "xtal", "mclk1", "mclk2"};
 
-static int tscs42xx_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int tscs42xx_i2c_probe(struct i2c_client *i2c)
 {
        struct tscs42xx *tscs42xx;
        int src;
@@ -1505,7 +1504,7 @@ static struct i2c_driver tscs42xx_i2c_driver = {
                .name = "tscs42xx",
                .of_match_table = tscs42xx_of_match,
        },
-       .probe =    tscs42xx_i2c_probe,
+       .probe_new = tscs42xx_i2c_probe,
        .id_table = tscs42xx_i2c_id,
 };
 
index 7e1826d..38622bc 100644 (file)
@@ -3120,18 +3120,17 @@ static int set_aif_sample_format(struct snd_soc_component *component,
        unsigned int width;
        int ret;
 
-       switch (format) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (snd_pcm_format_width(format)) {
+       case 16:
                width = FV_WL_16;
                break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
+       case 20:
                width = FV_WL_20;
                break;
-       case SNDRV_PCM_FORMAT_S24_3LE:
+       case 24:
                width = FV_WL_24;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-       case SNDRV_PCM_FORMAT_S32_LE:
+       case 32:
                width = FV_WL_32;
                break;
        default:
@@ -3326,6 +3325,7 @@ static const struct snd_soc_component_driver soc_component_dev_tscs454 = {
        .num_dapm_routes = ARRAY_SIZE(tscs454_intercon),
        .controls =     tscs454_snd_controls,
        .num_controls = ARRAY_SIZE(tscs454_snd_controls),
+       .endianness = 1,
 };
 
 #define TSCS454_RATES SNDRV_PCM_RATE_8000_96000
@@ -3400,8 +3400,7 @@ static struct snd_soc_dai_driver tscs454_dais[] = {
 static char const * const src_names[] = {
        "xtal", "mclk1", "mclk2", "bclk"};
 
-static int tscs454_i2c_probe(struct i2c_client *i2c,
-               const struct i2c_device_id *id)
+static int tscs454_i2c_probe(struct i2c_client *i2c)
 {
        struct tscs454 *tscs454;
        int src;
@@ -3474,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = {
                .name = "tscs454",
                .of_match_table = tscs454_of_match,
        },
-       .probe =    tscs454_i2c_probe,
+       .probe_new = tscs454_i2c_probe,
        .id_table = tscs454_i2c_id,
 };
 
index 13060a9..b500484 100644 (file)
@@ -739,8 +739,7 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int uda1380_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+static int uda1380_i2c_probe(struct i2c_client *i2c)
 {
        struct uda1380_platform_data *pdata = i2c->dev.platform_data;
        struct uda1380_priv *uda1380;
@@ -800,7 +799,7 @@ static struct i2c_driver uda1380_i2c_driver = {
                .name =  "uda1380-codec",
                .of_match_table = uda1380_of_match,
        },
-       .probe =    uda1380_i2c_probe,
+       .probe_new = uda1380_i2c_probe,
        .id_table = uda1380_i2c_id,
 };
 
index 1e60db4..617a36a 100644 (file)
@@ -4924,6 +4924,7 @@ static const struct snd_soc_component_driver wcd9335_component_drv = {
        .num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets),
        .dapm_routes = wcd9335_audio_map,
        .num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map),
+       .endianness = 1,
 };
 
 static int wcd9335_probe(struct wcd9335_codec *wcd)
index 6298ebe..f56907d 100644 (file)
@@ -5847,6 +5847,7 @@ static const struct snd_soc_component_driver wcd934x_component_drv = {
        .dapm_routes = wcd934x_audio_map,
        .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map),
        .set_jack = wcd934x_codec_set_jack,
+       .endianness = 1,
 };
 
 static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
index 898b288..c1b61b9 100644 (file)
@@ -4168,6 +4168,7 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
        .dapm_routes = wcd938x_audio_map,
        .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map),
        .set_jack = wcd938x_codec_set_jack,
+       .endianness = 1,
 };
 
 static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd)
index d6ffe99..b6366de 100644 (file)
@@ -192,8 +192,7 @@ static void wm1250_ev1_free(struct i2c_client *i2c)
                gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
 }
 
-static int wm1250_ev1_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *i2c_id)
+static int wm1250_ev1_probe(struct i2c_client *i2c)
 {
        int id, board, rev, ret;
 
@@ -247,7 +246,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
        .driver = {
                .name = "wm1250-ev1",
        },
-       .probe =    wm1250_ev1_probe,
+       .probe_new = wm1250_ev1_probe,
        .remove =   wm1250_ev1_remove,
        .id_table = wm1250_ev1_i2c_id,
 };
index 72e165c..ede5f2a 100644 (file)
@@ -536,7 +536,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
 {
        struct i2c_client *i2c = wm2000->i2c;
        int i, j;
-       int ret;
+       int ret = 0;
 
        if (wm2000->anc_mode == mode)
                return 0;
@@ -566,13 +566,13 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
                ret = anc_transitions[i].step[j](i2c,
                                                 anc_transitions[i].analogue);
                if (ret != 0)
-                       return ret;
+                       break;
        }
 
        if (anc_transitions[i].dest == ANC_OFF)
                clk_disable_unprepare(wm2000->mclk);
 
-       return 0;
+       return ret;
 }
 
 static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
@@ -803,12 +803,10 @@ static const struct snd_soc_component_driver soc_component_dev_wm2000 = {
        .num_dapm_routes        = ARRAY_SIZE(wm2000_audio_map),
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
-       .endianness             = 1,
        .non_legacy_dai_naming  = 1,
 };
 
-static int wm2000_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *i2c_id)
+static int wm2000_i2c_probe(struct i2c_client *i2c)
 {
        struct wm2000_priv *wm2000;
        struct wm2000_platform_data *pdata;
@@ -941,7 +939,7 @@ static struct i2c_driver wm2000_i2c_driver = {
        .driver = {
                .name = "wm2000",
        },
-       .probe = wm2000_i2c_probe,
+       .probe_new = wm2000_i2c_probe,
        .id_table = wm2000_i2c_id,
 };
 
index 8863b53..1cd5445 100644 (file)
@@ -2176,8 +2176,7 @@ static const unsigned int wm2200_mic_ctrl_reg[] = {
        WM2200_IN3L_CONTROL,
 };
 
-static int wm2200_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm2200_i2c_probe(struct i2c_client *i2c)
 {
        struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev);
        struct wm2200_priv *wm2200;
@@ -2489,7 +2488,7 @@ static struct i2c_driver wm2200_i2c_driver = {
                .name = "wm2200",
                .pm = &wm2200_pm,
        },
-       .probe =    wm2200_i2c_probe,
+       .probe_new = wm2200_i2c_probe,
        .remove =   wm2200_i2c_remove,
        .id_table = wm2200_i2c_id,
 };
index 9cab01e..a898709 100644 (file)
@@ -2411,8 +2411,7 @@ static const unsigned int wm5100_mic_ctrl_reg[] = {
        WM5100_IN4L_CONTROL,
 };
 
-static int wm5100_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm5100_i2c_probe(struct i2c_client *i2c)
 {
        struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev);
        struct wm5100_priv *wm5100;
@@ -2713,7 +2712,7 @@ static struct i2c_driver wm5100_i2c_driver = {
                .name = "wm5100",
                .pm = &wm5100_pm,
        },
-       .probe =    wm5100_i2c_probe,
+       .probe_new = wm5100_i2c_probe,
        .remove =   wm5100_i2c_remove,
        .id_table = wm5100_i2c_id,
 };
index a18e229..c6439d2 100644 (file)
@@ -646,8 +646,7 @@ static struct spi_driver wm8510_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8510_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8510_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8510_priv *wm8510;
        int ret;
@@ -680,7 +679,7 @@ static struct i2c_driver wm8510_i2c_driver = {
                .name = "wm8510",
                .of_match_table = wm8510_of_match,
        },
-       .probe =    wm8510_i2c_probe,
+       .probe_new = wm8510_i2c_probe,
        .id_table = wm8510_i2c_id,
 };
 #endif
index c8b50aa..ba35a02 100644 (file)
@@ -443,8 +443,7 @@ static const struct regmap_config wm8523_regmap = {
        .volatile_reg = wm8523_volatile_register,
 };
 
-static int wm8523_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8523_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8523_priv *wm8523;
        unsigned int val;
@@ -529,7 +528,7 @@ static struct i2c_driver wm8523_i2c_driver = {
                .name = "wm8523",
                .of_match_table = wm8523_of_match,
        },
-       .probe =    wm8523_i2c_probe,
+       .probe_new = wm8523_i2c_probe,
        .id_table = wm8523_i2c_id,
 };
 
index 85ad2f0..8402019 100644 (file)
@@ -996,8 +996,7 @@ static const struct of_device_id wm8580_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, wm8580_of_match);
 
-static int wm8580_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8580_i2c_probe(struct i2c_client *i2c)
 {
        const struct of_device_id *of_id;
        struct wm8580_priv *wm8580;
@@ -1051,7 +1050,7 @@ static struct i2c_driver wm8580_i2c_driver = {
                .name = "wm8580",
                .of_match_table = wm8580_of_match,
        },
-       .probe =    wm8580_i2c_probe,
+       .probe_new = wm8580_i2c_probe,
        .id_table = wm8580_i2c_id,
 };
 
index bc4d161..b68a1eb 100644 (file)
@@ -432,8 +432,7 @@ static struct spi_driver wm8711_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8711_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
+static int wm8711_i2c_probe(struct i2c_client *client)
 {
        struct wm8711_priv *wm8711;
        int ret;
@@ -466,7 +465,7 @@ static struct i2c_driver wm8711_i2c_driver = {
                .name = "wm8711",
                .of_match_table = wm8711_of_match,
        },
-       .probe =    wm8711_i2c_probe,
+       .probe_new = wm8711_i2c_probe,
        .id_table = wm8711_i2c_id,
 };
 #endif
index 2cd58d1..119ff0a 100644 (file)
@@ -273,8 +273,7 @@ static struct spi_driver wm8728_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8728_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8728_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8728_priv *wm8728;
        int ret;
@@ -307,7 +306,7 @@ static struct i2c_driver wm8728_i2c_driver = {
                .name = "wm8728",
                .of_match_table = wm8728_of_match,
        },
-       .probe =    wm8728_i2c_probe,
+       .probe_new = wm8728_i2c_probe,
        .id_table = wm8728_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c
new file mode 100644 (file)
index 0000000..fdf03bf
--- /dev/null
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731-i2c.c  --  WM8731 ALSA SoC Audio driver I2C code
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include "wm8731.h"
+
+
+static const struct of_device_id wm8731_of_match[] = {
+       { .compatible = "wlf,wm8731", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_i2c_probe(struct i2c_client *i2c)
+{
+       struct wm8731_priv *wm8731;
+       int ret;
+
+       wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+                             GFP_KERNEL);
+       if (wm8731 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, wm8731);
+
+       wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
+       if (IS_ERR(wm8731->regmap)) {
+               ret = PTR_ERR(wm8731->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return wm8731_init(&i2c->dev, wm8731);
+}
+
+static const struct i2c_device_id wm8731_i2c_id[] = {
+       { "wm8731", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+
+static struct i2c_driver wm8731_i2c_driver = {
+       .driver = {
+               .name = "wm8731",
+               .of_match_table = wm8731_of_match,
+       },
+       .probe_new = wm8731_i2c_probe,
+       .id_table = wm8731_i2c_id,
+};
+
+module_i2c_driver(wm8731_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - I2C");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c
new file mode 100644 (file)
index 0000000..542ed09
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731.c  --  WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include "wm8731.h"
+
+static const struct of_device_id wm8731_of_match[] = {
+       { .compatible = "wlf,wm8731", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_spi_probe(struct spi_device *spi)
+{
+       struct wm8731_priv *wm8731;
+       int ret;
+
+       wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
+       if (wm8731 == NULL)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, wm8731);
+
+       wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
+       if (IS_ERR(wm8731->regmap)) {
+               ret = PTR_ERR(wm8731->regmap);
+               dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return wm8731_init(&spi->dev, wm8731);
+}
+
+static struct spi_driver wm8731_spi_driver = {
+       .driver = {
+               .name   = "wm8731",
+               .of_match_table = wm8731_of_match,
+       },
+       .probe          = wm8731_spi_probe,
+};
+
+module_spi_driver(wm8731_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - SPI");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
index b14c6d1..2408c4a 100644 (file)
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
-#include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
-#include <linux/spi/spi.h>
-#include <linux/of_device.h>
-#include <linux/mutex.h>
 #include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -32,7 +28,6 @@
 
 #include "wm8731.h"
 
-#define WM8731_NUM_SUPPLIES 4
 static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
        "AVDD",
        "HPVDD",
@@ -40,21 +35,6 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
        "DBVDD",
 };
 
-/* codec private data */
-struct wm8731_priv {
-       struct regmap *regmap;
-       struct clk *mclk;
-       struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
-       const struct snd_pcm_hw_constraint_list *constraints;
-       unsigned int sysclk;
-       int sysclk_type;
-       int playback_fs;
-       bool deemph;
-
-       struct mutex lock;
-};
-
-
 /*
  * wm8731 register cache
  */
@@ -429,12 +409,11 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
        struct snd_soc_component *component = codec_dai->component;
        u16 iface = 0;
 
-       /* set master/slave audio interface */
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBM_CFM:
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+       case SND_SOC_DAIFMT_CBP_CFP:
                iface |= 0x0040;
                break;
-       case SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_CBC_CFC:
                break;
        default:
                return -EINVAL;
@@ -570,59 +549,6 @@ static struct snd_soc_dai_driver wm8731_dai = {
        .symmetric_rate = 1,
 };
 
-static int wm8731_request_supplies(struct device *dev,
-               struct wm8731_priv *wm8731)
-{
-       int ret = 0, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
-               wm8731->supplies[i].supply = wm8731_supply_names[i];
-
-       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
-                                wm8731->supplies);
-       if (ret != 0) {
-               dev_err(dev, "Failed to request supplies: %d\n", ret);
-               return ret;
-       }
-
-       ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
-                                   wm8731->supplies);
-       if (ret != 0) {
-               dev_err(dev, "Failed to enable supplies: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731)
-{
-       int ret = 0;
-
-       ret = wm8731_reset(wm8731->regmap);
-       if (ret < 0) {
-               dev_err(dev, "Failed to issue reset: %d\n", ret);
-               goto err;
-       }
-
-       /* Clear POWEROFF, keep everything else disabled */
-       regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
-
-       /* Latch the update bits */
-       regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
-       regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
-       regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
-       regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
-
-       /* Disable bypass path by default */
-       regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
-
-       regcache_mark_dirty(wm8731->regmap);
-
-err:
-       return ret;
-}
-
 static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
        .set_bias_level         = wm8731_set_bias_level,
        .controls               = wm8731_snd_controls,
@@ -638,136 +564,65 @@ static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static const struct of_device_id wm8731_of_match[] = {
-       { .compatible = "wlf,wm8731", },
-       { }
-};
-
-MODULE_DEVICE_TABLE(of, wm8731_of_match);
-
-static const struct regmap_config wm8731_regmap = {
-       .reg_bits = 7,
-       .val_bits = 9,
-
-       .max_register = WM8731_RESET,
-       .volatile_reg = wm8731_volatile,
-
-       .cache_type = REGCACHE_RBTREE,
-       .reg_defaults = wm8731_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
-};
-
-#if defined(CONFIG_SPI_MASTER)
-static int wm8731_spi_probe(struct spi_device *spi)
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731)
 {
-       struct wm8731_priv *wm8731;
-       int ret;
-
-       wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
-       if (wm8731 == NULL)
-               return -ENOMEM;
+       int ret = 0, i;
 
-       wm8731->mclk = devm_clk_get(&spi->dev, "mclk");
+       wm8731->mclk = devm_clk_get(dev, "mclk");
        if (IS_ERR(wm8731->mclk)) {
                ret = PTR_ERR(wm8731->mclk);
                if (ret == -ENOENT) {
                        wm8731->mclk = NULL;
-                       dev_warn(&spi->dev, "Assuming static MCLK\n");
+                       dev_warn(dev, "Assuming static MCLK\n");
                } else {
-                       dev_err(&spi->dev, "Failed to get MCLK: %d\n",
-                               ret);
+                       dev_err(dev, "Failed to get MCLK: %d\n", ret);
                        return ret;
                }
        }
 
        mutex_init(&wm8731->lock);
 
-       spi_set_drvdata(spi, wm8731);
-
-       ret = wm8731_request_supplies(&spi->dev, wm8731);
-       if (ret != 0)
-               return ret;
+       for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+               wm8731->supplies[i].supply = wm8731_supply_names[i];
 
-       wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
-       if (IS_ERR(wm8731->regmap)) {
-               ret = PTR_ERR(wm8731->regmap);
-               dev_err(&spi->dev, "Failed to allocate register map: %d\n",
-                       ret);
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
+                                wm8731->supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to request supplies: %d\n", ret);
                return ret;
        }
 
-       ret = wm8731_hw_init(&spi->dev, wm8731);
-       if (ret != 0)
-               return ret;
-
-       ret = devm_snd_soc_register_component(&spi->dev,
-                       &soc_component_dev_wm8731, &wm8731_dai, 1);
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+                                   wm8731->supplies);
        if (ret != 0) {
-               dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
+               dev_err(dev, "Failed to enable supplies: %d\n", ret);
                return ret;
        }
 
-       return 0;
-}
-
-static struct spi_driver wm8731_spi_driver = {
-       .driver = {
-               .name   = "wm8731",
-               .of_match_table = wm8731_of_match,
-       },
-       .probe          = wm8731_spi_probe,
-};
-#endif /* CONFIG_SPI_MASTER */
-
-#if IS_ENABLED(CONFIG_I2C)
-static int wm8731_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
-{
-       struct wm8731_priv *wm8731;
-       int ret;
-
-       wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
-                             GFP_KERNEL);
-       if (wm8731 == NULL)
-               return -ENOMEM;
-
-       wm8731->mclk = devm_clk_get(&i2c->dev, "mclk");
-       if (IS_ERR(wm8731->mclk)) {
-               ret = PTR_ERR(wm8731->mclk);
-               if (ret == -ENOENT) {
-                       wm8731->mclk = NULL;
-                       dev_warn(&i2c->dev, "Assuming static MCLK\n");
-               } else {
-                       dev_err(&i2c->dev, "Failed to get MCLK: %d\n",
-                               ret);
-                       return ret;
-               }
+       ret = wm8731_reset(wm8731->regmap);
+       if (ret < 0) {
+               dev_err(dev, "Failed to issue reset: %d\n", ret);
+               goto err_regulator_enable;
        }
 
-       mutex_init(&wm8731->lock);
-
-       i2c_set_clientdata(i2c, wm8731);
+       /* Clear POWEROFF, keep everything else disabled */
+       regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
 
-       ret = wm8731_request_supplies(&i2c->dev, wm8731);
-       if (ret != 0)
-               return ret;
+       /* Latch the update bits */
+       regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
+       regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
+       regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
+       regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
 
-       wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
-       if (IS_ERR(wm8731->regmap)) {
-               ret = PTR_ERR(wm8731->regmap);
-               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
-                       ret);
-               goto err_regulator_enable;
-       }
+       /* Disable bypass path by default */
+       regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
 
-       ret = wm8731_hw_init(&i2c->dev, wm8731);
-       if (ret != 0)
-               goto err_regulator_enable;
+       regcache_mark_dirty(wm8731->regmap);
 
-       ret = devm_snd_soc_register_component(&i2c->dev,
+       ret = devm_snd_soc_register_component(dev,
                        &soc_component_dev_wm8731, &wm8731_dai, 1);
        if (ret != 0) {
-               dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+               dev_err(dev, "Failed to register CODEC: %d\n", ret);
                goto err_regulator_enable;
        }
 
@@ -779,54 +634,20 @@ err_regulator_enable:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(wm8731_init);
 
-static const struct i2c_device_id wm8731_i2c_id[] = {
-       { "wm8731", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
-
-static struct i2c_driver wm8731_i2c_driver = {
-       .driver = {
-               .name = "wm8731",
-               .of_match_table = wm8731_of_match,
-       },
-       .probe =    wm8731_i2c_probe,
-       .id_table = wm8731_i2c_id,
-};
-#endif
+const struct regmap_config wm8731_regmap = {
+       .reg_bits = 7,
+       .val_bits = 9,
 
-static int __init wm8731_modinit(void)
-{
-       int ret = 0;
-#if IS_ENABLED(CONFIG_I2C)
-       ret = i2c_add_driver(&wm8731_i2c_driver);
-       if (ret != 0) {
-               printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
-                      ret);
-       }
-#endif
-#if defined(CONFIG_SPI_MASTER)
-       ret = spi_register_driver(&wm8731_spi_driver);
-       if (ret != 0) {
-               printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n",
-                      ret);
-       }
-#endif
-       return ret;
-}
-module_init(wm8731_modinit);
+       .max_register = WM8731_RESET,
+       .volatile_reg = wm8731_volatile,
 
-static void __exit wm8731_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
-       i2c_del_driver(&wm8731_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
-       spi_unregister_driver(&wm8731_spi_driver);
-#endif
-}
-module_exit(wm8731_exit);
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = wm8731_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(wm8731_regmap);
 
 MODULE_DESCRIPTION("ASoC WM8731 driver");
 MODULE_AUTHOR("Richard Purdie");
index 4fcf122..8d5a7a9 100644 (file)
 #ifndef _WM8731_H
 #define _WM8731_H
 
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+struct clk;
+struct snd_pcm_hw_constraint_list;
+
 /* WM8731 register space */
 
 #define WM8731_LINVOL   0x00
 
 #define WM8731_DAI             0
 
+#define WM8731_NUM_SUPPLIES 4
+
+/* codec private data */
+struct wm8731_priv {
+       struct regmap *regmap;
+       struct clk *mclk;
+       struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+       const struct snd_pcm_hw_constraint_list *constraints;
+       unsigned int sysclk;
+       int sysclk_type;
+       int playback_fs;
+       bool deemph;
+
+       struct mutex lock;
+};
+
+extern const struct regmap_config wm8731_regmap;
+
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731);
+
 #endif
index 7a3f9fb..5778091 100644 (file)
@@ -606,8 +606,7 @@ static const struct regmap_config wm8737_regmap = {
 };
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8737_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8737_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8737_priv *wm8737;
        int ret, i;
@@ -651,7 +650,7 @@ static struct i2c_driver wm8737_i2c_driver = {
                .name = "wm8737",
                .of_match_table = wm8737_of_match,
        },
-       .probe =    wm8737_i2c_probe,
+       .probe_new = wm8737_i2c_probe,
        .id_table = wm8737_i2c_id,
 };
 #endif
index 0e39943..871e2c5 100644 (file)
@@ -565,8 +565,7 @@ static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
 }
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8741_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8741_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8741_priv *wm8741;
        int ret, i;
@@ -618,7 +617,7 @@ static struct i2c_driver wm8741_i2c_driver = {
                .name = "wm8741",
                .of_match_table = wm8741_of_match,
        },
-       .probe =    wm8741_i2c_probe,
+       .probe_new = wm8741_i2c_probe,
        .id_table = wm8741_i2c_id,
 };
 #endif
index 9491817..1426fc1 100644 (file)
@@ -780,8 +780,7 @@ static struct spi_driver wm8750_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8750_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8750_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8750_priv *wm8750;
        struct regmap *regmap;
@@ -815,7 +814,7 @@ static struct i2c_driver wm8750_i2c_driver = {
                .name = "wm8750",
                .of_match_table = wm8750_of_match,
        },
-       .probe =    wm8750_i2c_probe,
+       .probe_new = wm8750_i2c_probe,
        .id_table = wm8750_i2c_id,
 };
 #endif
index deaa54b..931134d 100644 (file)
@@ -1552,8 +1552,7 @@ static struct spi_driver wm8753_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8753_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8753_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8753_priv *wm8753;
        int ret;
@@ -1592,7 +1591,7 @@ static struct i2c_driver wm8753_i2c_driver = {
                .name = "wm8753",
                .of_match_table = wm8753_of_match,
        },
-       .probe =    wm8753_i2c_probe,
+       .probe_new = wm8753_i2c_probe,
        .id_table = wm8753_i2c_id,
 };
 #endif
index 554acf5..f164cb6 100644 (file)
@@ -490,8 +490,7 @@ static struct spi_driver wm8776_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8776_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8776_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8776_priv *wm8776;
        int ret;
@@ -525,7 +524,7 @@ static struct i2c_driver wm8776_i2c_driver = {
                .name = "wm8776",
                .of_match_table = wm8776_of_match,
        },
-       .probe =    wm8776_i2c_probe,
+       .probe_new = wm8776_i2c_probe,
        .id_table = wm8776_i2c_id,
 };
 #endif
index f97a75e..04dc9fb 100644 (file)
@@ -14,8 +14,7 @@
 
 #include "wm8804.h"
 
-static int wm8804_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8804_i2c_probe(struct i2c_client *i2c)
 {
        struct regmap *regmap;
 
@@ -62,7 +61,7 @@ static struct i2c_driver wm8804_i2c_driver = {
                .of_match_table = of_match_ptr(wm8804_of_match),
                .acpi_match_table = ACPI_PTR(wm8804_acpi_match),
        },
-       .probe = wm8804_i2c_probe,
+       .probe_new = wm8804_i2c_probe,
        .remove = wm8804_i2c_remove,
        .id_table = wm8804_i2c_id
 };
index bf3a441..84a3daf 100644 (file)
@@ -1261,8 +1261,7 @@ static struct spi_driver wm8900_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8900_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8900_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8900_priv *wm8900;
        int ret;
@@ -1299,7 +1298,7 @@ static struct i2c_driver wm8900_i2c_driver = {
        .driver = {
                .name = "wm8900",
        },
-       .probe =    wm8900_i2c_probe,
+       .probe_new = wm8900_i2c_probe,
        .remove =   wm8900_i2c_remove,
        .id_table = wm8900_i2c_id,
 };
index 75f3015..3c95c2a 100644 (file)
@@ -1981,8 +1981,7 @@ static int wm8903_set_pdata_from_of(struct i2c_client *i2c,
        return 0;
 }
 
-static int wm8903_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8903_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct wm8903_priv *wm8903;
@@ -2132,7 +2131,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
                if (ret != 0) {
                        dev_err(wm8903->dev, "Failed to request IRQ: %d\n",
                                ret);
-                       return ret;
+                       goto err;
                }
 
                /* Enable write sequencer interrupts */
@@ -2214,7 +2213,7 @@ static struct i2c_driver wm8903_i2c_driver = {
                .name = "wm8903",
                .of_match_table = wm8903_of_match,
        },
-       .probe =    wm8903_i2c_probe,
+       .probe_new = wm8903_i2c_probe,
        .remove =   wm8903_i2c_remove,
        .id_table = wm8903_i2c_id,
 };
index a02a77f..04bb8e3 100644 (file)
@@ -2162,8 +2162,9 @@ static const struct of_device_id wm8904_of_match[] = {
 MODULE_DEVICE_TABLE(of, wm8904_of_match);
 #endif
 
-static int wm8904_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static const struct i2c_device_id wm8904_i2c_id[];
+
+static int wm8904_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8904_priv *wm8904;
        unsigned int val;
@@ -2197,6 +2198,8 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
                        return -EINVAL;
                wm8904->devtype = (enum wm8904_type)match->data;
        } else {
+               const struct i2c_device_id *id =
+                       i2c_match_id(wm8904_i2c_id, i2c);
                wm8904->devtype = id->driver_data;
        }
 
@@ -2328,7 +2331,7 @@ static struct i2c_driver wm8904_i2c_driver = {
                .name = "wm8904",
                .of_match_table = of_match_ptr(wm8904_of_match),
        },
-       .probe =    wm8904_i2c_probe,
+       .probe_new = wm8904_i2c_probe,
        .id_table = wm8904_i2c_id,
 };
 
index 440d048..589394d 100644 (file)
@@ -750,8 +750,7 @@ static const struct regmap_config wm8940_regmap = {
        .volatile_reg = wm8940_volatile_register,
 };
 
-static int wm8940_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8940_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8940_priv *wm8940;
        int ret;
@@ -779,11 +778,18 @@ static const struct i2c_device_id wm8940_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
 
+static const struct of_device_id wm8940_of_match[] = {
+       { .compatible = "wlf,wm8940", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8940_of_match);
+
 static struct i2c_driver wm8940_i2c_driver = {
        .driver = {
                .name = "wm8940",
+               .of_match_table = wm8940_of_match,
        },
-       .probe =    wm8940_i2c_probe,
+       .probe_new = wm8940_i2c_probe,
        .id_table = wm8940_i2c_id,
 };
 
index 513df47..80e3cbd 100644 (file)
@@ -968,8 +968,7 @@ static const struct regmap_config wm8955_regmap = {
        .num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults),
 };
 
-static int wm8955_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8955_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8955_priv *wm8955;
        int ret;
@@ -1005,7 +1004,7 @@ static struct i2c_driver wm8955_i2c_driver = {
        .driver = {
                .name = "wm8955",
        },
-       .probe =    wm8955_i2c_probe,
+       .probe_new = wm8955_i2c_probe,
        .id_table = wm8955_i2c_id,
 };
 
index ca7660f..8c8f32b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pm.h>
 #include <linux/clk.h>
 #include <linux/i2c.h>
+#include <linux/acpi.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -1410,8 +1411,7 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
                                   ARRAY_SIZE(pdata->hp_cfg));
 }
 
-static int wm8960_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8960_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
        struct wm8960_priv *wm8960;
@@ -1498,18 +1498,30 @@ static const struct i2c_device_id wm8960_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
 
+#if defined(CONFIG_OF)
 static const struct of_device_id wm8960_of_match[] = {
        { .compatible = "wlf,wm8960", },
        { }
 };
 MODULE_DEVICE_TABLE(of, wm8960_of_match);
+#endif
+
+#if defined(CONFIG_ACPI)
+static const struct acpi_device_id wm8960_acpi_match[] = {
+       { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */
+       { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match);
+#endif
 
 static struct i2c_driver wm8960_i2c_driver = {
        .driver = {
                .name = "wm8960",
-               .of_match_table = wm8960_of_match,
+               .of_match_table = of_match_ptr(wm8960_of_match),
+               .acpi_match_table = ACPI_PTR(wm8960_acpi_match),
        },
-       .probe =    wm8960_i2c_probe,
+       .probe_new = wm8960_i2c_probe,
        .remove =   wm8960_i2c_remove,
        .id_table = wm8960_i2c_id,
 };
index ef80d9f..69eb731 100644 (file)
@@ -911,8 +911,7 @@ static const struct regmap_config wm8961_regmap = {
        .readable_reg = wm8961_readable,
 };
 
-static int wm8961_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8961_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8961_priv *wm8961;
        unsigned int val;
@@ -977,7 +976,7 @@ static struct i2c_driver wm8961_i2c_driver = {
        .driver = {
                .name = "wm8961",
        },
-       .probe =    wm8961_i2c_probe,
+       .probe_new = wm8961_i2c_probe,
        .id_table = wm8961_i2c_id,
 };
 
index 2c41d31..34cd5a2 100644 (file)
@@ -2896,9 +2896,8 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
 
        reinit_completion(&wm8962->fll_lock);
 
-       ret = pm_runtime_get_sync(component->dev);
+       ret = pm_runtime_resume_and_get(component->dev);
        if (ret < 0) {
-               pm_runtime_put_noidle(component->dev);
                dev_err(component->dev, "Failed to resume device: %d\n", ret);
                return ret;
        }
@@ -3030,9 +3029,8 @@ static irqreturn_t wm8962_irq(int irq, void *data)
        unsigned int active;
        int reg, ret;
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0) {
-               pm_runtime_put_noidle(dev);
                dev_err(dev, "Failed to resume: %d\n", ret);
                return IRQ_NONE;
        }
@@ -3555,8 +3553,7 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
        return PTR_ERR_OR_ZERO(pdata->mclk);
 }
 
-static int wm8962_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8962_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev);
        struct wm8962_priv *wm8962;
@@ -3892,7 +3889,7 @@ static struct i2c_driver wm8962_i2c_driver = {
                .of_match_table = wm8962_of_match,
                .pm = &wm8962_pm,
        },
-       .probe =    wm8962_i2c_probe,
+       .probe_new = wm8962_i2c_probe,
        .remove =   wm8962_i2c_remove,
        .id_table = wm8962_i2c_id,
 };
index ddf0e2f..8a289b0 100644 (file)
@@ -672,8 +672,7 @@ static const struct regmap_config wm8971_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int wm8971_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8971_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8971_priv *wm8971;
 
@@ -702,7 +701,7 @@ static struct i2c_driver wm8971_i2c_driver = {
        .driver = {
                .name = "wm8971",
        },
-       .probe =    wm8971_i2c_probe,
+       .probe_new = wm8971_i2c_probe,
        .id_table = wm8971_i2c_id,
 };
 
index fdc68ab..a8d7809 100644 (file)
@@ -685,8 +685,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8974 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int wm8974_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8974_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8974_priv *priv;
        struct regmap *regmap;
@@ -725,7 +724,7 @@ static struct i2c_driver wm8974_i2c_driver = {
                .name = "wm8974",
                .of_match_table = wm8974_of_match,
        },
-       .probe =    wm8974_i2c_probe,
+       .probe_new = wm8974_i2c_probe,
        .id_table = wm8974_i2c_id,
 };
 
index 7091e1a..141f50b 100644 (file)
@@ -1020,8 +1020,7 @@ static const struct regmap_config wm8978_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
 };
 
-static int wm8978_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8978_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8978_priv *wm8978;
        int ret;
@@ -1074,7 +1073,7 @@ static struct i2c_driver wm8978_i2c_driver = {
                .name = "wm8978",
                .of_match_table = wm8978_of_match,
        },
-       .probe =    wm8978_i2c_probe,
+       .probe_new = wm8978_i2c_probe,
        .id_table = wm8978_i2c_id,
 };
 
index d8ed22a..ae89554 100644 (file)
@@ -1035,8 +1035,7 @@ static struct spi_driver wm8983_spi_driver = {
 #endif
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8983_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8983_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8983_priv *wm8983;
        int ret;
@@ -1070,7 +1069,7 @@ static struct i2c_driver wm8983_i2c_driver = {
        .driver = {
                .name = "wm8983",
        },
-       .probe = wm8983_i2c_probe,
+       .probe_new = wm8983_i2c_probe,
        .id_table = wm8983_i2c_id
 };
 #endif
index a7e0110..cf2c32e 100644 (file)
@@ -1167,10 +1167,12 @@ static struct spi_driver wm8985_spi_driver = {
 #endif
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8985_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static const struct i2c_device_id wm8985_i2c_id[];
+
+static int wm8985_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8985_priv *wm8985;
+       const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c);
        int ret;
 
        wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL);
@@ -1205,7 +1207,7 @@ static struct i2c_driver wm8985_i2c_driver = {
        .driver = {
                .name = "wm8985",
        },
-       .probe = wm8985_i2c_probe,
+       .probe_new = wm8985_i2c_probe,
        .id_table = wm8985_i2c_id
 };
 #endif
index 1d2f881..27538d6 100644 (file)
@@ -872,8 +872,7 @@ static struct spi_driver wm8988_spi_driver = {
 #endif /* CONFIG_SPI_MASTER */
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8988_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8988_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8988_priv *wm8988;
        int ret;
@@ -907,7 +906,7 @@ static struct i2c_driver wm8988_i2c_driver = {
        .driver = {
                .name = "wm8988",
        },
-       .probe =    wm8988_i2c_probe,
+       .probe_new = wm8988_i2c_probe,
        .id_table = wm8988_i2c_id,
 };
 #endif
index 9389407..c9448a5 100644 (file)
@@ -1220,8 +1220,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8990 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int wm8990_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8990_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8990_priv *wm8990;
        int ret;
@@ -1249,7 +1248,7 @@ static struct i2c_driver wm8990_i2c_driver = {
        .driver = {
                .name = "wm8990",
        },
-       .probe =    wm8990_i2c_probe,
+       .probe_new = wm8990_i2c_probe,
        .id_table = wm8990_i2c_id,
 };
 
index 16bc860..998bc89 100644 (file)
@@ -1257,8 +1257,7 @@ static const struct regmap_config wm8991_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int wm8991_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8991_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8991_priv *wm8991;
        unsigned int val;
@@ -1325,7 +1324,7 @@ static struct i2c_driver wm8991_i2c_driver = {
        .driver = {
                .name = "wm8991",
        },
-       .probe = wm8991_i2c_probe,
+       .probe_new = wm8991_i2c_probe,
        .id_table = wm8991_i2c_id,
 };
 
index c4f4169..f4da77e 100644 (file)
@@ -1624,8 +1624,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8993 = {
        .non_legacy_dai_naming  = 1,
 };
 
-static int wm8993_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8993_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8993_priv *wm8993;
        unsigned int reg;
@@ -1745,7 +1744,7 @@ static struct i2c_driver wm8993_i2c_driver = {
        .driver = {
                .name = "wm8993",
        },
-       .probe =    wm8993_i2c_probe,
+       .probe_new = wm8993_i2c_probe,
        .remove =   wm8993_i2c_remove,
        .id_table = wm8993_i2c_id,
 };
index b896d9c..ea97274 100644 (file)
@@ -2231,8 +2231,7 @@ static struct spi_driver wm8995_spi_driver = {
 #endif
 
 #if IS_ENABLED(CONFIG_I2C)
-static int wm8995_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8995_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8995_priv *wm8995;
        int ret;
@@ -2270,7 +2269,7 @@ static struct i2c_driver wm8995_i2c_driver = {
        .driver = {
                .name = "wm8995",
        },
-       .probe = wm8995_i2c_probe,
+       .probe_new = wm8995_i2c_probe,
        .id_table = wm8995_i2c_id
 };
 #endif
index 197ae7d..f7bb27d 100644 (file)
@@ -2755,8 +2755,7 @@ static struct snd_soc_dai_driver wm8996_dai[] = {
        },
 };
 
-static int wm8996_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8996_i2c_probe(struct i2c_client *i2c)
 {
        struct wm8996_priv *wm8996;
        int ret, i;
@@ -3091,7 +3090,7 @@ static struct i2c_driver wm8996_i2c_driver = {
        .driver = {
                .name = "wm8996",
        },
-       .probe =    wm8996_i2c_probe,
+       .probe_new = wm8996_i2c_probe,
        .remove =   wm8996_i2c_remove,
        .id_table = wm8996_i2c_id,
 };
index 4a667ee..87b5844 100644 (file)
@@ -1299,8 +1299,7 @@ static const struct regmap_config wm9081_regmap = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int wm9081_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm9081_i2c_probe(struct i2c_client *i2c)
 {
        struct wm9081_priv *wm9081;
        unsigned int reg;
@@ -1373,7 +1372,7 @@ static struct i2c_driver wm9081_i2c_driver = {
        .driver = {
                .name = "wm9081",
        },
-       .probe =    wm9081_i2c_probe,
+       .probe_new = wm9081_i2c_probe,
        .remove =   wm9081_i2c_remove,
        .id_table = wm9081_i2c_id,
 };
index e0231a5..f7d80f1 100644 (file)
@@ -543,7 +543,6 @@ static const struct snd_soc_component_driver soc_component_dev_wm9090 = {
        .suspend_bias_off       = 1,
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
-       .endianness             = 1,
        .non_legacy_dai_naming  = 1,
 };
 
@@ -561,8 +560,7 @@ static const struct regmap_config wm9090_regmap = {
 };
 
 
-static int wm9090_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm9090_i2c_probe(struct i2c_client *i2c)
 {
        struct wm9090_priv *wm9090;
        unsigned int reg;
@@ -619,7 +617,7 @@ static struct i2c_driver wm9090_i2c_driver = {
        .driver = {
                .name = "wm9090",
        },
-       .probe = wm9090_i2c_probe,
+       .probe_new = wm9090_i2c_probe,
        .id_table = wm9090_id,
 };
 
index 616b26c..f3a56f3 100644 (file)
@@ -1066,6 +1066,7 @@ static const struct snd_soc_component_driver wsa881x_component_drv = {
        .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
        .dapm_routes = wsa881x_audio_map,
        .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+       .endianness = 1,
 };
 
 static int wsa881x_update_status(struct sdw_slave *slave,
index d7d1536..20a9f8e 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <linux/pm_runtime.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
@@ -1211,11 +1211,9 @@ static int fsl_asrc_probe(struct platform_device *pdev)
                        goto err_pm_disable;
        }
 
-       ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
                goto err_pm_get_sync;
-       }
 
        ret = fsl_asrc_init(asrc);
        if (ret) {
index cd9b36e..5038faf 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
 
index 30620d5..86d5c36 100644 (file)
@@ -7,7 +7,7 @@
 #define _FSL_EASRC_H
 
 #include <sound/asound.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 #include "fsl_asrc_common.h"
 
index ed444e8..1a2bdf8 100644 (file)
@@ -1050,11 +1050,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
                        goto err_pm_disable;
        }
 
-       ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
                goto err_pm_get_sync;
-       }
 
        ret = fsl_esai_hw_init(esai_priv);
        if (ret)
index 9f90989..7b88d52 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright 2018 NXP
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
@@ -15,6 +16,7 @@
 #include <linux/regmap.h>
 #include <linux/sysfs.h>
 #include <linux/types.h>
+#include <linux/dma/imx-dma.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/core.h>
 
 #include "fsl_micfil.h"
-#include "imx-pcm.h"
 
-#define FSL_MICFIL_RATES               SNDRV_PCM_RATE_8000_48000
-#define FSL_MICFIL_FORMATS             (SNDRV_PCM_FMTBIT_S16_LE)
+#define MICFIL_OSR_DEFAULT     16
+
+enum quality {
+       QUALITY_HIGH,
+       QUALITY_MEDIUM,
+       QUALITY_LOW,
+       QUALITY_VLOW0,
+       QUALITY_VLOW1,
+       QUALITY_VLOW2,
+};
 
 struct fsl_micfil {
        struct platform_device *pdev;
@@ -34,13 +43,11 @@ struct fsl_micfil {
        struct clk *busclk;
        struct clk *mclk;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
+       struct sdma_peripheral_config sdmacfg;
        unsigned int dataline;
        char name[32];
        int irq[MICFIL_IRQ_LINES];
-       unsigned int mclk_streams;
-       int quality;    /*QUALITY 2-0 bits */
-       bool slave_mode;
-       int channel_gain[8];
+       enum quality quality;
 };
 
 struct fsl_micfil_soc_data {
@@ -48,6 +55,7 @@ struct fsl_micfil_soc_data {
        unsigned int fifo_depth;
        unsigned int dataline;
        bool imx;
+       u64  formats;
 };
 
 static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
@@ -55,37 +63,91 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
        .fifos = 8,
        .fifo_depth = 8,
        .dataline =  0xf,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
+       .imx = true,
+       .fifos = 8,
+       .fifo_depth = 32,
+       .dataline =  0xf,
+       .formats = SNDRV_PCM_FMTBIT_S32_LE,
 };
 
 static const struct of_device_id fsl_micfil_dt_ids[] = {
        { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm },
+       { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids);
 
-/* Table 5. Quality Modes
- * Medium      0 0 0
- * High                0 0 1
- * Very Low 2  1 0 0
- * Very Low 1  1 0 1
- * Very Low 0  1 1 0
- * Low         1 1 1
- */
 static const char * const micfil_quality_select_texts[] = {
-       "Medium", "High",
-       "N/A", "N/A",
-       "VLow2", "VLow1",
-       "VLow0", "Low",
+       [QUALITY_HIGH] = "High",
+       [QUALITY_MEDIUM] = "Medium",
+       [QUALITY_LOW] = "Low",
+       [QUALITY_VLOW0] = "VLow0",
+       [QUALITY_VLOW1] = "Vlow1",
+       [QUALITY_VLOW2] = "Vlow2",
 };
 
 static const struct soc_enum fsl_micfil_quality_enum =
-       SOC_ENUM_SINGLE(REG_MICFIL_CTRL2,
-                       MICFIL_CTRL2_QSEL_SHIFT,
-                       ARRAY_SIZE(micfil_quality_select_texts),
-                       micfil_quality_select_texts);
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts),
+                           micfil_quality_select_texts);
 
 static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0);
 
+static int micfil_set_quality(struct fsl_micfil *micfil)
+{
+       u32 qsel;
+
+       switch (micfil->quality) {
+       case QUALITY_HIGH:
+               qsel = MICFIL_QSEL_HIGH_QUALITY;
+               break;
+       case QUALITY_MEDIUM:
+               qsel = MICFIL_QSEL_MEDIUM_QUALITY;
+               break;
+       case QUALITY_LOW:
+               qsel = MICFIL_QSEL_LOW_QUALITY;
+               break;
+       case QUALITY_VLOW0:
+               qsel = MICFIL_QSEL_VLOW0_QUALITY;
+               break;
+       case QUALITY_VLOW1:
+               qsel = MICFIL_QSEL_VLOW1_QUALITY;
+               break;
+       case QUALITY_VLOW2:
+               qsel = MICFIL_QSEL_VLOW2_QUALITY;
+               break;
+       }
+
+       return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+                                 MICFIL_CTRL2_QSEL,
+                                 FIELD_PREP(MICFIL_CTRL2_QSEL, qsel));
+}
+
+static int micfil_quality_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+       ucontrol->value.integer.value[0] = micfil->quality;
+
+       return 0;
+}
+
+static int micfil_quality_set(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+       micfil->quality = ucontrol->value.integer.value[0];
+
+       return micfil_set_quality(micfil);
+}
+
 static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
        SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
                          MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv),
@@ -105,64 +167,9 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
                          MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv),
        SOC_ENUM_EXT("MICFIL Quality Select",
                     fsl_micfil_quality_enum,
-                    snd_soc_get_enum_double, snd_soc_put_enum_double),
+                    micfil_quality_get, micfil_quality_set),
 };
 
-static inline int get_pdm_clk(struct fsl_micfil *micfil,
-                             unsigned int rate)
-{
-       u32 ctrl2_reg;
-       int qsel, osr;
-       int bclk;
-
-       regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
-       osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK)
-                   >> MICFIL_CTRL2_CICOSR_SHIFT);
-
-       regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
-       qsel = ctrl2_reg & MICFIL_CTRL2_QSEL_MASK;
-
-       switch (qsel) {
-       case MICFIL_HIGH_QUALITY:
-               bclk = rate * 8 * osr / 2; /* kfactor = 0.5 */
-               break;
-       case MICFIL_MEDIUM_QUALITY:
-       case MICFIL_VLOW0_QUALITY:
-               bclk = rate * 4 * osr * 1; /* kfactor = 1 */
-               break;
-       case MICFIL_LOW_QUALITY:
-       case MICFIL_VLOW1_QUALITY:
-               bclk = rate * 2 * osr * 2; /* kfactor = 2 */
-               break;
-       case MICFIL_VLOW2_QUALITY:
-               bclk = rate * osr * 4; /* kfactor = 4 */
-               break;
-       default:
-               dev_err(&micfil->pdev->dev,
-                       "Please make sure you select a valid quality.\n");
-               bclk = -1;
-               break;
-       }
-
-       return bclk;
-}
-
-static inline int get_clk_div(struct fsl_micfil *micfil,
-                             unsigned int rate)
-{
-       u32 ctrl2_reg;
-       long mclk_rate;
-       int clk_div;
-
-       regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
-
-       mclk_rate = clk_get_rate(micfil->mclk);
-
-       clk_div = mclk_rate / (get_pdm_clk(micfil, rate) * 2);
-
-       return clk_div;
-}
-
 /* The SRES is a self-negated bit which provides the CPU with the
  * capability to initialize the PDM Interface module through the
  * slave-bus interface. This bit always reads as zero, and this
@@ -173,45 +180,19 @@ static int fsl_micfil_reset(struct device *dev)
        struct fsl_micfil *micfil = dev_get_drvdata(dev);
        int ret;
 
-       ret = regmap_update_bits(micfil->regmap,
-                                REG_MICFIL_CTRL1,
-                                MICFIL_CTRL1_MDIS_MASK,
-                                0);
-       if (ret) {
-               dev_err(dev, "failed to clear MDIS bit %d\n", ret);
+       ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+                               MICFIL_CTRL1_MDIS);
+       if (ret)
                return ret;
-       }
 
-       ret = regmap_update_bits(micfil->regmap,
-                                REG_MICFIL_CTRL1,
-                                MICFIL_CTRL1_SRES_MASK,
-                                MICFIL_CTRL1_SRES);
-       if (ret) {
-               dev_err(dev, "failed to reset MICFIL: %d\n", ret);
+       ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+                             MICFIL_CTRL1_SRES);
+       if (ret)
                return ret;
-       }
 
        return 0;
 }
 
-static int fsl_micfil_set_mclk_rate(struct fsl_micfil *micfil,
-                                   unsigned int freq)
-{
-       struct device *dev = &micfil->pdev->dev;
-       int ret;
-
-       clk_disable_unprepare(micfil->mclk);
-
-       ret = clk_set_rate(micfil->mclk, freq * 1024);
-       if (ret)
-               dev_warn(dev, "failed to set rate (%u): %d\n",
-                        freq * 1024, ret);
-
-       clk_prepare_enable(micfil->mclk);
-
-       return ret;
-}
-
 static int fsl_micfil_startup(struct snd_pcm_substream *substream,
                              struct snd_soc_dai *dai)
 {
@@ -249,42 +230,32 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
                 * 11 - reserved
                 */
                ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
-                                        MICFIL_CTRL1_DISEL_MASK,
-                                        (1 << MICFIL_CTRL1_DISEL_SHIFT));
-               if (ret) {
-                       dev_err(dev, "failed to update DISEL bits\n");
+                               MICFIL_CTRL1_DISEL,
+                               FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA));
+               if (ret)
                        return ret;
-               }
 
                /* Enable the module */
-               ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
-                                        MICFIL_CTRL1_PDMIEN_MASK,
-                                        MICFIL_CTRL1_PDMIEN);
-               if (ret) {
-                       dev_err(dev, "failed to enable the module\n");
+               ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+                                     MICFIL_CTRL1_PDMIEN);
+               if (ret)
                        return ret;
-               }
 
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                /* Disable the module */
-               ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
-                                        MICFIL_CTRL1_PDMIEN_MASK,
-                                        0);
-               if (ret) {
-                       dev_err(dev, "failed to enable the module\n");
+               ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+                                       MICFIL_CTRL1_PDMIEN);
+               if (ret)
                        return ret;
-               }
 
                ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
-                                        MICFIL_CTRL1_DISEL_MASK,
-                                        (0 << MICFIL_CTRL1_DISEL_SHIFT));
-               if (ret) {
-                       dev_err(dev, "failed to update DISEL bits\n");
+                               MICFIL_CTRL1_DISEL,
+                               FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE));
+               if (ret)
                        return ret;
-               }
                break;
        default:
                return -EINVAL;
@@ -292,39 +263,6 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
        return 0;
 }
 
-static int fsl_set_clock_params(struct device *dev, unsigned int rate)
-{
-       struct fsl_micfil *micfil = dev_get_drvdata(dev);
-       int clk_div;
-       int ret;
-
-       ret = fsl_micfil_set_mclk_rate(micfil, rate);
-       if (ret < 0)
-               dev_err(dev, "failed to set mclk[%lu] to rate %u\n",
-                       clk_get_rate(micfil->mclk), rate);
-
-       /* set CICOSR */
-       ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
-                                MICFIL_CTRL2_CICOSR_MASK,
-                                MICFIL_CTRL2_OSR_DEFAULT);
-       if (ret)
-               dev_err(dev, "failed to set CICOSR in reg 0x%X\n",
-                       REG_MICFIL_CTRL2);
-
-       /* set CLK_DIV */
-       clk_div = get_clk_div(micfil, rate);
-       if (clk_div < 0)
-               ret = -EINVAL;
-
-       ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
-                                MICFIL_CTRL2_CLKDIV_MASK, clk_div);
-       if (ret)
-               dev_err(dev, "failed to set CLKDIV in reg 0x%X\n",
-                       REG_MICFIL_CTRL2);
-
-       return ret;
-}
-
 static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -332,97 +270,69 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
        struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
        unsigned int channels = params_channels(params);
        unsigned int rate = params_rate(params);
-       struct device *dev = &micfil->pdev->dev;
+       int clk_div = 8;
+       int osr = MICFIL_OSR_DEFAULT;
        int ret;
 
        /* 1. Disable the module */
-       ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
-                                MICFIL_CTRL1_PDMIEN_MASK, 0);
-       if (ret) {
-               dev_err(dev, "failed to disable the module\n");
+       ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+                               MICFIL_CTRL1_PDMIEN);
+       if (ret)
                return ret;
-       }
 
        /* enable channels */
        ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
                                 0xFF, ((1 << channels) - 1));
-       if (ret) {
-               dev_err(dev, "failed to enable channels %d, reg 0x%X\n", ret,
-                       REG_MICFIL_CTRL1);
+       if (ret)
                return ret;
-       }
 
-       ret = fsl_set_clock_params(dev, rate);
-       if (ret < 0) {
-               dev_err(dev, "Failed to set clock parameters [%d]\n", ret);
+       ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);
+       if (ret)
+               return ret;
+
+       ret = micfil_set_quality(micfil);
+       if (ret)
                return ret;
-       }
 
+       ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+                                MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
+                                FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
+                                FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr));
+
+       micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg;
+       micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg);
+       micfil->sdmacfg.n_fifos_src = channels;
+       micfil->sdmacfg.sw_done = true;
        micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX;
 
        return 0;
 }
 
-static int fsl_micfil_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
-                                    unsigned int freq, int dir)
-{
-       struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
-       struct device *dev = &micfil->pdev->dev;
-
-       int ret;
-
-       if (!freq)
-               return 0;
-
-       ret = fsl_micfil_set_mclk_rate(micfil, freq);
-       if (ret < 0)
-               dev_err(dev, "failed to set mclk[%lu] to rate %u\n",
-                       clk_get_rate(micfil->mclk), freq);
-
-       return ret;
-}
-
 static const struct snd_soc_dai_ops fsl_micfil_dai_ops = {
        .startup = fsl_micfil_startup,
        .trigger = fsl_micfil_trigger,
        .hw_params = fsl_micfil_hw_params,
-       .set_sysclk = fsl_micfil_set_dai_sysclk,
 };
 
 static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
 {
        struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev);
-       struct device *dev = cpu_dai->dev;
-       unsigned int val;
        int ret;
-       int i;
 
-       /* set qsel to medium */
-       ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
-                                MICFIL_CTRL2_QSEL_MASK, MICFIL_MEDIUM_QUALITY);
-       if (ret) {
-               dev_err(dev, "failed to set quality mode bits, reg 0x%X\n",
-                       REG_MICFIL_CTRL2);
-               return ret;
-       }
+       micfil->quality = QUALITY_MEDIUM;
 
        /* set default gain to max_gain */
        regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x77777777);
-       for (i = 0; i < 8; i++)
-               micfil->channel_gain[i] = 0xF;
 
        snd_soc_dai_init_dma_data(cpu_dai, NULL,
                                  &micfil->dma_params_rx);
 
        /* FIFO Watermark Control - FIFOWMK*/
-       val = MICFIL_FIFO_CTRL_FIFOWMK(micfil->soc->fifo_depth) - 1;
        ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL,
-                                MICFIL_FIFO_CTRL_FIFOWMK_MASK,
-                                val);
-       if (ret) {
-               dev_err(dev, "failed to set FIFOWMK\n");
+                       MICFIL_FIFO_CTRL_FIFOWMK,
+                       FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1));
+       if (ret)
                return ret;
-       }
 
        return 0;
 }
@@ -433,8 +343,8 @@ static struct snd_soc_dai_driver fsl_micfil_dai = {
                .stream_name = "CPU-Capture",
                .channels_min = 1,
                .channels_max = 8,
-               .rates = FSL_MICFIL_RATES,
-               .formats = FSL_MICFIL_FORMATS,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
        .ops = &fsl_micfil_dai_ops,
 };
@@ -578,11 +488,11 @@ static irqreturn_t micfil_isr(int irq, void *devid)
        regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg);
        regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
 
-       dma_enabled = MICFIL_DMA_ENABLED(ctrl1_reg);
+       dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA;
 
        /* Channel 0-7 Output Data Flags */
        for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) {
-               if (stat_reg & MICFIL_STAT_CHXF_MASK(i))
+               if (stat_reg & MICFIL_STAT_CHXF(i))
                        dev_dbg(&pdev->dev,
                                "Data available in Data Channel %d\n", i);
                /* if DMA is not enabled, field must be written with 1
@@ -591,17 +501,17 @@ static irqreturn_t micfil_isr(int irq, void *devid)
                if (!dma_enabled)
                        regmap_write_bits(micfil->regmap,
                                          REG_MICFIL_STAT,
-                                         MICFIL_STAT_CHXF_MASK(i),
+                                         MICFIL_STAT_CHXF(i),
                                          1);
        }
 
        for (i = 0; i < MICFIL_FIFO_NUM; i++) {
-               if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER_MASK(i))
+               if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i))
                        dev_dbg(&pdev->dev,
                                "FIFO Overflow Exception flag for channel %d\n",
                                i);
 
-               if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(i))
+               if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i))
                        dev_dbg(&pdev->dev,
                                "FIFO Underflow Exception flag for channel %d\n",
                                i);
@@ -618,16 +528,16 @@ static irqreturn_t micfil_err_isr(int irq, void *devid)
 
        regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
 
-       if (stat_reg & MICFIL_STAT_BSY_FIL_MASK)
+       if (stat_reg & MICFIL_STAT_BSY_FIL)
                dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n");
 
-       if (stat_reg & MICFIL_STAT_FIR_RDY_MASK)
+       if (stat_reg & MICFIL_STAT_FIR_RDY)
                dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n");
 
-       if (stat_reg & MICFIL_STAT_LOWFREQF_MASK) {
+       if (stat_reg & MICFIL_STAT_LOWFREQF) {
                dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n");
                regmap_write_bits(micfil->regmap, REG_MICFIL_STAT,
-                                 MICFIL_STAT_LOWFREQF_MASK, 1);
+                                 MICFIL_STAT_LOWFREQF, 1);
        }
 
        return IRQ_HANDLED;
@@ -640,7 +550,6 @@ static int fsl_micfil_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *regs;
        int ret, i;
-       unsigned long irqflag = 0;
 
        micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL);
        if (!micfil)
@@ -699,17 +608,13 @@ static int fsl_micfil_probe(struct platform_device *pdev)
        /* get IRQs */
        for (i = 0; i < MICFIL_IRQ_LINES; i++) {
                micfil->irq[i] = platform_get_irq(pdev, i);
-               dev_err(&pdev->dev, "GET IRQ: %d\n", micfil->irq[i]);
                if (micfil->irq[i] < 0)
                        return micfil->irq[i];
        }
 
-       if (of_property_read_bool(np, "fsl,shared-interrupt"))
-               irqflag = IRQF_SHARED;
-
        /* Digital Microphone interface interrupt */
        ret = devm_request_irq(&pdev->dev, micfil->irq[0],
-                              micfil_isr, irqflag,
+                              micfil_isr, IRQF_SHARED,
                               micfil->name, micfil);
        if (ret) {
                dev_err(&pdev->dev, "failed to claim mic interface irq %u\n",
@@ -719,7 +624,7 @@ static int fsl_micfil_probe(struct platform_device *pdev)
 
        /* Digital Microphone interface error interrupt */
        ret = devm_request_irq(&pdev->dev, micfil->irq[1],
-                              micfil_err_isr, irqflag,
+                              micfil_err_isr, IRQF_SHARED,
                               micfil->name, micfil);
        if (ret) {
                dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n",
@@ -731,7 +636,6 @@ static int fsl_micfil_probe(struct platform_device *pdev)
        micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0;
        micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX;
 
-
        platform_set_drvdata(pdev, micfil);
 
        pm_runtime_enable(&pdev->dev);
@@ -747,6 +651,8 @@ static int fsl_micfil_probe(struct platform_device *pdev)
                return ret;
        }
 
+       fsl_micfil_dai.capture.formats = micfil->soc->formats;
+
        ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component,
                                              &fsl_micfil_dai, 1);
        if (ret) {
index bac825c..053caba 100644 (file)
 #define REG_MICFIL_VAD0_ZCD            0xA8
 
 /* MICFIL Control Register 1 -- REG_MICFILL_CTRL1 0x00 */
-#define MICFIL_CTRL1_MDIS_SHIFT                31
-#define MICFIL_CTRL1_MDIS_MASK         BIT(MICFIL_CTRL1_MDIS_SHIFT)
-#define MICFIL_CTRL1_MDIS              BIT(MICFIL_CTRL1_MDIS_SHIFT)
-#define MICFIL_CTRL1_DOZEN_SHIFT       30
-#define MICFIL_CTRL1_DOZEN_MASK                BIT(MICFIL_CTRL1_DOZEN_SHIFT)
-#define MICFIL_CTRL1_DOZEN             BIT(MICFIL_CTRL1_DOZEN_SHIFT)
-#define MICFIL_CTRL1_PDMIEN_SHIFT      29
-#define MICFIL_CTRL1_PDMIEN_MASK       BIT(MICFIL_CTRL1_PDMIEN_SHIFT)
-#define MICFIL_CTRL1_PDMIEN            BIT(MICFIL_CTRL1_PDMIEN_SHIFT)
-#define MICFIL_CTRL1_DBG_SHIFT         28
-#define MICFIL_CTRL1_DBG_MASK          BIT(MICFIL_CTRL1_DBG_SHIFT)
-#define MICFIL_CTRL1_DBG               BIT(MICFIL_CTRL1_DBG_SHIFT)
-#define MICFIL_CTRL1_SRES_SHIFT                27
-#define MICFIL_CTRL1_SRES_MASK         BIT(MICFIL_CTRL1_SRES_SHIFT)
-#define MICFIL_CTRL1_SRES              BIT(MICFIL_CTRL1_SRES_SHIFT)
-#define MICFIL_CTRL1_DBGE_SHIFT                26
-#define MICFIL_CTRL1_DBGE_MASK         BIT(MICFIL_CTRL1_DBGE_SHIFT)
-#define MICFIL_CTRL1_DBGE              BIT(MICFIL_CTRL1_DBGE_SHIFT)
-#define MICFIL_CTRL1_DISEL_SHIFT       24
-#define MICFIL_CTRL1_DISEL_WIDTH       2
-#define MICFIL_CTRL1_DISEL_MASK                ((BIT(MICFIL_CTRL1_DISEL_WIDTH) - 1) \
-                                        << MICFIL_CTRL1_DISEL_SHIFT)
-#define MICFIL_CTRL1_DISEL(v)          (((v) << MICFIL_CTRL1_DISEL_SHIFT) \
-                                        & MICFIL_CTRL1_DISEL_MASK)
-#define MICFIL_CTRL1_ERREN_SHIFT       23
-#define MICFIL_CTRL1_ERREN_MASK                BIT(MICFIL_CTRL1_ERREN_SHIFT)
-#define MICFIL_CTRL1_ERREN             BIT(MICFIL_CTRL1_ERREN_SHIFT)
-#define MICFIL_CTRL1_CHEN_SHIFT                0
-#define MICFIL_CTRL1_CHEN_WIDTH                8
-#define MICFIL_CTRL1_CHEN_MASK(x)      (BIT(x) << MICFIL_CTRL1_CHEN_SHIFT)
-#define MICFIL_CTRL1_CHEN(x)           (MICFIL_CTRL1_CHEN_MASK(x))
+#define MICFIL_CTRL1_MDIS              BIT(31)
+#define MICFIL_CTRL1_DOZEN             BIT(30)
+#define MICFIL_CTRL1_PDMIEN            BIT(29)
+#define MICFIL_CTRL1_DBG               BIT(28)
+#define MICFIL_CTRL1_SRES              BIT(27)
+#define MICFIL_CTRL1_DBGE              BIT(26)
+
+#define MICFIL_CTRL1_DISEL_DISABLE     0
+#define MICFIL_CTRL1_DISEL_DMA         1
+#define MICFIL_CTRL1_DISEL_IRQ         2
+#define MICFIL_CTRL1_DISEL             GENMASK(25, 24)
+#define MICFIL_CTRL1_ERREN             BIT(23)
+#define MICFIL_CTRL1_CHEN(ch)          BIT(ch)
 
 /* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */
 #define MICFIL_CTRL2_QSEL_SHIFT                25
-#define MICFIL_CTRL2_QSEL_WIDTH                3
-#define MICFIL_CTRL2_QSEL_MASK         ((BIT(MICFIL_CTRL2_QSEL_WIDTH) - 1) \
-                                        << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_HIGH_QUALITY            BIT(MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_MEDIUM_QUALITY          (0 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_LOW_QUALITY             (7 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW0_QUALITY           (6 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW1_QUALITY           (5 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW2_QUALITY           (4 << MICFIL_CTRL2_QSEL_SHIFT)
+#define MICFIL_CTRL2_QSEL              GENMASK(27, 25)
+#define MICFIL_QSEL_MEDIUM_QUALITY     0
+#define MICFIL_QSEL_HIGH_QUALITY       1
+#define MICFIL_QSEL_LOW_QUALITY                7
+#define MICFIL_QSEL_VLOW0_QUALITY      6
+#define MICFIL_QSEL_VLOW1_QUALITY      5
+#define MICFIL_QSEL_VLOW2_QUALITY      4
 
-#define MICFIL_CTRL2_CICOSR_SHIFT      16
-#define MICFIL_CTRL2_CICOSR_WIDTH      4
-#define MICFIL_CTRL2_CICOSR_MASK       ((BIT(MICFIL_CTRL2_CICOSR_WIDTH) - 1) \
-                                        << MICFIL_CTRL2_CICOSR_SHIFT)
-#define MICFIL_CTRL2_CICOSR(v)         (((v) << MICFIL_CTRL2_CICOSR_SHIFT) \
-                                        & MICFIL_CTRL2_CICOSR_MASK)
-#define MICFIL_CTRL2_CLKDIV_SHIFT      0
-#define MICFIL_CTRL2_CLKDIV_WIDTH      8
-#define MICFIL_CTRL2_CLKDIV_MASK       ((BIT(MICFIL_CTRL2_CLKDIV_WIDTH) - 1) \
-                                        << MICFIL_CTRL2_CLKDIV_SHIFT)
-#define MICFIL_CTRL2_CLKDIV(v)         (((v) << MICFIL_CTRL2_CLKDIV_SHIFT) \
-                                        & MICFIL_CTRL2_CLKDIV_MASK)
+#define MICFIL_CTRL2_CICOSR            GENMASK(19, 16)
+#define MICFIL_CTRL2_CLKDIV            GENMASK(7, 0)
 
 /* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */
-#define MICFIL_STAT_BSY_FIL_SHIFT      31
-#define MICFIL_STAT_BSY_FIL_MASK       BIT(MICFIL_STAT_BSY_FIL_SHIFT)
-#define MICFIL_STAT_BSY_FIL            BIT(MICFIL_STAT_BSY_FIL_SHIFT)
-#define MICFIL_STAT_FIR_RDY_SHIFT      30
-#define MICFIL_STAT_FIR_RDY_MASK       BIT(MICFIL_STAT_FIR_RDY_SHIFT)
-#define MICFIL_STAT_FIR_RDY            BIT(MICFIL_STAT_FIR_RDY_SHIFT)
-#define MICFIL_STAT_LOWFREQF_SHIFT     29
-#define MICFIL_STAT_LOWFREQF_MASK      BIT(MICFIL_STAT_LOWFREQF_SHIFT)
-#define MICFIL_STAT_LOWFREQF           BIT(MICFIL_STAT_LOWFREQF_SHIFT)
-#define MICFIL_STAT_CHXF_SHIFT(v)      (v)
-#define MICFIL_STAT_CHXF_MASK(v)       BIT(MICFIL_STAT_CHXF_SHIFT(v))
-#define MICFIL_STAT_CHXF(v)            BIT(MICFIL_STAT_CHXF_SHIFT(v))
+#define MICFIL_STAT_BSY_FIL            BIT(31)
+#define MICFIL_STAT_FIR_RDY            BIT(30)
+#define MICFIL_STAT_LOWFREQF           BIT(29)
+#define MICFIL_STAT_CHXF(ch)           BIT(ch)
 
 /* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */
-#define MICFIL_FIFO_CTRL_FIFOWMK_SHIFT 0
-#define MICFIL_FIFO_CTRL_FIFOWMK_WIDTH 3
-#define MICFIL_FIFO_CTRL_FIFOWMK_MASK  ((BIT(MICFIL_FIFO_CTRL_FIFOWMK_WIDTH) - 1) \
-                                        << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT)
-#define MICFIL_FIFO_CTRL_FIFOWMK(v)    (((v) << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) \
-                                        & MICFIL_FIFO_CTRL_FIFOWMK_MASK)
+#define MICFIL_FIFO_CTRL_FIFOWMK       GENMASK(2, 0)
 
 /* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */
-#define MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v)   (v)
-#define MICFIL_FIFO_STAT_FIFOX_OVER_MASK(v)    BIT(MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v))
-#define MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v)  ((v) + 8)
-#define MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(v)   BIT(MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v))
+#define MICFIL_FIFO_STAT_FIFOX_OVER(ch)        BIT(ch)
+#define MICFIL_FIFO_STAT_FIFOX_UNDER(ch)       BIT((ch) + 8)
 
 /* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/
-#define MICFIL_VAD0_CTRL1_CHSEL_SHIFT  24
-#define MICFIL_VAD0_CTRL1_CHSEL_WIDTH  3
-#define MICFIL_VAD0_CTRL1_CHSEL_MASK   ((BIT(MICFIL_VAD0_CTRL1_CHSEL_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL1_CHSEL_SHIFT)
-#define MICFIL_VAD0_CTRL1_CHSEL(v)     (((v) << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) \
-                                        & MICFIL_VAD0_CTRL1_CHSEL_MASK)
-#define MICFIL_VAD0_CTRL1_CICOSR_SHIFT 16
-#define MICFIL_VAD0_CTRL1_CICOSR_WIDTH 4
-#define MICFIL_VAD0_CTRL1_CICOSR_MASK  ((BIT(MICFIL_VAD0_CTRL1_CICOSR_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL1_CICOSR_SHIFT)
-#define MICFIL_VAD0_CTRL1_CICOSR(v)    (((v) << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) \
-                                        & MICFIL_VAD0_CTRL1_CICOSR_MASK)
-#define MICFIL_VAD0_CTRL1_INITT_SHIFT  8
-#define MICFIL_VAD0_CTRL1_INITT_WIDTH  5
-#define MICFIL_VAD0_CTRL1_INITT_MASK   ((BIT(MICFIL_VAD0_CTRL1_INITT_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL1_INITT_SHIFT)
-#define MICFIL_VAD0_CTRL1_INITT(v)     (((v) << MICFIL_VAD0_CTRL1_INITT_SHIFT) \
-                                        & MICFIL_VAD0_CTRL1_INITT_MASK)
-#define MICFIL_VAD0_CTRL1_ST10_SHIFT   4
-#define MICFIL_VAD0_CTRL1_ST10_MASK    BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT)
-#define MICFIL_VAD0_CTRL1_ST10         BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT)
-#define MICFIL_VAD0_CTRL1_ERIE_SHIFT   3
-#define MICFIL_VAD0_CTRL1_ERIE_MASK    BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT)
-#define MICFIL_VAD0_CTRL1_ERIE         BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT)
-#define MICFIL_VAD0_CTRL1_IE_SHIFT     2
-#define MICFIL_VAD0_CTRL1_IE_MASK      BIT(MICFIL_VAD0_CTRL1_IE_SHIFT)
-#define MICFIL_VAD0_CTRL1_IE           BIT(MICFIL_VAD0_CTRL1_IE_SHIFT)
-#define MICFIL_VAD0_CTRL1_RST_SHIFT    1
-#define MICFIL_VAD0_CTRL1_RST_MASK     BIT(MICFIL_VAD0_CTRL1_RST_SHIFT)
-#define MICFIL_VAD0_CTRL1_RST          BIT(MICFIL_VAD0_CTRL1_RST_SHIFT)
-#define MICFIL_VAD0_CTRL1_EN_SHIFT     0
-#define MICFIL_VAD0_CTRL1_EN_MASK      BIT(MICFIL_VAD0_CTRL1_EN_SHIFT)
-#define MICFIL_VAD0_CTRL1_EN           BIT(MICFIL_VAD0_CTRL1_EN_SHIFT)
+#define MICFIL_VAD0_CTRL1_CHSEL                GENMASK(26, 24)
+#define MICFIL_VAD0_CTRL1_CICOSR       GENMASK(19, 16)
+#define MICFIL_VAD0_CTRL1_INITT                GENMASK(12, 8)
+#define MICFIL_VAD0_CTRL1_ST10         BIT(4)
+#define MICFIL_VAD0_CTRL1_ERIE         BIT(3)
+#define MICFIL_VAD0_CTRL1_IE           BIT(2)
+#define MICFIL_VAD0_CTRL1_RST          BIT(1)
+#define MICFIL_VAD0_CTRL1_EN           BIT(0)
 
 /* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/
-#define MICFIL_VAD0_CTRL2_FRENDIS_SHIFT        31
-#define MICFIL_VAD0_CTRL2_FRENDIS_MASK BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRENDIS      BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_PREFEN_SHIFT 30
-#define MICFIL_VAD0_CTRL2_PREFEN_MASK  BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT)
-#define MICFIL_VAD0_CTRL2_PREFEN       BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT)
-#define MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT        28
-#define MICFIL_VAD0_CTRL2_FOUTDIS_MASK BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FOUTDIS      BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRAMET_SHIFT 16
-#define MICFIL_VAD0_CTRL2_FRAMET_WIDTH 6
-#define MICFIL_VAD0_CTRL2_FRAMET_MASK  ((BIT(MICFIL_VAD0_CTRL2_FRAMET_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL2_FRAMET_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRAMET(v)    (((v) << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) \
-                                        & MICFIL_VAD0_CTRL2_FRAMET_MASK)
-#define MICFIL_VAD0_CTRL2_INPGAIN_SHIFT        8
-#define MICFIL_VAD0_CTRL2_INPGAIN_WIDTH        4
-#define MICFIL_VAD0_CTRL2_INPGAIN_MASK ((BIT(MICFIL_VAD0_CTRL2_INPGAIN_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT)
-#define MICFIL_VAD0_CTRL2_INPGAIN(v)   (((v) << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) \
-                                       & MICFIL_VAD0_CTRL2_INPGAIN_MASK)
-#define MICFIL_VAD0_CTRL2_HPF_SHIFT    0
-#define MICFIL_VAD0_CTRL2_HPF_WIDTH    2
-#define MICFIL_VAD0_CTRL2_HPF_MASK     ((BIT(MICFIL_VAD0_CTRL2_HPF_WIDTH) - 1) \
-                                        << MICFIL_VAD0_CTRL2_HPF_SHIFT)
-#define MICFIL_VAD0_CTRL2_HPF(v)       (((v) << MICFIL_VAD0_CTRL2_HPF_SHIFT) \
-                                        & MICFIL_VAD0_CTRL2_HPF_MASK)
+#define MICFIL_VAD0_CTRL2_FRENDIS      BIT(31)
+#define MICFIL_VAD0_CTRL2_PREFEN       BIT(30)
+#define MICFIL_VAD0_CTRL2_FOUTDIS      BIT(28)
+#define MICFIL_VAD0_CTRL2_FRAMET       GENMASK(21, 16)
+#define MICFIL_VAD0_CTRL2_INPGAIN      GENMASK(11, 8)
+#define MICFIL_VAD0_CTRL2_HPF          GENMASK(1, 0)
 
 /* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */
-#define MICFIL_VAD0_SCONFIG_SFILEN_SHIFT       31
-#define MICFIL_VAD0_SCONFIG_SFILEN_MASK                BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SFILEN             BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT       30
-#define MICFIL_VAD0_SCONFIG_SMAXEN_MASK                BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SMAXEN             BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SGAIN_SHIFT                0
-#define MICFIL_VAD0_SCONFIG_SGAIN_WIDTH                4
-#define MICFIL_VAD0_SCONFIG_SGAIN_MASK         ((BIT(MICFIL_VAD0_SCONFIG_SGAIN_WIDTH) - 1) \
-                                               << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SGAIN(v)           (((v) << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) \
-                                                & MICFIL_VAD0_SCONFIG_SGAIN_MASK)
+#define MICFIL_VAD0_SCONFIG_SFILEN             BIT(31)
+#define MICFIL_VAD0_SCONFIG_SMAXEN             BIT(30)
+#define MICFIL_VAD0_SCONFIG_SGAIN              GENMASK(3, 0)
 
 /* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */
-#define MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT      31
-#define MICFIL_VAD0_NCONFIG_NFILAUT_MASK       BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILAUT            BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NMINEN_SHIFT       30
-#define MICFIL_VAD0_NCONFIG_NMINEN_MASK                BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NMINEN             BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NDECEN_SHIFT       29
-#define MICFIL_VAD0_NCONFIG_NDECEN_MASK                BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NDECEN             BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NOREN_SHIFT                28
-#define MICFIL_VAD0_NCONFIG_NOREN              BIT(MICFIL_VAD0_NCONFIG_NOREN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT      8
-#define MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH      5
-#define MICFIL_VAD0_NCONFIG_NFILADJ_MASK       ((BIT(MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH) - 1) \
-                                                << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILADJ(v)         (((v) << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) \
-                                                & MICFIL_VAD0_NCONFIG_NFILADJ_MASK)
-#define MICFIL_VAD0_NCONFIG_NGAIN_SHIFT                0
-#define MICFIL_VAD0_NCONFIG_NGAIN_WIDTH                4
-#define MICFIL_VAD0_NCONFIG_NGAIN_MASK         ((BIT(MICFIL_VAD0_NCONFIG_NGAIN_WIDTH) - 1) \
-                                                << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NGAIN(v)           (((v) << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) \
-                                                & MICFIL_VAD0_NCONFIG_NGAIN_MASK)
+#define MICFIL_VAD0_NCONFIG_NFILAUT            BIT(31)
+#define MICFIL_VAD0_NCONFIG_NMINEN             BIT(30)
+#define MICFIL_VAD0_NCONFIG_NDECEN             BIT(29)
+#define MICFIL_VAD0_NCONFIG_NOREN              BIT(28)
+#define MICFIL_VAD0_NCONFIG_NFILADJ            GENMASK(12, 8)
+#define MICFIL_VAD0_NCONFIG_NGAIN              GENMASK(3, 0)
 
 /* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */
-#define MICFIL_VAD0_ZCD_ZCDTH_SHIFT    16
-#define MICFIL_VAD0_ZCD_ZCDTH_WIDTH    10
-#define MICFIL_VAD0_ZCD_ZCDTH_MASK     ((BIT(MICFIL_VAD0_ZCD_ZCDTH_WIDTH) - 1) \
-                                        << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDTH(v)       (((v) << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)\
-                                        & MICFIL_VAD0_ZCD_ZCDTH_MASK)
-#define MICFIL_VAD0_ZCD_ZCDADJ_SHIFT   8
-#define MICFIL_VAD0_ZCD_ZCDADJ_WIDTH   4
-#define MICFIL_VAD0_ZCD_ZCDADJ_MASK    ((BIT(MICFIL_VAD0_ZCD_ZCDADJ_WIDTH) - 1)\
-                                        << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDADJ(v)      (((v) << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)\
-                                        & MICFIL_VAD0_ZCD_ZCDADJ_MASK)
-#define MICFIL_VAD0_ZCD_ZCDAND_SHIFT   4
-#define MICFIL_VAD0_ZCD_ZCDAND_MASK    BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAND         BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAUT_SHIFT   2
-#define MICFIL_VAD0_ZCD_ZCDAUT_MASK    BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAUT         BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDEN_SHIFT    0
-#define MICFIL_VAD0_ZCD_ZCDEN_MASK     BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDEN          BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT)
+#define MICFIL_VAD0_ZCD_ZCDTH          GENMASK(25, 16)
+#define MICFIL_VAD0_ZCD_ZCDADJ         GENMASK(11, 8)
+#define MICFIL_VAD0_ZCD_ZCDAND         BIT(4)
+#define MICFIL_VAD0_ZCD_ZCDAUT         BIT(2)
+#define MICFIL_VAD0_ZCD_ZCDEN          BIT(0)
 
 /* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */
-#define MICFIL_VAD0_STAT_INITF_SHIFT   31
-#define MICFIL_VAD0_STAT_INITF_MASK    BIT(MICFIL_VAD0_STAT_INITF_SHIFT)
-#define MICFIL_VAD0_STAT_INITF         BIT(MICFIL_VAD0_STAT_INITF_SHIFT)
-#define MICFIL_VAD0_STAT_INSATF_SHIFT  16
-#define MICFIL_VAD0_STAT_INSATF_MASK   BIT(MICFIL_VAD0_STAT_INSATF_SHIFT)
-#define MICFIL_VAD0_STAT_INSATF                BIT(MICFIL_VAD0_STAT_INSATF_SHIFT)
-#define MICFIL_VAD0_STAT_EF_SHIFT      15
-#define MICFIL_VAD0_STAT_EF_MASK       BIT(MICFIL_VAD0_STAT_EF_SHIFT)
-#define MICFIL_VAD0_STAT_EF            BIT(MICFIL_VAD0_STAT_EF_SHIFT)
-#define MICFIL_VAD0_STAT_IF_SHIFT      0
-#define MICFIL_VAD0_STAT_IF_MASK       BIT(MICFIL_VAD0_STAT_IF_SHIFT)
-#define MICFIL_VAD0_STAT_IF            BIT(MICFIL_VAD0_STAT_IF_SHIFT)
+#define MICFIL_VAD0_STAT_INITF         BIT(31)
+#define MICFIL_VAD0_STAT_INSATF                BIT(16)
+#define MICFIL_VAD0_STAT_EF            BIT(15)
+#define MICFIL_VAD0_STAT_IF            BIT(0)
 
 /* MICFIL Output Control Register */
 #define MICFIL_OUTGAIN_CHX_SHIFT(v)    (4 * (v))
 
 /* Constants */
-#define MICFIL_DMA_IRQ_DISABLED(v)     ((v) & MICFIL_CTRL1_DISEL_MASK)
-#define MICFIL_DMA_ENABLED(v)          ((0x1 << MICFIL_CTRL1_DISEL_SHIFT) \
-                                        == ((v) & MICFIL_CTRL1_DISEL_MASK))
-#define MICFIL_IRQ_ENABLED(v)          ((0x2 << MICFIL_CTRL1_DISEL_SHIFT) \
-                                        == ((v) & MICFIL_CTRL1_DISEL_MASK))
 #define MICFIL_OUTPUT_CHANNELS         8
 #define MICFIL_FIFO_NUM                        8
 
 #define MICFIL_SLEEP_MIN               90000 /* in us */
 #define MICFIL_SLEEP_MAX               100000 /* in us */
 #define MICFIL_DMA_MAXBURST_RX         6
-#define MICFIL_CTRL2_OSR_DEFAULT       (0 << MICFIL_CTRL2_CICOSR_SHIFT)
 
 #endif /* _FSL_MICFIL_H */
index ffc24af..fa950dd 100644 (file)
@@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
        /* Select MCLK direction */
        if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
-           sai->verid.version >= 0x0301) {
+           sai->soc_data->max_register >= FSL_SAI_MCTL) {
                regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
                                   FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
        }
@@ -1203,6 +1203,7 @@ static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
        .reg_offset = 0,
        .mclk0_is_mclk1 = false,
        .flags = 0,
+       .max_register = FSL_SAI_RMR,
 };
 
 static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
@@ -1213,6 +1214,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
        .reg_offset = 0,
        .mclk0_is_mclk1 = true,
        .flags = 0,
+       .max_register = FSL_SAI_RMR,
 };
 
 static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
@@ -1223,6 +1225,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
        .reg_offset = 8,
        .mclk0_is_mclk1 = false,
        .flags = PMQOS_CPU_LATENCY,
+       .max_register = FSL_SAI_RMR,
 };
 
 static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
@@ -1233,6 +1236,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
        .reg_offset = 8,
        .mclk0_is_mclk1 = false,
        .flags = 0,
+       .max_register = FSL_SAI_RMR,
 };
 
 static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
@@ -1243,6 +1247,40 @@ static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
        .reg_offset = 0,
        .mclk0_is_mclk1 = false,
        .flags = 0,
+       .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = {
+       .use_imx_pcm = true,
+       .use_edma = false,
+       .fifo_depth = 128,
+       .reg_offset = 8,
+       .mclk0_is_mclk1 = false,
+       .pins = 8,
+       .flags = 0,
+       .max_register = FSL_SAI_MCTL,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = {
+       .use_imx_pcm = true,
+       .use_edma = false,
+       .fifo_depth = 128,
+       .reg_offset = 8,
+       .mclk0_is_mclk1 = false,
+       .pins = 8,
+       .flags = 0,
+       .max_register = FSL_SAI_MDIV,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = {
+       .use_imx_pcm = true,
+       .use_edma = true,
+       .fifo_depth = 16,
+       .reg_offset = 8,
+       .mclk0_is_mclk1 = false,
+       .pins = 4,
+       .flags = PMQOS_CPU_LATENCY,
+       .max_register = FSL_SAI_RTCAP,
 };
 
 static const struct of_device_id fsl_sai_ids[] = {
@@ -1252,6 +1290,9 @@ static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
        { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
        { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data },
+       { .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data },
+       { .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data },
+       { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fsl_sai_ids);
index 7310fd0..e4965ef 100644 (file)
@@ -223,6 +223,7 @@ struct fsl_sai_soc_data {
        unsigned int pins;
        unsigned int reg_offset;
        unsigned int flags;
+       unsigned int max_register;
 };
 
 /**
index ca30a4e..84cb36d 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/dma/imx-dma.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -214,6 +215,7 @@ struct fsl_ssi_soc_data {
  * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
  * @use_dma: DMA is used or FIQ with stream filter
  * @use_dual_fifo: DMA with support for dual FIFO mode
+ * @use_dyna_fifo: DMA with support for multi FIFO script
  * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
  * @fifo_depth: Depth of the SSI FIFOs
  * @slot_width: Width of each DAI slot
@@ -243,6 +245,7 @@ struct fsl_ssi_soc_data {
  * @dma_maxburst: Max number of words to transfer in one go. So far,
  *                this is always the same as fifo_watermark.
  * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
+ * @audio_config: configure for dma multi fifo script
  */
 struct fsl_ssi {
        struct regmap *regs;
@@ -255,6 +258,7 @@ struct fsl_ssi {
        bool synchronous;
        bool use_dma;
        bool use_dual_fifo;
+       bool use_dyna_fifo;
        bool has_ipg_clk_name;
        unsigned int fifo_depth;
        unsigned int slot_width;
@@ -287,6 +291,7 @@ struct fsl_ssi {
        u32 dma_maxburst;
 
        struct mutex ac97_reg_lock;
+       struct sdma_peripheral_config audio_config[2];
 };
 
 /*
@@ -643,7 +648,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
         * task from fifo0, fifo1 would be neglected at the end of each
         * period. But SSI would still access fifo1 with an invalid data.
         */
-       if (ssi->use_dual_fifo)
+       if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
                snd_pcm_hw_constraint_step(substream->runtime, 0,
                                           SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
 
@@ -802,6 +807,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
 {
        bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+       struct fsl_ssi_regvals *vals = ssi->regvals;
        struct regmap *regs = ssi->regs;
        unsigned int channels = params_channels(hw_params);
        unsigned int sample_size = params_width(hw_params);
@@ -856,6 +862,28 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        tx2 = tx || ssi->synchronous;
        regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
 
+       if (ssi->use_dyna_fifo) {
+               if (channels == 1) {
+                       ssi->audio_config[0].n_fifos_dst = 1;
+                       ssi->audio_config[1].n_fifos_src = 1;
+                       vals[RX].srcr &= ~SSI_SRCR_RFEN1;
+                       vals[TX].stcr &= ~SSI_STCR_TFEN1;
+                       vals[RX].scr  &= ~SSI_SCR_TCH_EN;
+                       vals[TX].scr  &= ~SSI_SCR_TCH_EN;
+               } else {
+                       ssi->audio_config[0].n_fifos_dst = 2;
+                       ssi->audio_config[1].n_fifos_src = 2;
+                       vals[RX].srcr |= SSI_SRCR_RFEN1;
+                       vals[TX].stcr |= SSI_STCR_TFEN1;
+                       vals[RX].scr  |= SSI_SCR_TCH_EN;
+                       vals[TX].scr  |= SSI_SCR_TCH_EN;
+               }
+               ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0];
+               ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]);
+               ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1];
+               ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]);
+       }
+
        return 0;
 }
 
@@ -1353,7 +1381,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
        ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
 
        /* Use even numbers to avoid channel swap due to SDMA script design */
-       if (ssi->use_dual_fifo) {
+       if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
                ssi->dma_params_tx.maxburst &= ~0x1;
                ssi->dma_params_rx.maxburst &= ~0x1;
        }
@@ -1446,6 +1474,8 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
        if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
                ssi->use_dual_fifo = true;
 
+       if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
+               ssi->use_dyna_fifo = true;
        /*
         * Backward compatible for older bindings by manually triggering the
         * machine driver's probe(). Use /compatible property, including the
index 1689730..b80c573 100644 (file)
@@ -48,7 +48,7 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
        if (gpio_is_valid(data->jack_gpio)) {
                ret = snd_soc_card_jack_new(rtd->card, "Headphone",
                                            SND_JACK_HEADPHONE | SND_JACK_BTN_0,
-                                           &headset_jack, NULL, 0);
+                                           &headset_jack);
                if (ret)
                        return ret;
 
index 929f69b..a780cf5 100644 (file)
@@ -78,8 +78,9 @@ static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        data->hdmi_jack_pin.pin = "HDMI Jack";
        data->hdmi_jack_pin.mask = SND_JACK_LINEOUT;
        /* enable jack detection */
-       ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &data->hdmi_jack, &data->hdmi_jack_pin, 1);
+       ret = snd_soc_card_jack_new_pins(card, "HDMI Jack", SND_JACK_LINEOUT,
+                                        &data->hdmi_jack,
+                                        &data->hdmi_jack_pin, 1);
        if (ret) {
                dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
                return ret;
@@ -126,6 +127,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data) {
                ret = -ENOMEM;
+               put_device(&cpu_pdev->dev);
                goto fail;
        }
 
@@ -205,8 +207,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
        }
 
 fail:
-       if (cpu_np)
-               of_node_put(cpu_np);
+       of_node_put(cpu_np);
 
        return ret;
 }
index 5c6cf1c..06b25f4 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef _IMX_PCM_H
 #define _IMX_PCM_H
 
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 
 /*
  * Do not change this as the FIQ handler depends on this size
index 8daced4..580a0d9 100644 (file)
@@ -120,19 +120,19 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data) {
                ret = -ENOMEM;
-               goto fail;
+               goto put_device;
        }
 
        comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
        if (!comp) {
                ret = -ENOMEM;
-               goto fail;
+               goto put_device;
        }
 
        data->codec_clk = clk_get(&codec_dev->dev, NULL);
        if (IS_ERR(data->codec_clk)) {
                ret = PTR_ERR(data->codec_clk);
-               goto fail;
+               goto put_device;
        }
 
        data->clk_frequency = clk_get_rate(data->codec_clk);
@@ -158,10 +158,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        data->card.dev = &pdev->dev;
        ret = snd_soc_of_parse_card_name(&data->card, "model");
        if (ret)
-               goto fail;
+               goto put_device;
        ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
        if (ret)
-               goto fail;
+               goto put_device;
        data->card.num_links = 1;
        data->card.owner = THIS_MODULE;
        data->card.dai_link = &data->dai;
@@ -174,7 +174,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
        if (ret) {
                dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
-               goto fail;
+               goto put_device;
        }
 
        of_node_put(ssi_np);
@@ -182,6 +182,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
 
        return 0;
 
+put_device:
+       put_device(&codec_dev->dev);
 fail:
        if (data && !IS_ERR(data->codec_clk))
                clk_put(data->codec_clk);
index 19cd093..2d30d82 100644 (file)
 #define DRV_NAME "imx-ssi"
 
 #include <linux/dmaengine.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
 #include <sound/dmaengine_pcm.h>
 #include "imx-pcm.h"
 
index c0f3907..77ac405 100644 (file)
@@ -711,7 +711,7 @@ static void graph_link_init(struct asoc_simple_priv *priv,
         */
        daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame);
        if (is_cpu_node)
-               daiclk = snd_soc_daifmt_clock_provider_fliped(daiclk);
+               daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk);
 
        dai_link->dai_fmt       = daifmt | daiclk;
        dai_link->init          = asoc_simple_dai_init;
@@ -1178,8 +1178,6 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
        struct link_info *li;
        int ret;
 
-       dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n");
-
        li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
        if (!li)
                return -ENOMEM;
@@ -1245,6 +1243,9 @@ err:
        if (ret < 0)
                dev_err_probe(dev, ret, "parse error\n");
 
+       if (ret == 0)
+               dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n");
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(audio_graph2_parse_of);
index da0c278..539d7f0 100644 (file)
@@ -721,9 +721,8 @@ int asoc_simple_init_jack(struct snd_soc_card *card,
                sjack->gpio.invert      = !!(flags & OF_GPIO_ACTIVE_LOW);
                sjack->gpio.debounce_time = 150;
 
-               snd_soc_card_jack_new(card, pin_name, mask,
-                                     &sjack->jack,
-                                     &sjack->pin, 1);
+               snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
+                                          &sjack->pin, 1);
 
                snd_soc_jack_add_gpios(&sjack->jack, 1,
                                       &sjack->gpio);
index f1f36f1..09d23b1 100644 (file)
@@ -342,11 +342,9 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
        chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
 
-       ret = pm_runtime_get_sync(i2s->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(i2s->dev);
+       ret = pm_runtime_resume_and_get(i2s->dev);
+       if (ret < 0)
                return ret;
-       }
 
        for (i = 0; i < i2s->active_channels; i++)
                img_i2s_in_ch_disable(i2s, i);
index 800f247..cd6a6a8 100644 (file)
@@ -162,11 +162,9 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                return -EINVAL;
        }
 
-       ret = pm_runtime_get_sync(prl->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(prl->dev);
+       ret = pm_runtime_resume_and_get(prl->dev);
+       if (ret < 0)
                return ret;
-       }
 
        reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
        reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
index 95914d0..a79d1cc 100644 (file)
@@ -749,11 +749,9 @@ static int img_spdif_in_probe(struct platform_device *pdev)
                if (ret)
                        goto err_pm_disable;
        }
-       ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
                goto err_suspend;
-       }
 
        rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
        if (IS_ERR(rst)) {
index c3189d9..f7062eb 100644 (file)
@@ -362,11 +362,9 @@ static int img_spdif_out_probe(struct platform_device *pdev)
                if (ret)
                        goto err_pm_disable;
        }
-       ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret < 0) {
-               pm_runtime_put_noidle(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
                goto err_suspend;
-       }
 
        img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
                             IMG_SPDIF_OUT_CTL);
index d025ca0..7c85d1b 100644 (file)
@@ -211,11 +211,16 @@ config SND_SOC_INTEL_KEEMBAY
 
 config SND_SOC_INTEL_AVS
        tristate "Intel AVS driver"
-       depends on PCI && ACPI
+       depends on X86 || COMPILE_TEST
+       depends on PCI
        depends on COMMON_CLK
-       select SND_SOC_ACPI
+       select SND_SOC_ACPI if ACPI
+       select SND_SOC_TOPOLOGY
+       select SND_HDA
        select SND_HDA_EXT_CORE
        select SND_HDA_DSP_LOADER
+       select SND_INTEL_DSP_CONFIG
+       select WANT_DEV_COREDUMP
        help
          Enable support for Intel(R) cAVS 1.5 platforms with DSP
          capabilities. This includes Skylake, Kabylake, Amberlake and
index e21e11d..3a42d68 100644 (file)
@@ -360,7 +360,6 @@ void sst_context_cleanup(struct intel_sst_drv *ctx)
        sst_unregister(ctx->dev);
        sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
        sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
-       flush_scheduled_work();
        destroy_workqueue(ctx->post_msg_wq);
        cpu_latency_qos_remove_request(ctx->qos);
        kfree(ctx->fw_sg_list.src);
index 0af618d..dc31c2c 100644 (file)
@@ -136,11 +136,10 @@ static int sst_power_control(struct device *dev, bool state)
        int usage_count = 0;
 
        if (state) {
-               ret = pm_runtime_get_sync(dev);
+               ret = pm_runtime_resume_and_get(dev);
                usage_count = GET_USAGE_COUNT(dev);
                dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
                if (ret < 0) {
-                       pm_runtime_put_sync(dev);
                        dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
                        return ret;
                }
@@ -193,11 +192,9 @@ static int sst_cdev_open(struct device *dev,
        struct stream_info *stream;
        struct intel_sst_drv *ctx = dev_get_drvdata(dev);
 
-       retval = pm_runtime_get_sync(ctx->dev);
-       if (retval < 0) {
-               pm_runtime_put_sync(ctx->dev);
+       retval = pm_runtime_resume_and_get(ctx->dev);
+       if (retval < 0)
                return retval;
-       }
 
        str_id = sst_get_stream(ctx, str_params);
        if (str_id > 0) {
@@ -645,11 +642,9 @@ static int sst_send_byte_stream(struct device *dev,
 
        if (NULL == bytes)
                return -EINVAL;
-       ret_val = pm_runtime_get_sync(ctx->dev);
-       if (ret_val < 0) {
-               pm_runtime_put_sync(ctx->dev);
+       ret_val = pm_runtime_resume_and_get(ctx->dev);
+       if (ret_val < 0)
                return ret_val;
-       }
 
        ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
        sst_pm_runtime_put(ctx);
index f842bfc..b6b93ae 100644 (file)
@@ -1,6 +1,12 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
+snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
+                   topology.o path.o pcm.o board_selection.o
 snd-soc-avs-objs += cldma.o
+snd-soc-avs-objs += skl.o apl.o
+
+snd-soc-avs-objs += trace.o
+# tell define_trace.h where to find the trace header
+CFLAGS_trace.o := -I$(src)
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
new file mode 100644 (file)
index 0000000..b8e2b23
--- /dev/null
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+#include "path.h"
+#include "topology.h"
+
+static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                          u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+       struct apl_log_state_info *info;
+       u32 size, num_cores = adev->hw_cfg.dsp_cores;
+       int ret, i;
+
+       if (fls_long(resource_mask) > num_cores)
+               return -EINVAL;
+       size = struct_size(info, logs_core, num_cores);
+       info = kzalloc(size, GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->aging_timer_period = aging_period;
+       info->fifo_full_timer_period = fifo_full_period;
+       info->core_mask = resource_mask;
+       if (enable)
+               for_each_set_bit(i, &resource_mask, num_cores) {
+                       info->logs_core[i].enable = enable;
+                       info->logs_core[i].min_priority = *priorities++;
+               }
+       else
+               for_each_set_bit(i, &resource_mask, num_cores)
+                       info->logs_core[i].enable = enable;
+
+       ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+       kfree(info);
+       if (ret)
+               return AVS_IPC_RET(ret);
+
+       return 0;
+}
+
+static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+       struct apl_log_buffer_layout layout;
+       unsigned long flags;
+       void __iomem *addr, *buf;
+
+       addr = avs_log_buffer_addr(adev, msg->log.core);
+       if (!addr)
+               return -ENXIO;
+
+       memcpy_fromio(&layout, addr, sizeof(layout));
+
+       spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+       if (!kfifo_initialized(&adev->dbg.trace_fifo))
+               /* consume the logs regardless of consumer presence */
+               goto update_read_ptr;
+
+       buf = apl_log_payload_addr(addr);
+
+       if (layout.read_ptr > layout.write_ptr) {
+               __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+                                     apl_log_payload_size(adev) - layout.read_ptr,
+                                     &adev->dbg.fifo_lock);
+               layout.read_ptr = 0;
+       }
+       __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+                             layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
+
+       wake_up(&adev->dbg.trace_waitq);
+
+update_read_ptr:
+       spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+       writel(layout.write_ptr, addr);
+       return 0;
+}
+
+static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout)
+{
+       unsigned long timeout;
+       void __iomem *addr;
+
+       addr = avs_log_buffer_addr(adev, core);
+       if (!addr)
+               return -ENXIO;
+
+       timeout = jiffies + msecs_to_jiffies(10);
+
+       do {
+               memcpy_fromio(layout, addr, sizeof(*layout));
+               if (layout->read_ptr != layout->write_ptr)
+                       return 0;
+               usleep_range(500, 1000);
+       } while (!time_after(jiffies, timeout));
+
+       return -ETIMEDOUT;
+}
+
+/* reads log header and tests its type */
+#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+
+static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+       struct apl_log_buffer_layout layout;
+       void __iomem *addr, *buf;
+       size_t dump_size;
+       u16 offset = 0;
+       u8 *dump, *pos;
+
+       dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size;
+       dump = vzalloc(dump_size);
+       if (!dump)
+               return -ENOMEM;
+
+       memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+       if (!msg->ext.coredump.stack_dump_size)
+               goto exit;
+
+       /* Dump the registers even if an external error prevents gathering the stack. */
+       addr = avs_log_buffer_addr(adev, msg->ext.coredump.core_id);
+       if (!addr)
+               goto exit;
+
+       buf = apl_log_payload_addr(addr);
+       memcpy_fromio(&layout, addr, sizeof(layout));
+       if (!apl_is_entry_stackdump(buf + layout.read_ptr)) {
+               /*
+                * DSP awaits the remaining logs to be
+                * gathered before dumping stack
+                */
+               msg->log.core = msg->ext.coredump.core_id;
+               avs_dsp_op(adev, log_buffer_status, msg);
+       }
+
+       pos = dump + AVS_FW_REGS_SIZE;
+       /* gather the stack */
+       do {
+               u32 count;
+
+               if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+                       break;
+
+               if (layout.read_ptr > layout.write_ptr) {
+                       count = apl_log_payload_size(adev) - layout.read_ptr;
+                       memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+                       layout.read_ptr = 0;
+                       offset += count;
+               }
+               count = layout.write_ptr - layout.read_ptr;
+               memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+               offset += count;
+
+               /* update read pointer */
+               writel(layout.write_ptr, addr);
+       } while (offset < msg->ext.coredump.stack_dump_size);
+
+exit:
+       dev_coredumpv(adev->dev, dump, dump_size, GFP_KERNEL);
+
+       return 0;
+}
+
+static bool apl_lp_streaming(struct avs_dev *adev)
+{
+       struct avs_path *path;
+
+       /* Any gateway without buffer allocated in LP area disqualifies D0IX. */
+       list_for_each_entry(path, &adev->path_list, node) {
+               struct avs_path_pipeline *ppl;
+
+               list_for_each_entry(ppl, &path->ppl_list, node) {
+                       struct avs_path_module *mod;
+
+                       list_for_each_entry(mod, &ppl->mod_list, node) {
+                               struct avs_tplg_modcfg_ext *cfg;
+
+                               cfg = mod->template->cfg_ext;
+
+                               /* only copiers have gateway attributes */
+                               if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
+                                       continue;
+                               /* non-gateway copiers do not prevent PG */
+                               if (cfg->copier.dma_type == INVALID_OBJECT_ID)
+                                       continue;
+
+                               if (!mod->gtw_attrs.lp_buffer_alloc)
+                                       return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+       /* wake in all cases */
+       if (wake)
+               return true;
+
+       /*
+        * If no pipelines are running, allow for d0ix schedule.
+        * If all gateways have lp=1, allow for d0ix schedule.
+        * If any gateway with lp=0 is allocated, abort scheduling d0ix.
+        *
+        * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
+        * not the power-gating mechanism known from cAVS 2.0.
+        */
+       return apl_lp_streaming(adev);
+}
+
+static int apl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+       bool streaming = false;
+       int ret;
+
+       if (enable)
+               /* Either idle or all gateways with lp=1. */
+               streaming = !list_empty(&adev->path_list);
+
+       ret = avs_ipc_set_d0ix(adev, enable, streaming);
+       return AVS_IPC_RET(ret);
+}
+
+const struct avs_dsp_ops apl_dsp_ops = {
+       .power = avs_dsp_core_power,
+       .reset = avs_dsp_core_reset,
+       .stall = avs_dsp_core_stall,
+       .irq_handler = avs_dsp_irq_handler,
+       .irq_thread = avs_dsp_irq_thread,
+       .int_control = avs_dsp_interrupt_control,
+       .load_basefw = avs_hda_load_basefw,
+       .load_lib = avs_hda_load_library,
+       .transfer_mods = avs_hda_transfer_modules,
+       .enable_logs = apl_enable_logs,
+       .log_buffer_offset = skl_log_buffer_offset,
+       .log_buffer_status = apl_log_buffer_status,
+       .coredump = apl_coredump,
+       .d0ix_toggle = apl_d0ix_toggle,
+       .set_d0ix = apl_set_d0ix,
+};
index b48a342..92e3772 100644 (file)
 
 #include <linux/device.h>
 #include <linux/firmware.h>
+#include <linux/kfifo.h>
 #include <sound/hda_codec.h>
 #include <sound/hda_register.h>
+#include <sound/soc-component.h>
 #include "messages.h"
 #include "registers.h"
 
 struct avs_dev;
+struct avs_tplg;
+struct avs_tplg_library;
+struct avs_soc_component;
+struct avs_ipc_msg;
 
 /*
  * struct avs_dsp_ops - Platform-specific DSP operations
@@ -38,11 +44,21 @@ struct avs_dsp_ops {
        int (* const load_basefw)(struct avs_dev *, struct firmware *);
        int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
        int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+       int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
+                                 u32 *);
+       int (* const log_buffer_offset)(struct avs_dev *, u32);
+       int (* const log_buffer_status)(struct avs_dev *, union avs_notify_msg *);
+       int (* const coredump)(struct avs_dev *, union avs_notify_msg *);
+       bool (* const d0ix_toggle)(struct avs_dev *, struct avs_ipc_msg *, bool);
+       int (* const set_d0ix)(struct avs_dev *, bool);
 };
 
 #define avs_dsp_op(adev, op, ...) \
        ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
 
+extern const struct avs_dsp_ops skl_dsp_ops;
+extern const struct avs_dsp_ops apl_dsp_ops;
+
 #define AVS_PLATATTR_CLDMA             BIT_ULL(0)
 #define AVS_PLATATTR_IMR               BIT_ULL(1)
 
@@ -70,6 +86,16 @@ struct avs_fw_entry {
        struct list_head node;
 };
 
+struct avs_debug {
+       struct kfifo trace_fifo;
+       spinlock_t fifo_lock;   /* serialize I/O for trace_fifo */
+       spinlock_t trace_lock;  /* serialize debug window I/O between each LOG_BUFFER_STATUS */
+       wait_queue_head_t trace_waitq;
+       u32 aging_timer_period;
+       u32 fifo_full_timer_period;
+       u32 logged_resources;   /* context dependent: core or library */
+};
+
 /*
  * struct avs_dev - Intel HD-Audio driver data
  *
@@ -103,6 +129,16 @@ struct avs_dev {
        char **lib_names;
 
        struct completion fw_ready;
+       struct work_struct probe_work;
+
+       struct nhlt_acpi_table *nhlt;
+       struct list_head comp_list;
+       struct mutex comp_list_mutex;
+       struct list_head path_list;
+       spinlock_t path_list_lock;
+       struct mutex path_mutex;
+
+       struct avs_debug dbg;
 };
 
 /* from hda_bus to avs_dev */
@@ -153,12 +189,18 @@ struct avs_ipc {
        struct avs_ipc_msg rx;
        u32 default_timeout_ms;
        bool ready;
+       atomic_t recovering;
 
        bool rx_completed;
        spinlock_t rx_lock;
        struct mutex msg_mutex;
        struct completion done_completion;
        struct completion busy_completion;
+
+       struct work_struct recovery_work;
+       struct delayed_work d0ix_work;
+       atomic_t d0ix_disable_depth;
+       bool in_d0ix;
 };
 
 #define AVS_EIPC       EREMOTEIO
@@ -195,6 +237,11 @@ int avs_dsp_send_msg_timeout(struct avs_dev *adev,
                             struct avs_ipc_msg *reply, int timeout);
 int avs_dsp_send_msg(struct avs_dev *adev,
                     struct avs_ipc_msg *request, struct avs_ipc_msg *reply);
+/* Two variants below are for messages that control DSP power states. */
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+                               struct avs_ipc_msg *reply, int timeout, bool wake_d0i0);
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+                       struct avs_ipc_msg *reply, bool wake_d0i0);
 int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev,
                                 struct avs_ipc_msg *request, int timeout);
 int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request);
@@ -202,6 +249,11 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable);
 int avs_ipc_init(struct avs_ipc *ipc, struct device *dev);
 void avs_ipc_block(struct avs_ipc *ipc);
 
+int avs_dsp_disable_d0ix(struct avs_dev *adev);
+int avs_dsp_enable_d0ix(struct avs_dev *adev);
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+
 /* Firmware resources management */
 
 int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
@@ -232,6 +284,7 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable);
 void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable);
 void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
 
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs);
 int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
 int avs_dsp_first_boot_firmware(struct avs_dev *adev);
 
@@ -244,4 +297,53 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
 int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
                             struct avs_module_entry *mods, u32 num_mods);
 
+/* Soc component members */
+
+struct avs_soc_component {
+       struct snd_soc_component base;
+       struct avs_tplg *tplg;
+
+       struct list_head node;
+};
+
+#define to_avs_soc_component(comp) \
+       container_of(comp, struct avs_soc_component, base)
+
+extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+                             unsigned long *tdms);
+int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+
+int avs_register_all_boards(struct avs_dev *adev);
+void avs_unregister_all_boards(struct avs_dev *adev);
+
+/* Firmware tracing helpers */
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+                                  spinlock_t *lock);
+
+#define avs_log_buffer_size(adev) \
+       ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+       s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+       (__offset < 0) ? NULL : \
+                        (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+struct apl_log_buffer_layout {
+       u32 read_ptr;
+       u32 write_ptr;
+       u8 buffer[];
+} __packed;
+
+#define apl_log_payload_size(adev) \
+       (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout))
+
+#define apl_log_payload_addr(addr) \
+       (addr + sizeof(struct apl_log_buffer_layout))
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644 (file)
index 0000000..80cb016
--- /dev/null
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+
+static bool i2s_test;
+module_param(i2s_test, bool, 0444);
+MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards");
+
+static const struct dmi_system_id kbl_dmi_table[] = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
+               },
+       },
+       {}
+};
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+               },
+       },
+       {}
+};
+
+static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
+{
+       struct snd_soc_acpi_mach *mach = arg;
+       const struct dmi_system_id *dmi_id;
+       struct dmi_system_id *dmi_table;
+
+       if (mach->quirk_data == NULL)
+               return mach;
+
+       dmi_table = (struct dmi_system_id *)mach->quirk_data;
+
+       dmi_id = dmi_first_match(dmi_table);
+       if (!dmi_id)
+               return NULL;
+
+       return mach;
+}
+
+#define AVS_SSP(x)             (BIT(x))
+#define AVS_SSP_RANGE(a, b)    (GENMASK(b, a))
+
+/* supported I2S board codec configurations */
+static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt286",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "rt286-tplg.bin",
+       },
+       {
+               .id = "10508825",
+               .drv_name = "avs_nau8825",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "nau8825-tplg.bin",
+       },
+       {
+               .id = "INT343B",
+               .drv_name = "avs_ssm4567",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "ssm4567-tplg.bin",
+       },
+       {
+               .id = "MX98357A",
+               .drv_name = "avs_max98357a",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "max98357a-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt286",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .quirk_data = &kbl_dmi_table,
+               .machine_quirk = dmi_match_quirk,
+               .tplg_filename = "rt286-tplg.bin",
+       },
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt298",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .quirk_data = &kblr_dmi_table,
+               .machine_quirk = dmi_match_quirk,
+               .tplg_filename = "rt298-tplg.bin",
+       },
+       {
+               .id = "MX98373",
+               .drv_name = "avs_max98373",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "max98373-tplg.bin",
+       },
+       {
+               .id = "DLGS7219",
+               .drv_name = "avs_da7219",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "da7219-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt298",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(5),
+               },
+               .tplg_filename = "rt298-tplg.bin",
+       },
+       {
+               .id = "INT34C3",
+               .drv_name = "avs_tdf8532",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP_RANGE(0, 5),
+               },
+               .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+               .tplg_filename = "tdf8532-tplg.bin",
+       },
+       {
+               .id = "MX98357A",
+               .drv_name = "avs_max98357a",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(5),
+               },
+               .tplg_filename = "max98357a-tplg.bin",
+       },
+       {
+               .id = "DLGS7219",
+               .drv_name = "avs_da7219",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "da7219-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+       {
+               .id = "INT343A",
+               .drv_name = "avs_rt298",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(2),
+               },
+               .tplg_filename = "rt298-tplg.bin",
+       },
+       {},
+};
+
+static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(0),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(1),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(2),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(3),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(4),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       {
+               .drv_name = "avs_i2s_test",
+               .mach_params = {
+                       .i2s_link_mask = AVS_SSP(5),
+               },
+               .tplg_filename = "i2s-test-tplg.bin",
+       },
+       /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+};
+
+struct avs_acpi_boards {
+       int id;
+       struct snd_soc_acpi_mach *machs;
+};
+
+#define AVS_MACH_ENTRY(_id, _mach) \
+       { .id = (_id), .machs = (_mach), }
+
+/* supported I2S boards per platform */
+static const struct avs_acpi_boards i2s_boards[] = {
+       AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */
+       AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */
+       AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */
+       AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */
+       {},
+};
+
+static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+{
+       int id, i;
+
+       id = adev->base.pci->device;
+       for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
+               if (i2s_boards[i].id == id)
+                       return &i2s_boards[i];
+       return NULL;
+}
+
+/* platform devices owned by AVS audio are removed with this hook */
+static void board_pdev_unregister(void *data)
+{
+       platform_device_unregister(data);
+}
+
+static int avs_register_dmic_board(struct avs_dev *adev)
+{
+       struct platform_device *codec, *board;
+       struct snd_soc_acpi_mach mach = {{0}};
+       int ret;
+
+       if (!adev->nhlt ||
+           !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
+               dev_dbg(adev->dev, "no DMIC endpoints present\n");
+               return 0;
+       }
+
+       codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+       if (IS_ERR(codec)) {
+               dev_err(adev->dev, "dmic codec register failed\n");
+               return PTR_ERR(codec);
+       }
+
+       ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
+       if (ret < 0) {
+               platform_device_unregister(codec);
+               return ret;
+       }
+
+       ret = avs_dmic_platform_register(adev, "dmic-platform");
+       if (ret < 0)
+               return ret;
+
+       mach.tplg_filename = "dmic-tplg.bin";
+       mach.mach_params.platform = "dmic-platform";
+
+       board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
+                                       (const void *)&mach, sizeof(mach));
+       if (IS_ERR(board)) {
+               dev_err(adev->dev, "dmic board register failed\n");
+               return PTR_ERR(board);
+       }
+
+       ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+       if (ret < 0) {
+               platform_device_unregister(board);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+       struct platform_device *board;
+       int num_ssps;
+       char *name;
+       int ret;
+
+       num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+       if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+               dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+                       num_ssps, mach->drv_name, __fls(mach->mach_params.i2s_link_mask));
+               return -ENODEV;
+       }
+
+       name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
+                             mach->mach_params.i2s_link_mask);
+       if (!name)
+               return -ENOMEM;
+
+       ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
+       if (ret < 0)
+               return ret;
+
+       mach->mach_params.platform = name;
+
+       board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
+                                             (const void *)mach, sizeof(*mach));
+       if (IS_ERR(board)) {
+               dev_err(adev->dev, "ssp board register failed\n");
+               return PTR_ERR(board);
+       }
+
+       ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+       if (ret < 0) {
+               platform_device_unregister(board);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+       const struct avs_acpi_boards *boards;
+       struct snd_soc_acpi_mach *mach;
+       int ret;
+
+       if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
+               dev_dbg(adev->dev, "no I2S endpoints present\n");
+               return 0;
+       }
+
+       if (i2s_test) {
+               int i, num_ssps;
+
+               num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+               /* constrain just in case FW says there can be more SSPs than possible */
+               num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
+
+               mach = avs_test_i2s_machines;
+
+               for (i = 0; i < num_ssps; i++) {
+                       ret = avs_register_i2s_board(adev, &mach[i]);
+                       if (ret < 0)
+                               dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
+                                        ret);
+               }
+               return 0;
+       }
+
+       boards = avs_get_i2s_boards(adev);
+       if (!boards) {
+               dev_dbg(adev->dev, "no I2S endpoints supported\n");
+               return 0;
+       }
+
+       for (mach = boards->machs; mach->id[0]; mach++) {
+               if (!acpi_dev_present(mach->id, NULL, -1))
+                       continue;
+
+               if (mach->machine_quirk)
+                       if (!mach->machine_quirk(mach))
+                               continue;
+
+               ret = avs_register_i2s_board(adev, mach);
+               if (ret < 0)
+                       dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
+       }
+
+       return 0;
+}
+
+static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
+{
+       struct snd_soc_acpi_mach mach = {{0}};
+       struct platform_device *board;
+       struct hdac_device *hdev = &codec->core;
+       char *pname;
+       int ret, id;
+
+       pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
+       if (!pname)
+               return -ENOMEM;
+
+       ret = avs_hda_platform_register(adev, pname);
+       if (ret < 0)
+               return ret;
+
+       mach.pdata = codec;
+       mach.mach_params.platform = pname;
+       mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
+                                           hdev->vendor_id);
+       if (!mach.tplg_filename)
+               return -ENOMEM;
+
+       id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
+       board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
+                                             sizeof(mach));
+       if (IS_ERR(board)) {
+               dev_err(adev->dev, "hda board register failed\n");
+               return PTR_ERR(board);
+       }
+
+       ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+       if (ret < 0) {
+               platform_device_unregister(board);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int avs_register_hda_boards(struct avs_dev *adev)
+{
+       struct hdac_bus *bus = &adev->base.core;
+       struct hdac_device *hdev;
+       int ret;
+
+       if (!bus->num_codecs) {
+               dev_dbg(adev->dev, "no HDA endpoints present\n");
+               return 0;
+       }
+
+       list_for_each_entry(hdev, &bus->codec_list, list) {
+               struct hda_codec *codec;
+
+               codec = dev_to_hda_codec(&hdev->dev);
+
+               ret = avs_register_hda_board(adev, codec);
+               if (ret < 0)
+                       dev_warn(adev->dev, "register hda-%08x failed: %d\n",
+                                codec->core.vendor_id, ret);
+       }
+
+       return 0;
+}
+
+int avs_register_all_boards(struct avs_dev *adev)
+{
+       int ret;
+
+       ret = avs_register_dmic_board(adev);
+       if (ret < 0)
+               dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
+                        ret);
+
+       ret = avs_register_i2s_boards(adev);
+       if (ret < 0)
+               dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
+                        ret);
+
+       ret = avs_register_hda_boards(adev);
+       if (ret < 0)
+               dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
+                        ret);
+
+       return 0;
+}
+
+void avs_unregister_all_boards(struct avs_dev *adev)
+{
+       snd_soc_unregister_component(adev->dev);
+}
index a4d063d..3a0997c 100644 (file)
 // foundation of this driver
 //
 
+#include <linux/module.h>
 #include <linux/pci.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
 #include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
 #include "avs.h"
+#include "cldma.h"
 
 static void
 avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
@@ -59,3 +67,626 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
        value = enable ? AZX_VS_EM2_L1SEN : 0;
        snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
 }
+
+static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
+{
+       unsigned int cp_streams, pb_streams;
+       unsigned int gcap;
+
+       gcap = snd_hdac_chip_readw(bus, GCAP);
+       cp_streams = (gcap >> 8) & 0x0F;
+       pb_streams = (gcap >> 12) & 0x0F;
+       bus->num_streams = cp_streams + pb_streams;
+
+       snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+       snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+       return snd_hdac_bus_alloc_stream_pages(bus);
+}
+
+static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+       struct hdac_ext_link *hlink;
+       bool ret;
+
+       avs_hdac_clock_gating_enable(bus, false);
+       ret = snd_hdac_bus_init_chip(bus, full_reset);
+
+       /* Reset stream-to-link mapping */
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
+
+       avs_hdac_clock_gating_enable(bus, true);
+
+       /* Set DUM bit to address incorrect position reporting for capture
+        * streams. In order to do so, CTRL needs to be out of reset state
+        */
+       snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM);
+
+       return ret;
+}
+
+static int probe_codec(struct hdac_bus *bus, int addr)
+{
+       struct hda_codec *codec;
+       unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+                          (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+       unsigned int res = -1;
+       int ret;
+
+       mutex_lock(&bus->cmd_mutex);
+       snd_hdac_bus_send_cmd(bus, cmd);
+       snd_hdac_bus_get_response(bus, addr, &res);
+       mutex_unlock(&bus->cmd_mutex);
+       if (res == -1)
+               return -EIO;
+
+       dev_dbg(bus->dev, "codec #%d probed OK: 0x%x\n", addr, res);
+
+       codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "hdaudioB%dD%d", bus->idx, addr);
+       if (IS_ERR(codec)) {
+               dev_err(bus->dev, "init codec failed: %ld\n", PTR_ERR(codec));
+               return PTR_ERR(codec);
+       }
+       /*
+        * Allow avs_core suspend by forcing suspended state on all
+        * of its codec child devices. Component interested in
+        * dealing with hda codecs directly takes pm responsibilities
+        */
+       pm_runtime_set_suspended(hda_codec_dev(codec));
+
+       /* configure effectively creates new ASoC component */
+       ret = snd_hda_codec_configure(codec);
+       if (ret < 0) {
+               dev_err(bus->dev, "failed to config codec %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
+{
+       int c;
+
+       /* First try to probe all given codec slots */
+       for (c = 0; c < HDA_MAX_CODECS; c++) {
+               if (!(bus->codec_mask & BIT(c)))
+                       continue;
+
+               if (!probe_codec(bus, c))
+                       /* success, continue probing */
+                       continue;
+
+               /*
+                * Some BIOSen give you wrong codec addresses
+                * that don't exist
+                */
+               dev_warn(bus->dev, "Codec #%d probe error; disabling it...\n", c);
+               bus->codec_mask &= ~BIT(c);
+               /*
+                * More badly, accessing to a non-existing
+                * codec often screws up the controller bus,
+                * and disturbs the further communications.
+                * Thus if an error occurs during probing,
+                * better to reset the controller bus to get
+                * back to the sanity state.
+                */
+               snd_hdac_bus_stop_chip(bus);
+               avs_hdac_bus_init_chip(bus, true);
+       }
+}
+
+static void avs_hda_probe_work(struct work_struct *work)
+{
+       struct avs_dev *adev = container_of(work, struct avs_dev, probe_work);
+       struct hdac_bus *bus = &adev->base.core;
+       struct hdac_ext_link *hlink;
+       int ret;
+
+       pm_runtime_set_active(bus->dev); /* clear runtime_error flag */
+
+       ret = snd_hdac_i915_init(bus);
+       if (ret < 0)
+               dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
+
+       snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+       avs_hdac_bus_init_chip(bus, true);
+       avs_hdac_bus_probe_codecs(bus);
+       snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+       /* with all codecs probed, links can be powered down */
+       list_for_each_entry(hlink, &bus->hlink_list, list)
+               snd_hdac_ext_bus_link_put(bus, hlink);
+
+       snd_hdac_ext_bus_ppcap_enable(bus, true);
+       snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+       ret = avs_dsp_first_boot_firmware(adev);
+       if (ret < 0)
+               return;
+
+       adev->nhlt = intel_nhlt_init(adev->dev);
+       if (!adev->nhlt)
+               dev_info(bus->dev, "platform has no NHLT\n");
+
+       avs_register_all_boards(adev);
+
+       /* configure PM */
+       pm_runtime_set_autosuspend_delay(bus->dev, 2000);
+       pm_runtime_use_autosuspend(bus->dev);
+       pm_runtime_mark_last_busy(bus->dev);
+       pm_runtime_put_autosuspend(bus->dev);
+       pm_runtime_allow(bus->dev);
+}
+
+static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size)
+{
+       u64 prev_pos, pos, num_bytes;
+
+       div64_u64_rem(stream->curr_pos, buffer_size, &prev_pos);
+       pos = snd_hdac_stream_get_pos_posbuf(stream);
+
+       if (pos < prev_pos)
+               num_bytes = (buffer_size - prev_pos) +  pos;
+       else
+               num_bytes = pos - prev_pos;
+
+       stream->curr_pos += num_bytes;
+}
+
+/* called from IRQ */
+static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream)
+{
+       if (stream->substream) {
+               snd_pcm_period_elapsed(stream->substream);
+       } else if (stream->cstream) {
+               u64 buffer_size = stream->cstream->runtime->buffer_size;
+
+               hdac_stream_update_pos(stream, buffer_size);
+               snd_compr_fragment_elapsed(stream->cstream);
+       }
+}
+
+static irqreturn_t hdac_bus_irq_handler(int irq, void *context)
+{
+       struct hdac_bus *bus = context;
+       u32 mask, int_enable;
+       u32 status;
+       int ret = IRQ_NONE;
+
+       if (!pm_runtime_active(bus->dev))
+               return ret;
+
+       spin_lock(&bus->reg_lock);
+
+       status = snd_hdac_chip_readl(bus, INTSTS);
+       if (status == 0 || status == UINT_MAX) {
+               spin_unlock(&bus->reg_lock);
+               return ret;
+       }
+
+       /* clear rirb int */
+       status = snd_hdac_chip_readb(bus, RIRBSTS);
+       if (status & RIRB_INT_MASK) {
+               if (status & RIRB_INT_RESPONSE)
+                       snd_hdac_bus_update_rirb(bus);
+               snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+       }
+
+       mask = (0x1 << bus->num_streams) - 1;
+
+       status = snd_hdac_chip_readl(bus, INTSTS);
+       status &= mask;
+       if (status) {
+               /* Disable stream interrupts; Re-enable in bottom half */
+               int_enable = snd_hdac_chip_readl(bus, INTCTL);
+               snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask)));
+               ret = IRQ_WAKE_THREAD;
+       } else {
+               ret = IRQ_HANDLED;
+       }
+
+       spin_unlock(&bus->reg_lock);
+       return ret;
+}
+
+static irqreturn_t hdac_bus_irq_thread(int irq, void *context)
+{
+       struct hdac_bus *bus = context;
+       u32 status;
+       u32 int_enable;
+       u32 mask;
+       unsigned long flags;
+
+       status = snd_hdac_chip_readl(bus, INTSTS);
+
+       snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream);
+
+       /* Re-enable stream interrupts */
+       mask = (0x1 << bus->num_streams) - 1;
+       spin_lock_irqsave(&bus->reg_lock, flags);
+       int_enable = snd_hdac_chip_readl(bus, INTCTL);
+       snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask));
+       spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static int avs_hdac_acquire_irq(struct avs_dev *adev)
+{
+       struct hdac_bus *bus = &adev->base.core;
+       struct pci_dev *pci = to_pci_dev(bus->dev);
+       int ret;
+
+       /* request one and check that we only got one interrupt */
+       ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
+       if (ret != 1) {
+               dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret);
+               return ret;
+       }
+
+       ret = pci_request_irq(pci, 0, hdac_bus_irq_handler, hdac_bus_irq_thread, bus,
+                             KBUILD_MODNAME);
+       if (ret < 0) {
+               dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret);
+               goto free_vector;
+       }
+
+       ret = pci_request_irq(pci, 0, avs_dsp_irq_handler, avs_dsp_irq_thread, adev,
+                             KBUILD_MODNAME);
+       if (ret < 0) {
+               dev_err(adev->dev, "Failed to request IPC IRQ handler: %d\n", ret);
+               goto free_stream_irq;
+       }
+
+       return 0;
+
+free_stream_irq:
+       pci_free_irq(pci, 0, bus);
+free_vector:
+       pci_free_irq_vectors(pci);
+       return ret;
+}
+
+static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct pci_device_id *id)
+{
+       struct hda_bus *bus = &adev->base;
+       struct avs_ipc *ipc;
+       struct device *dev = &pci->dev;
+       int ret;
+
+       ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, NULL);
+       if (ret < 0)
+               return ret;
+
+       bus->core.use_posbuf = 1;
+       bus->core.bdl_pos_adj = 0;
+       bus->core.sync_write = 1;
+       bus->pci = pci;
+       bus->mixer_assigned = -1;
+       mutex_init(&bus->prepare_mutex);
+
+       ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
+       if (!ipc)
+               return -ENOMEM;
+       ret = avs_ipc_init(ipc, dev);
+       if (ret < 0)
+               return ret;
+
+       adev->dev = dev;
+       adev->spec = (const struct avs_spec *)id->driver_data;
+       adev->ipc = ipc;
+       adev->hw_cfg.dsp_cores = hweight_long(AVS_MAIN_CORE_MASK);
+       INIT_WORK(&adev->probe_work, avs_hda_probe_work);
+       INIT_LIST_HEAD(&adev->comp_list);
+       INIT_LIST_HEAD(&adev->path_list);
+       INIT_LIST_HEAD(&adev->fw_list);
+       init_completion(&adev->fw_ready);
+       spin_lock_init(&adev->path_list_lock);
+       mutex_init(&adev->modres_mutex);
+       mutex_init(&adev->comp_list_mutex);
+       mutex_init(&adev->path_mutex);
+
+       return 0;
+}
+
+static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+       struct hdac_bus *bus;
+       struct avs_dev *adev;
+       struct device *dev = &pci->dev;
+       int ret;
+
+       ret = snd_intel_dsp_driver_probe(pci);
+       if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_AVS)
+               return -ENODEV;
+
+       ret = pcim_enable_device(pci);
+       if (ret < 0)
+               return ret;
+
+       adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
+       if (!adev)
+               return -ENOMEM;
+       ret = avs_bus_init(adev, pci, id);
+       if (ret < 0) {
+               dev_err(dev, "failed to init avs bus: %d\n", ret);
+               return ret;
+       }
+
+       ret = pci_request_regions(pci, "AVS HDAudio");
+       if (ret < 0)
+               return ret;
+
+       bus = &adev->base.core;
+       bus->addr = pci_resource_start(pci, 0);
+       bus->remap_addr = pci_ioremap_bar(pci, 0);
+       if (!bus->remap_addr) {
+               dev_err(bus->dev, "ioremap error\n");
+               ret = -ENXIO;
+               goto err_remap_bar0;
+       }
+
+       adev->dsp_ba = pci_ioremap_bar(pci, 4);
+       if (!adev->dsp_ba) {
+               dev_err(bus->dev, "ioremap error\n");
+               ret = -ENXIO;
+               goto err_remap_bar4;
+       }
+
+       snd_hdac_bus_parse_capabilities(bus);
+       if (bus->mlcap)
+               snd_hdac_ext_bus_get_ml_capabilities(bus);
+
+       if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
+               dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+       } else {
+               dma_set_mask(dev, DMA_BIT_MASK(32));
+               dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+       }
+
+       ret = avs_hdac_bus_init_streams(bus);
+       if (ret < 0) {
+               dev_err(dev, "failed to init streams: %d\n", ret);
+               goto err_init_streams;
+       }
+
+       ret = avs_hdac_acquire_irq(adev);
+       if (ret < 0) {
+               dev_err(bus->dev, "failed to acquire irq: %d\n", ret);
+               goto err_acquire_irq;
+       }
+
+       pci_set_master(pci);
+       pci_set_drvdata(pci, bus);
+       device_disable_async_suspend(dev);
+
+       schedule_work(&adev->probe_work);
+
+       return 0;
+
+err_acquire_irq:
+       snd_hdac_bus_free_stream_pages(bus);
+       snd_hdac_stream_free_all(bus);
+err_init_streams:
+       iounmap(adev->dsp_ba);
+err_remap_bar4:
+       iounmap(bus->remap_addr);
+err_remap_bar0:
+       pci_release_regions(pci);
+       return ret;
+}
+
+static void avs_pci_remove(struct pci_dev *pci)
+{
+       struct hdac_device *hdev, *save;
+       struct hdac_bus *bus = pci_get_drvdata(pci);
+       struct avs_dev *adev = hdac_to_avs(bus);
+
+       cancel_work_sync(&adev->probe_work);
+       avs_ipc_block(adev->ipc);
+
+       avs_unregister_all_boards(adev);
+
+       if (adev->nhlt)
+               intel_nhlt_free(adev->nhlt);
+
+       if (avs_platattr_test(adev, CLDMA))
+               hda_cldma_free(&code_loader);
+
+       snd_hdac_stop_streams_and_chip(bus);
+       avs_dsp_op(adev, int_control, false);
+       snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+       /* it is safe to remove all codecs from the system now */
+       list_for_each_entry_safe(hdev, save, &bus->codec_list, list)
+               snd_hda_codec_unregister(hdac_to_hda_codec(hdev));
+
+       snd_hdac_bus_free_stream_pages(bus);
+       snd_hdac_stream_free_all(bus);
+       /* reverse ml_capabilities */
+       snd_hdac_link_free_all(bus);
+       snd_hdac_ext_bus_exit(bus);
+
+       avs_dsp_core_disable(adev, GENMASK(adev->hw_cfg.dsp_cores - 1, 0));
+       snd_hdac_ext_bus_ppcap_enable(bus, false);
+
+       /* snd_hdac_stop_streams_and_chip does that already? */
+       snd_hdac_bus_stop_chip(bus);
+       snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+       if (bus->audio_component)
+               snd_hdac_i915_exit(bus);
+
+       avs_module_info_free(adev);
+       pci_free_irq(pci, 0, adev);
+       pci_free_irq(pci, 0, bus);
+       pci_free_irq_vectors(pci);
+       iounmap(bus->remap_addr);
+       iounmap(adev->dsp_ba);
+       pci_release_regions(pci);
+
+       /* Firmware is not needed anymore */
+       avs_release_firmwares(adev);
+
+       /* pm_runtime_forbid() can rpm_resume() which we do not want */
+       pm_runtime_disable(&pci->dev);
+       pm_runtime_forbid(&pci->dev);
+       pm_runtime_enable(&pci->dev);
+       pm_runtime_get_noresume(&pci->dev);
+}
+
+static int __maybe_unused avs_suspend_common(struct avs_dev *adev)
+{
+       struct hdac_bus *bus = &adev->base.core;
+       int ret;
+
+       flush_work(&adev->probe_work);
+
+       snd_hdac_ext_bus_link_power_down_all(bus);
+
+       ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
+       /*
+        * pm_runtime is blocked on DSP failure but system-wide suspend is not.
+        * Do not block entire system from suspending if that's the case.
+        */
+       if (ret && ret != -EPERM) {
+               dev_err(adev->dev, "set dx failed: %d\n", ret);
+               return AVS_IPC_RET(ret);
+       }
+
+       avs_dsp_op(adev, int_control, false);
+       snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+       ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+       if (ret < 0) {
+               dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
+               return ret;
+       }
+
+       snd_hdac_ext_bus_ppcap_enable(bus, false);
+       /* disable LP SRAM retention */
+       avs_hda_power_gating_enable(adev, false);
+       snd_hdac_bus_stop_chip(bus);
+       /* disable CG when putting controller to reset */
+       avs_hdac_clock_gating_enable(bus, false);
+       snd_hdac_bus_enter_link_reset(bus);
+       avs_hdac_clock_gating_enable(bus, true);
+
+       snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+       return 0;
+}
+
+static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge)
+{
+       struct hdac_bus *bus = &adev->base.core;
+       struct hdac_ext_link *hlink;
+       int ret;
+
+       snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+       avs_hdac_bus_init_chip(bus, true);
+
+       snd_hdac_ext_bus_ppcap_enable(bus, true);
+       snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+       ret = avs_dsp_boot_firmware(adev, purge);
+       if (ret < 0) {
+               dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+               return ret;
+       }
+
+       /* turn off the links that were off before suspend */
+       list_for_each_entry(hlink, &bus->hlink_list, list) {
+               if (!hlink->ref_count)
+                       snd_hdac_ext_bus_link_power_down(hlink);
+       }
+
+       /* check dma status and clean up CORB/RIRB buffers */
+       if (!bus->cmd_dma_state)
+               snd_hdac_bus_stop_cmd_io(bus);
+
+       return 0;
+}
+
+static int __maybe_unused avs_suspend(struct device *dev)
+{
+       return avs_suspend_common(to_avs_dev(dev));
+}
+
+static int __maybe_unused avs_resume(struct device *dev)
+{
+       return avs_resume_common(to_avs_dev(dev), true);
+}
+
+static int __maybe_unused avs_runtime_suspend(struct device *dev)
+{
+       return avs_suspend_common(to_avs_dev(dev));
+}
+
+static int __maybe_unused avs_runtime_resume(struct device *dev)
+{
+       return avs_resume_common(to_avs_dev(dev), true);
+}
+
+static const struct dev_pm_ops avs_dev_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(avs_suspend, avs_resume)
+       SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
+};
+
+static const struct avs_spec skl_desc = {
+       .name = "skl",
+       .min_fw_version = {
+               .major = 9,
+               .minor = 21,
+               .hotfix = 0,
+               .build = 4732,
+       },
+       .dsp_ops = &skl_dsp_ops,
+       .core_init_mask = 1,
+       .attributes = AVS_PLATATTR_CLDMA,
+       .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+       .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
+       .rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_spec apl_desc = {
+       .name = "apl",
+       .min_fw_version = {
+               .major = 9,
+               .minor = 22,
+               .hotfix = 1,
+               .build = 4323,
+       },
+       .dsp_ops = &apl_dsp_ops,
+       .core_init_mask = 3,
+       .attributes = AVS_PLATATTR_IMR,
+       .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+       .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+       .rom_status = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct pci_device_id avs_ids[] = {
+       { PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */
+       { PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */
+       { PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */
+       { PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, avs_ids);
+
+static struct pci_driver avs_pci_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = avs_ids,
+       .probe = avs_pci_probe,
+       .remove = avs_pci_remove,
+       .driver = {
+               .pm = &avs_dev_pm,
+       },
+};
+module_pci_driver(avs_pci_driver);
+
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>");
+MODULE_DESCRIPTION("Intel cAVS sound driver");
+MODULE_LICENSE("GPL");
index 3ff17bd..06d2f7a 100644 (file)
@@ -6,10 +6,10 @@
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
-#include <linux/module.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "registers.h"
+#include "trace.h"
 
 #define AVS_ADSPCS_INTERVAL_US         500
 #define AVS_ADSPCS_TIMEOUT_US          50000
@@ -19,6 +19,9 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
        u32 value, mask, reg;
        int ret;
 
+       value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+       trace_avs_dsp_core_op(value, core_mask, "power", power);
+
        mask = AVS_ADSPCS_SPA_MASK(core_mask);
        value = power ? mask : 0;
 
@@ -43,6 +46,9 @@ int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
        u32 value, mask, reg;
        int ret;
 
+       value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+       trace_avs_dsp_core_op(value, core_mask, "reset", reset);
+
        mask = AVS_ADSPCS_CRST_MASK(core_mask);
        value = reset ? mask : 0;
 
@@ -64,6 +70,9 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
        u32 value, mask, reg;
        int ret;
 
+       value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+       trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+
        mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
        value = stall ? mask : 0;
 
@@ -152,6 +161,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
 
        adev->core_refs[core_id]++;
        if (adev->core_refs[core_id] == 1) {
+               /*
+                * No cores other than main-core can be running for DSP
+                * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
+                * simply d0ix power state will no longer be attempted.
+                */
+               ret = avs_dsp_disable_d0ix(adev);
+               if (ret && ret != -AVS_EIPC)
+                       goto err_disable_d0ix;
+
                ret = avs_dsp_enable(adev, mask);
                if (ret)
                        goto err_enable_dsp;
@@ -160,6 +178,8 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
        return 0;
 
 err_enable_dsp:
+       avs_dsp_enable_d0ix(adev);
+err_disable_d0ix:
        adev->core_refs[core_id]--;
 err:
        dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
@@ -185,6 +205,9 @@ static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
                ret = avs_dsp_disable(adev, mask);
                if (ret)
                        goto err;
+
+               /* Match disable_d0ix in avs_dsp_get_core(). */
+               avs_dsp_enable_d0ix(adev);
        }
 
        return 0;
@@ -298,5 +321,3 @@ int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
        ida_free(&adev->ppl_ida, instance_id);
        return ret;
 }
-
-MODULE_LICENSE("GPL");
index 68aaf01..d755ba8 100644 (file)
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
+#include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/slab.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "messages.h"
 #include "registers.h"
+#include "trace.h"
 
 #define AVS_IPC_TIMEOUT_MS     300
+#define AVS_D0IX_DELAY_MS      300
+
+static int
+avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
+{
+       struct avs_ipc *ipc = adev->ipc;
+       int ret;
+
+       /* Is transition required? */
+       if (ipc->in_d0ix == enable)
+               return 0;
+
+       ret = avs_dsp_op(adev, set_d0ix, enable);
+       if (ret) {
+               /* Prevent further d0ix attempts on conscious IPC failure. */
+               if (ret == -AVS_EIPC)
+                       atomic_inc(&ipc->d0ix_disable_depth);
+
+               ipc->in_d0ix = false;
+               return ret;
+       }
+
+       ipc->in_d0ix = enable;
+       return 0;
+}
+
+static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+       if (atomic_read(&adev->ipc->d0ix_disable_depth))
+               return;
+
+       mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work,
+                        msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+}
+
+static void avs_dsp_d0ix_work(struct work_struct *work)
+{
+       struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work);
+
+       avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true);
+}
+
+static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+       struct avs_ipc *ipc = adev->ipc;
+
+       if (!atomic_read(&ipc->d0ix_disable_depth)) {
+               cancel_delayed_work_sync(&ipc->d0ix_work);
+               return avs_dsp_set_d0ix(adev, false);
+       }
+
+       return 0;
+}
+
+int avs_dsp_disable_d0ix(struct avs_dev *adev)
+{
+       struct avs_ipc *ipc = adev->ipc;
+
+       /* Prevent PG only on the first disable. */
+       if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) {
+               cancel_delayed_work_sync(&ipc->d0ix_work);
+               return avs_dsp_set_d0ix(adev, false);
+       }
+
+       return 0;
+}
+
+int avs_dsp_enable_d0ix(struct avs_dev *adev)
+{
+       struct avs_ipc *ipc = adev->ipc;
+
+       if (atomic_dec_and_test(&ipc->d0ix_disable_depth))
+               queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work,
+                                  msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+       return 0;
+}
+
+static void avs_dsp_recovery(struct avs_dev *adev)
+{
+       struct avs_soc_component *acomp;
+       unsigned int core_mask;
+       int ret;
+
+       mutex_lock(&adev->comp_list_mutex);
+       /* disconnect all running streams */
+       list_for_each_entry(acomp, &adev->comp_list, node) {
+               struct snd_soc_pcm_runtime *rtd;
+               struct snd_soc_card *card;
+
+               card = acomp->base.card;
+               if (!card)
+                       continue;
+
+               for_each_card_rtds(card, rtd) {
+                       struct snd_pcm *pcm;
+                       int dir;
+
+                       pcm = rtd->pcm;
+                       if (!pcm || rtd->dai_link->no_pcm)
+                               continue;
+
+                       for_each_pcm_streams(dir) {
+                               struct snd_pcm_substream *substream;
+
+                               substream = pcm->streams[dir].substream;
+                               if (!substream || !substream->runtime)
+                                       continue;
+
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+                       }
+               }
+       }
+       mutex_unlock(&adev->comp_list_mutex);
+
+       /* forcibly shutdown all cores */
+       core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
+       avs_dsp_core_disable(adev, core_mask);
+
+       /* attempt dsp reboot */
+       ret = avs_dsp_boot_firmware(adev, true);
+       if (ret < 0)
+               dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
+
+       pm_runtime_mark_last_busy(adev->dev);
+       pm_runtime_enable(adev->dev);
+       pm_request_autosuspend(adev->dev);
+
+       atomic_set(&adev->ipc->recovering, 0);
+}
+
+static void avs_dsp_recovery_work(struct work_struct *work)
+{
+       struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work);
+
+       avs_dsp_recovery(to_avs_dev(ipc->dev));
+}
+
+static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+       struct avs_ipc *ipc = adev->ipc;
+
+       /* Account for the double-exception case. */
+       ipc->ready = false;
+
+       if (!atomic_add_unless(&ipc->recovering, 1, 1)) {
+               dev_err(adev->dev, "dsp recovery is already in progress\n");
+               return;
+       }
+
+       dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
+
+       cancel_delayed_work_sync(&ipc->d0ix_work);
+       ipc->in_d0ix = false;
+       /* Re-enabled on recovery completion. */
+       pm_runtime_disable(adev->dev);
+
+       /* Process received notification. */
+       avs_dsp_op(adev, coredump, msg);
+
+       schedule_work(&ipc->recovery_work);
+}
 
 static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 {
        struct avs_ipc *ipc = adev->ipc;
        union avs_reply_msg msg = AVS_MSG(header);
+       u64 reg;
+
+       reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+       trace_avs_ipc_reply_msg(header, reg);
 
        ipc->rx.header = header;
        /* Abort copying payload if request processing was unsuccessful. */
@@ -28,6 +195,7 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
                        ipc->rx.size = msg.ext.large_config.data_off_size;
 
                memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size);
+               trace_avs_msg_payload(ipc->rx.data, ipc->rx.size);
        }
 }
 
@@ -37,6 +205,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
        union avs_notify_msg msg = AVS_MSG(header);
        size_t data_size = 0;
        void *data = NULL;
+       u64 reg;
+
+       reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+       trace_avs_ipc_notify_msg(header, reg);
 
        /* Ignore spurious notifications until handshake is established. */
        if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
@@ -57,6 +229,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
                data_size = sizeof(struct avs_notify_res_data);
                break;
 
+       case AVS_NOTIFY_LOG_BUFFER_STATUS:
+       case AVS_NOTIFY_EXCEPTION_CAUGHT:
+               break;
+
        case AVS_NOTIFY_MODULE_EVENT:
                /* To know the total payload size, header needs to be read first. */
                memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
@@ -74,6 +250,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
                        return;
 
                memcpy_fromio(data, avs_uplink_addr(adev), data_size);
+               trace_avs_msg_payload(data, data_size);
        }
 
        /* Perform notification-specific operations. */
@@ -84,6 +261,14 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
                complete(&adev->fw_ready);
                break;
 
+       case AVS_NOTIFY_LOG_BUFFER_STATUS:
+               avs_dsp_op(adev, log_buffer_status, &msg);
+               break;
+
+       case AVS_NOTIFY_EXCEPTION_CAUGHT:
+               avs_dsp_exception_caught(adev, &msg);
+               break;
+
        default:
                break;
        }
@@ -249,9 +434,15 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
        reinit_completion(&ipc->busy_completion);
 }
 
-static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx)
+static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs)
 {
+       u64 reg = ULONG_MAX;
+
        tx->header |= SKL_ADSP_HIPCI_BUSY;
+       if (read_fwregs)
+               reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+
+       trace_avs_request(tx, reg);
 
        if (tx->size)
                memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
@@ -272,15 +463,16 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
 
        spin_lock(&ipc->rx_lock);
        avs_ipc_msg_init(ipc, reply);
-       avs_dsp_send_tx(adev, request);
+       avs_dsp_send_tx(adev, request, true);
        spin_unlock(&ipc->rx_lock);
 
        ret = avs_ipc_wait_busy_completion(ipc, timeout);
        if (ret) {
                if (ret == -ETIMEDOUT) {
-                       dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret);
+                       union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT);
 
-                       avs_ipc_block(ipc);
+                       /* Same treatment as on exception, just stack_dump=0. */
+                       avs_dsp_exception_caught(adev, &msg);
                }
                goto exit;
        }
@@ -297,10 +489,37 @@ exit:
        return ret;
 }
 
+static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
+                                    struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+                                    bool schedule_d0ix)
+{
+       int ret;
+
+       trace_avs_d0ix("wake", wake_d0i0, request->header);
+       if (wake_d0i0) {
+               ret = avs_dsp_wake_d0i0(adev, request);
+               if (ret)
+                       return ret;
+       }
+
+       ret = avs_dsp_do_send_msg(adev, request, reply, timeout);
+       if (ret)
+               return ret;
+
+       trace_avs_d0ix("schedule", schedule_d0ix, request->header);
+       if (schedule_d0ix)
+               avs_dsp_schedule_d0ix(adev, request);
+
+       return 0;
+}
+
 int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
                             struct avs_ipc_msg *reply, int timeout)
 {
-       return avs_dsp_do_send_msg(adev, request, reply, timeout);
+       bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
+       bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
+
+       return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix);
 }
 
 int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
@@ -309,6 +528,19 @@ int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
        return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms);
 }
 
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+                               struct avs_ipc_msg *reply, int timeout, bool wake_d0i0)
+{
+       return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false);
+}
+
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+                       struct avs_ipc_msg *reply, bool wake_d0i0)
+{
+       return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
+                                          wake_d0i0);
+}
+
 static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
 {
        struct avs_ipc *ipc = adev->ipc;
@@ -318,7 +550,11 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req
 
        spin_lock(&ipc->rx_lock);
        avs_ipc_msg_init(ipc, NULL);
-       avs_dsp_send_tx(adev, request);
+       /*
+        * with hw still stalled, memory windows may not be
+        * configured properly so avoid accessing SRAM
+        */
+       avs_dsp_send_tx(adev, request, false);
        spin_unlock(&ipc->rx_lock);
 
        /* ROM messages must be sent before main core is unstalled */
@@ -368,6 +604,8 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
        ipc->dev = dev;
        ipc->ready = false;
        ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
+       INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
+       INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work);
        init_completion(&ipc->done_completion);
        init_completion(&ipc->busy_completion);
        spin_lock_init(&ipc->rx_lock);
@@ -379,4 +617,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
 void avs_ipc_block(struct avs_ipc *ipc)
 {
        ipc->ready = false;
+       cancel_work_sync(&ipc->recovery_work);
+       cancel_delayed_work_sync(&ipc->d0ix_work);
+       ipc->in_d0ix = false;
 }
index c47f851..542fd44 100644 (file)
@@ -15,6 +15,7 @@
 #include "cldma.h"
 #include "messages.h"
 #include "registers.h"
+#include "topology.h"
 
 #define AVS_ROM_STS_MASK               0xFF
 #define AVS_ROM_INIT_DONE              0x1
@@ -36,6 +37,8 @@
 #define AVS_EXT_MANIFEST_MAGIC         0x31454124
 #define SKL_MANIFEST_MAGIC             0x00000006
 #define SKL_ADSPFW_OFFSET              0x284
+#define APL_MANIFEST_MAGIC             0x44504324
+#define APL_ADSPFW_OFFSET              0x2000
 
 /* Occasionally, engineering (release candidate) firmware is provided for testing. */
 static bool debug_ignore_fw_version;
@@ -86,6 +89,8 @@ static int avs_fw_manifest_offset(struct firmware *fw)
        switch (magic) {
        case SKL_MANIFEST_MAGIC:
                return SKL_ADSPFW_OFFSET;
+       case APL_MANIFEST_MAGIC:
+               return APL_ADSPFW_OFFSET;
        default:
                return -EINVAL;
        }
@@ -466,6 +471,71 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
        return 0;
 }
 
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
+{
+       int start, id, i = 0;
+       int ret;
+
+       /* Calculate the id to assign for the next lib. */
+       for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
+               if (adev->lib_names[id][0] == '\0')
+                       break;
+       if (id + num_libs >= adev->fw_cfg.max_libs_count)
+               return -EINVAL;
+
+       start = id;
+       while (i < num_libs) {
+               struct avs_fw_manifest *man;
+               const struct firmware *fw;
+               struct firmware stripped_fw;
+               char *filename;
+               int j;
+
+               filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
+                                    libs[i].name);
+               if (!filename)
+                       return -ENOMEM;
+
+               /*
+                * If any call after this one fails, requested firmware is not released with
+                * avs_release_last_firmware() as failing to load code results in need for reload
+                * of entire driver module. And then avs_release_firmwares() is in place already.
+                */
+               ret = avs_request_firmware(adev, &fw, filename);
+               kfree(filename);
+               if (ret < 0)
+                       return ret;
+
+               stripped_fw = *fw;
+               ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
+               if (ret) {
+                       dev_err(adev->dev, "invalid library data: %d\n", ret);
+                       return ret;
+               }
+
+               ret = avs_fw_manifest_offset(&stripped_fw);
+               if (ret < 0)
+                       return ret;
+               man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
+
+               /* Don't load anything that's already in DSP memory. */
+               for (j = 0; j < id; j++)
+                       if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
+                               goto next_lib;
+
+               ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
+               if (ret)
+                       return ret;
+
+               strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
+               id++;
+next_lib:
+               i++;
+       }
+
+       return start == id ? 1 : 0;
+}
+
 static int avs_dsp_load_basefw(struct avs_dev *adev)
 {
        const struct avs_fw_version *min_req;
@@ -519,6 +589,7 @@ release_fw:
 
 int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
 {
+       struct avs_soc_component *acomp;
        int ret, i;
 
        /* Forgo full boot if flash from IMR succeeds. */
@@ -538,7 +609,20 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
        avs_hda_l1sen_enable(adev, false);
 
        ret = avs_dsp_load_basefw(adev);
+       if (ret)
+               goto reenable_gating;
+
+       mutex_lock(&adev->comp_list_mutex);
+       list_for_each_entry(acomp, &adev->comp_list, node) {
+               struct avs_tplg *tplg = acomp->tplg;
+
+               ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+               if (ret < 0)
+                       break;
+       }
+       mutex_unlock(&adev->comp_list_mutex);
 
+reenable_gating:
        avs_hda_l1sen_enable(adev, true);
        avs_hda_clock_gating_enable(adev, true);
 
index 004da16..6404fce 100644 (file)
@@ -432,7 +432,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
        request.data = &dx;
        request.size = sizeof(dx);
 
-       ret = avs_dsp_send_msg(adev, &request, NULL);
+       ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
        if (ret)
                avs_ipc_err(adev, &request, "set dx", ret);
 
@@ -456,7 +456,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
 
        request.header = msg.val;
 
-       ret = avs_dsp_send_msg(adev, &request, NULL);
+       ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
        if (ret)
                avs_ipc_err(adev, &request, "set d0ix", ret);
 
@@ -677,6 +677,37 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
        return 0;
 }
 
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
+{
+       int ret;
+
+       ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+                                      AVS_BASEFW_ENABLE_LOGS, log_info, size);
+       if (ret)
+               dev_err(adev->dev, "enable logs failed: %d\n", ret);
+
+       return ret;
+}
+
+int avs_ipc_set_system_time(struct avs_dev *adev)
+{
+       struct avs_sys_time sys_time;
+       int ret;
+       u64 us;
+
+       /* firmware expects UTC time in micro seconds */
+       us = ktime_to_us(ktime_get());
+       sys_time.val_l = us & UINT_MAX;
+       sys_time.val_u = us >> 32;
+
+       ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+                                      AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
+       if (ret)
+               dev_err(adev->dev, "set system time failed: %d\n", ret);
+
+       return ret;
+}
+
 int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
                                   u8 instance_id, u32 sink_id,
                                   const struct avs_audio_format *src_fmt,
index 0395dd7..c0f90db 100644 (file)
@@ -186,7 +186,9 @@ union avs_reply_msg {
 enum avs_notify_msg_type {
        AVS_NOTIFY_PHRASE_DETECTED = 4,
        AVS_NOTIFY_RESOURCE_EVENT = 5,
+       AVS_NOTIFY_LOG_BUFFER_STATUS = 6,
        AVS_NOTIFY_FW_READY = 8,
+       AVS_NOTIFY_EXCEPTION_CAUGHT = 10,
        AVS_NOTIFY_MODULE_EVENT = 12,
 };
 
@@ -202,9 +204,17 @@ union avs_notify_msg {
                                u32 msg_direction:1;
                                u32 msg_target:1;
                        };
+                       struct {
+                               u16 rsvd:12;
+                               u16 core:4;
+                       } log;
                };
                union {
                        u32 val;
+                       struct {
+                               u32 core_id:2;
+                               u32 stack_dump_size:16;
+                       } coredump;
                } ext;
        };
 } __packed;
@@ -324,12 +334,46 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
 #define AVS_BASEFW_INST_ID     0
 
 enum avs_basefw_runtime_param {
+       AVS_BASEFW_ENABLE_LOGS = 6,
        AVS_BASEFW_FIRMWARE_CONFIG = 7,
        AVS_BASEFW_HARDWARE_CONFIG = 8,
        AVS_BASEFW_MODULES_INFO = 9,
        AVS_BASEFW_LIBRARIES_INFO = 16,
+       AVS_BASEFW_SYSTEM_TIME = 20,
+};
+
+enum avs_log_enable {
+       AVS_LOG_DISABLE = 0,
+       AVS_LOG_ENABLE = 1
 };
 
+enum avs_skl_log_priority {
+       AVS_SKL_LOG_CRITICAL = 1,
+       AVS_SKL_LOG_HIGH,
+       AVS_SKL_LOG_MEDIUM,
+       AVS_SKL_LOG_LOW,
+       AVS_SKL_LOG_VERBOSE,
+};
+
+struct skl_log_state {
+       u32 enable;
+       u32 min_priority;
+} __packed;
+
+struct skl_log_state_info {
+       u32 core_mask;
+       struct skl_log_state logs_core[];
+} __packed;
+
+struct apl_log_state_info {
+       u32 aging_timer_period;
+       u32 fifo_full_timer_period;
+       u32 core_mask;
+       struct skl_log_state logs_core[];
+} __packed;
+
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
+
 struct avs_fw_version {
        u16 major;
        u16 minor;
@@ -497,6 +541,13 @@ static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry)
 
 int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info);
 
+struct avs_sys_time {
+       u32 val_l;
+       u32 val_u;
+} __packed;
+
+int avs_ipc_set_system_time(struct avs_dev *adev);
+
 /* Module configuration */
 
 #define AVS_MIXIN_MOD_UUID \
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
new file mode 100644 (file)
index 0000000..3d46dd5
--- /dev/null
@@ -0,0 +1,1005 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/intel-nhlt.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "path.h"
+#include "topology.h"
+
+/* Must be called with adev->comp_list_mutex held. */
+static struct avs_tplg *
+avs_path_find_tplg(struct avs_dev *adev, const char *name)
+{
+       struct avs_soc_component *acomp;
+
+       list_for_each_entry(acomp, &adev->comp_list, node)
+               if (!strcmp(acomp->tplg->name, name))
+                       return acomp->tplg;
+       return NULL;
+}
+
+static struct avs_path_module *
+avs_path_find_module(struct avs_path_pipeline *ppl, u32 template_id)
+{
+       struct avs_path_module *mod;
+
+       list_for_each_entry(mod, &ppl->mod_list, node)
+               if (mod->template->id == template_id)
+                       return mod;
+       return NULL;
+}
+
+static struct avs_path_pipeline *
+avs_path_find_pipeline(struct avs_path *path, u32 template_id)
+{
+       struct avs_path_pipeline *ppl;
+
+       list_for_each_entry(ppl, &path->ppl_list, node)
+               if (ppl->template->id == template_id)
+                       return ppl;
+       return NULL;
+}
+
+static struct avs_path *
+avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id)
+{
+       struct avs_tplg_path_template *pos, *template = NULL;
+       struct avs_tplg *tplg;
+       struct avs_path *path;
+
+       tplg = avs_path_find_tplg(adev, name);
+       if (!tplg)
+               return NULL;
+
+       list_for_each_entry(pos, &tplg->path_tmpl_list, node) {
+               if (pos->id == template_id) {
+                       template = pos;
+                       break;
+               }
+       }
+       if (!template)
+               return NULL;
+
+       spin_lock(&adev->path_list_lock);
+       /* Only one variant of given path template may be instantiated at a time. */
+       list_for_each_entry(path, &adev->path_list, node) {
+               if (path->template->owner == template) {
+                       spin_unlock(&adev->path_list_lock);
+                       return path;
+               }
+       }
+
+       spin_unlock(&adev->path_list_lock);
+       return NULL;
+}
+
+static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
+                              struct avs_audio_format *fmt)
+{
+       return (params_rate(params) == fmt->sampling_freq &&
+               params_channels(params) == fmt->num_channels &&
+               params_physical_width(params) == fmt->bit_depth &&
+               params_width(params) == fmt->valid_bit_depth);
+}
+
+static struct avs_tplg_path *
+avs_path_find_variant(struct avs_dev *adev,
+                     struct avs_tplg_path_template *template,
+                     struct snd_pcm_hw_params *fe_params,
+                     struct snd_pcm_hw_params *be_params)
+{
+       struct avs_tplg_path *variant;
+
+       list_for_each_entry(variant, &template->path_list, node) {
+               dev_dbg(adev->dev, "check FE rate %d chn %d vbd %d bd %d\n",
+                       variant->fe_fmt->sampling_freq, variant->fe_fmt->num_channels,
+                       variant->fe_fmt->valid_bit_depth, variant->fe_fmt->bit_depth);
+               dev_dbg(adev->dev, "check BE rate %d chn %d vbd %d bd %d\n",
+                       variant->be_fmt->sampling_freq, variant->be_fmt->num_channels,
+                       variant->be_fmt->valid_bit_depth, variant->be_fmt->bit_depth);
+
+               if (variant->fe_fmt && avs_test_hw_params(fe_params, variant->fe_fmt) &&
+                   variant->be_fmt && avs_test_hw_params(be_params, variant->be_fmt))
+                       return variant;
+       }
+
+       return NULL;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_host(u32 dma_type)
+{
+       return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
+              dma_type == AVS_DMA_HDA_HOST_INPUT;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_link(u32 dma_type)
+{
+       return !avs_dma_type_is_host(dma_type);
+}
+
+__maybe_unused
+static bool avs_dma_type_is_output(u32 dma_type)
+{
+       return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
+              dma_type == AVS_DMA_HDA_LINK_OUTPUT ||
+              dma_type == AVS_DMA_I2S_LINK_OUTPUT;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_input(u32 dma_type)
+{
+       return !avs_dma_type_is_output(dma_type);
+}
+
+static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct nhlt_acpi_table *nhlt = adev->nhlt;
+       struct avs_tplg_module *t = mod->template;
+       struct avs_copier_cfg *cfg;
+       struct nhlt_specific_cfg *ep_blob;
+       union avs_connector_node_id node_id = {0};
+       size_t cfg_size, data_size = 0;
+       void *data = NULL;
+       u32 dma_type;
+       int ret;
+
+       dma_type = t->cfg_ext->copier.dma_type;
+       node_id.dma_type = dma_type;
+
+       switch (dma_type) {
+               struct avs_audio_format *fmt;
+               int direction;
+
+       case AVS_DMA_I2S_LINK_OUTPUT:
+       case AVS_DMA_I2S_LINK_INPUT:
+               if (avs_dma_type_is_input(dma_type))
+                       direction = SNDRV_PCM_STREAM_CAPTURE;
+               else
+                       direction = SNDRV_PCM_STREAM_PLAYBACK;
+
+               if (t->cfg_ext->copier.blob_fmt)
+                       fmt = t->cfg_ext->copier.blob_fmt;
+               else if (direction == SNDRV_PCM_STREAM_CAPTURE)
+                       fmt = t->in_fmt;
+               else
+                       fmt = t->cfg_ext->copier.out_fmt;
+
+               ep_blob = intel_nhlt_get_endpoint_blob(adev->dev,
+                       nhlt, t->cfg_ext->copier.vindex.i2s.instance,
+                       NHLT_LINK_SSP, fmt->valid_bit_depth, fmt->bit_depth,
+                       fmt->num_channels, fmt->sampling_freq, direction,
+                       NHLT_DEVICE_I2S);
+               if (!ep_blob) {
+                       dev_err(adev->dev, "no I2S ep_blob found\n");
+                       return -ENOENT;
+               }
+
+               data = ep_blob->caps;
+               data_size = ep_blob->size;
+               /* I2S gateway's vindex is statically assigned in topology */
+               node_id.vindex = t->cfg_ext->copier.vindex.val;
+
+               break;
+
+       case AVS_DMA_DMIC_LINK_INPUT:
+               direction = SNDRV_PCM_STREAM_CAPTURE;
+
+               if (t->cfg_ext->copier.blob_fmt)
+                       fmt = t->cfg_ext->copier.blob_fmt;
+               else
+                       fmt = t->in_fmt;
+
+               ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, nhlt, 0,
+                               NHLT_LINK_DMIC, fmt->valid_bit_depth,
+                               fmt->bit_depth, fmt->num_channels,
+                               fmt->sampling_freq, direction, NHLT_DEVICE_DMIC);
+               if (!ep_blob) {
+                       dev_err(adev->dev, "no DMIC ep_blob found\n");
+                       return -ENOENT;
+               }
+
+               data = ep_blob->caps;
+               data_size = ep_blob->size;
+               /* DMIC gateway's vindex is statically assigned in topology */
+               node_id.vindex = t->cfg_ext->copier.vindex.val;
+
+               break;
+
+       case AVS_DMA_HDA_HOST_OUTPUT:
+       case AVS_DMA_HDA_HOST_INPUT:
+               /* HOST gateway's vindex is dynamically assigned with DMA id */
+               node_id.vindex = mod->owner->owner->dma_id;
+               break;
+
+       case AVS_DMA_HDA_LINK_OUTPUT:
+       case AVS_DMA_HDA_LINK_INPUT:
+               node_id.vindex = t->cfg_ext->copier.vindex.val |
+                                mod->owner->owner->dma_id;
+               break;
+
+       case INVALID_OBJECT_ID:
+       default:
+               node_id = INVALID_NODE_ID;
+               break;
+       }
+
+       cfg_size = sizeof(*cfg) + data_size;
+       /* Every config-BLOB contains gateway attributes. */
+       if (data_size)
+               cfg_size -= sizeof(cfg->gtw_cfg.config.attrs);
+
+       cfg = kzalloc(cfg_size, GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->base.cpc = t->cfg_base->cpc;
+       cfg->base.ibs = t->cfg_base->ibs;
+       cfg->base.obs = t->cfg_base->obs;
+       cfg->base.is_pages = t->cfg_base->is_pages;
+       cfg->base.audio_fmt = *t->in_fmt;
+       cfg->out_fmt = *t->cfg_ext->copier.out_fmt;
+       cfg->feature_mask = t->cfg_ext->copier.feature_mask;
+       cfg->gtw_cfg.node_id = node_id;
+       cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size;
+       /* config_length in DWORDs */
+       cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4);
+       if (data)
+               memcpy(&cfg->gtw_cfg.config, data, data_size);
+
+       mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+
+       ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                 t->core_id, t->domain, cfg, cfg_size,
+                                 &mod->instance_id);
+       kfree(cfg);
+       return ret;
+}
+
+static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_updown_mixer_cfg cfg;
+       int i;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config;
+       cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select;
+       for (i = 0; i < AVS_CHANNELS_MAX; i++)
+               cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i];
+       cfg.channel_map = t->cfg_ext->updown_mix.channel_map;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_src_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_src_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.out_freq = t->cfg_ext->src.out_freq;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_asrc_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.out_freq = t->cfg_ext->asrc.out_freq;
+       cfg.mode = t->cfg_ext->asrc.mode;
+       cfg.disable_jitter_buffer = t->cfg_ext->asrc.disable_jitter_buffer;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_aec_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_aec_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.ref_fmt = *t->cfg_ext->aec.ref_fmt;
+       cfg.out_fmt = *t->cfg_ext->aec.out_fmt;
+       cfg.cpc_lp_mode = t->cfg_ext->aec.cpc_lp_mode;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_mux_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_mux_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.ref_fmt = *t->cfg_ext->mux.ref_fmt;
+       cfg.out_fmt = *t->cfg_ext->mux.out_fmt;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_wov_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_wov_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.cpc_lp_mode = t->cfg_ext->wov.cpc_lp_mode;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_micsel_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_micsel_cfg cfg;
+
+       cfg.base.cpc = t->cfg_base->cpc;
+       cfg.base.ibs = t->cfg_base->ibs;
+       cfg.base.obs = t->cfg_base->obs;
+       cfg.base.is_pages = t->cfg_base->is_pages;
+       cfg.base.audio_fmt = *t->in_fmt;
+       cfg.out_fmt = *t->cfg_ext->micsel.out_fmt;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_modbase_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_modcfg_base cfg;
+
+       cfg.cpc = t->cfg_base->cpc;
+       cfg.ibs = t->cfg_base->ibs;
+       cfg.obs = t->cfg_base->obs;
+       cfg.is_pages = t->cfg_base->is_pages;
+       cfg.audio_fmt = *t->in_fmt;
+
+       return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                  t->core_id, t->domain, &cfg, sizeof(cfg),
+                                  &mod->instance_id);
+}
+
+static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       struct avs_tplg_module *t = mod->template;
+       struct avs_tplg_modcfg_ext *tcfg = t->cfg_ext;
+       struct avs_modcfg_ext *cfg;
+       size_t cfg_size, num_pins;
+       int ret, i;
+
+       num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins;
+       cfg_size = sizeof(*cfg) + sizeof(*cfg->pin_fmts) * num_pins;
+
+       cfg = kzalloc(cfg_size, GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->base.cpc = t->cfg_base->cpc;
+       cfg->base.ibs = t->cfg_base->ibs;
+       cfg->base.obs = t->cfg_base->obs;
+       cfg->base.is_pages = t->cfg_base->is_pages;
+       cfg->base.audio_fmt = *t->in_fmt;
+       cfg->num_input_pins = tcfg->generic.num_input_pins;
+       cfg->num_output_pins = tcfg->generic.num_output_pins;
+
+       /* configure pin formats */
+       for (i = 0; i < num_pins; i++) {
+               struct avs_tplg_pin_format *tpin = &tcfg->generic.pin_fmts[i];
+               struct avs_pin_format *pin = &cfg->pin_fmts[i];
+
+               pin->pin_index = tpin->pin_index;
+               pin->iobs = tpin->iobs;
+               pin->audio_fmt = *tpin->fmt;
+       }
+
+       ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+                                 t->core_id, t->domain, cfg, cfg_size,
+                                 &mod->instance_id);
+       kfree(cfg);
+       return ret;
+}
+
+static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       const guid_t *type = &mod->template->cfg_ext->type;
+
+       if (guid_equal(type, &AVS_MIXIN_MOD_UUID) ||
+           guid_equal(type, &AVS_MIXOUT_MOD_UUID) ||
+           guid_equal(type, &AVS_KPBUFF_MOD_UUID))
+               return avs_modbase_create(adev, mod);
+       if (guid_equal(type, &AVS_COPIER_MOD_UUID))
+               return avs_copier_create(adev, mod);
+       if (guid_equal(type, &AVS_MICSEL_MOD_UUID))
+               return avs_micsel_create(adev, mod);
+       if (guid_equal(type, &AVS_MUX_MOD_UUID))
+               return avs_mux_create(adev, mod);
+       if (guid_equal(type, &AVS_UPDWMIX_MOD_UUID))
+               return avs_updown_mix_create(adev, mod);
+       if (guid_equal(type, &AVS_SRCINTC_MOD_UUID))
+               return avs_src_create(adev, mod);
+       if (guid_equal(type, &AVS_AEC_MOD_UUID))
+               return avs_aec_create(adev, mod);
+       if (guid_equal(type, &AVS_ASRC_MOD_UUID))
+               return avs_asrc_create(adev, mod);
+       if (guid_equal(type, &AVS_INTELWOV_MOD_UUID))
+               return avs_wov_create(adev, mod);
+
+       if (guid_equal(type, &AVS_PROBE_MOD_UUID)) {
+               dev_err(adev->dev, "Probe module can't be instantiated by topology");
+               return -EINVAL;
+       }
+
+       return avs_modext_create(adev, mod);
+}
+
+static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
+{
+       kfree(mod);
+}
+
+static struct avs_path_module *
+avs_path_module_create(struct avs_dev *adev,
+                      struct avs_path_pipeline *owner,
+                      struct avs_tplg_module *template)
+{
+       struct avs_path_module *mod;
+       int module_id, ret;
+
+       module_id = avs_get_module_id(adev, &template->cfg_ext->type);
+       if (module_id < 0)
+               return ERR_PTR(module_id);
+
+       mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+       if (!mod)
+               return ERR_PTR(-ENOMEM);
+
+       mod->template = template;
+       mod->module_id = module_id;
+       mod->owner = owner;
+       INIT_LIST_HEAD(&mod->node);
+
+       ret = avs_path_module_type_create(adev, mod);
+       if (ret) {
+               dev_err(adev->dev, "module-type create failed: %d\n", ret);
+               kfree(mod);
+               return ERR_PTR(ret);
+       }
+
+       return mod;
+}
+
+static int avs_path_binding_arm(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+       struct avs_path_module *this_mod, *target_mod;
+       struct avs_path_pipeline *target_ppl;
+       struct avs_path *target_path;
+       struct avs_tplg_binding *t;
+
+       t = binding->template;
+       this_mod = avs_path_find_module(binding->owner,
+                                       t->mod_id);
+       if (!this_mod) {
+               dev_err(adev->dev, "path mod %d not found\n", t->mod_id);
+               return -EINVAL;
+       }
+
+       /* update with target_tplg_name too */
+       target_path = avs_path_find_path(adev, t->target_tplg_name,
+                                        t->target_path_tmpl_id);
+       if (!target_path) {
+               dev_err(adev->dev, "target path %s:%d not found\n",
+                       t->target_tplg_name, t->target_path_tmpl_id);
+               return -EINVAL;
+       }
+
+       target_ppl = avs_path_find_pipeline(target_path,
+                                           t->target_ppl_id);
+       if (!target_ppl) {
+               dev_err(adev->dev, "target ppl %d not found\n", t->target_ppl_id);
+               return -EINVAL;
+       }
+
+       target_mod = avs_path_find_module(target_ppl, t->target_mod_id);
+       if (!target_mod) {
+               dev_err(adev->dev, "target mod %d not found\n", t->target_mod_id);
+               return -EINVAL;
+       }
+
+       if (t->is_sink) {
+               binding->sink = this_mod;
+               binding->sink_pin = t->mod_pin;
+               binding->source = target_mod;
+               binding->source_pin = t->target_mod_pin;
+       } else {
+               binding->sink = target_mod;
+               binding->sink_pin = t->target_mod_pin;
+               binding->source = this_mod;
+               binding->source_pin = t->mod_pin;
+       }
+
+       return 0;
+}
+
+static void avs_path_binding_free(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+       kfree(binding);
+}
+
+static struct avs_path_binding *avs_path_binding_create(struct avs_dev *adev,
+                                                       struct avs_path_pipeline *owner,
+                                                       struct avs_tplg_binding *t)
+{
+       struct avs_path_binding *binding;
+
+       binding = kzalloc(sizeof(*binding), GFP_KERNEL);
+       if (!binding)
+               return ERR_PTR(-ENOMEM);
+
+       binding->template = t;
+       binding->owner = owner;
+       INIT_LIST_HEAD(&binding->node);
+
+       return binding;
+}
+
+static int avs_path_pipeline_arm(struct avs_dev *adev,
+                                struct avs_path_pipeline *ppl)
+{
+       struct avs_path_module *mod;
+
+       list_for_each_entry(mod, &ppl->mod_list, node) {
+               struct avs_path_module *source, *sink;
+               int ret;
+
+               /*
+                * Only one module (so it's implicitly last) or it is the last
+                * one, either way we don't have next module to bind it to.
+                */
+               if (mod == list_last_entry(&ppl->mod_list,
+                                          struct avs_path_module, node))
+                       break;
+
+               /* bind current module to next module on list */
+               source = mod;
+               sink = list_next_entry(mod, node);
+               if (!source || !sink)
+                       return -EINVAL;
+
+               ret = avs_ipc_bind(adev, source->module_id, source->instance_id,
+                                  sink->module_id, sink->instance_id, 0, 0);
+               if (ret)
+                       return AVS_IPC_RET(ret);
+       }
+
+       return 0;
+}
+
+static void avs_path_pipeline_free(struct avs_dev *adev,
+                                  struct avs_path_pipeline *ppl)
+{
+       struct avs_path_binding *binding, *bsave;
+       struct avs_path_module *mod, *save;
+
+       list_for_each_entry_safe(binding, bsave, &ppl->binding_list, node) {
+               list_del(&binding->node);
+               avs_path_binding_free(adev, binding);
+       }
+
+       avs_dsp_delete_pipeline(adev, ppl->instance_id);
+
+       /* Unload resources occupied by owned modules */
+       list_for_each_entry_safe(mod, save, &ppl->mod_list, node) {
+               avs_dsp_delete_module(adev, mod->module_id, mod->instance_id,
+                                     mod->owner->instance_id,
+                                     mod->template->core_id);
+               avs_path_module_free(adev, mod);
+       }
+
+       list_del(&ppl->node);
+       kfree(ppl);
+}
+
+static struct avs_path_pipeline *
+avs_path_pipeline_create(struct avs_dev *adev, struct avs_path *owner,
+                        struct avs_tplg_pipeline *template)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_tplg_pplcfg *cfg = template->cfg;
+       struct avs_tplg_module *tmod;
+       int ret, i;
+
+       ppl = kzalloc(sizeof(*ppl), GFP_KERNEL);
+       if (!ppl)
+               return ERR_PTR(-ENOMEM);
+
+       ppl->template = template;
+       ppl->owner = owner;
+       INIT_LIST_HEAD(&ppl->binding_list);
+       INIT_LIST_HEAD(&ppl->mod_list);
+       INIT_LIST_HEAD(&ppl->node);
+
+       ret = avs_dsp_create_pipeline(adev, cfg->req_size, cfg->priority,
+                                     cfg->lp, cfg->attributes,
+                                     &ppl->instance_id);
+       if (ret) {
+               dev_err(adev->dev, "error creating pipeline %d\n", ret);
+               kfree(ppl);
+               return ERR_PTR(ret);
+       }
+
+       list_for_each_entry(tmod, &template->mod_list, node) {
+               struct avs_path_module *mod;
+
+               mod = avs_path_module_create(adev, ppl, tmod);
+               if (IS_ERR(mod)) {
+                       ret = PTR_ERR(mod);
+                       dev_err(adev->dev, "error creating module %d\n", ret);
+                       goto init_err;
+               }
+
+               list_add_tail(&mod->node, &ppl->mod_list);
+       }
+
+       for (i = 0; i < template->num_bindings; i++) {
+               struct avs_path_binding *binding;
+
+               binding = avs_path_binding_create(adev, ppl, template->bindings[i]);
+               if (IS_ERR(binding)) {
+                       ret = PTR_ERR(binding);
+                       dev_err(adev->dev, "error creating binding %d\n", ret);
+                       goto init_err;
+               }
+
+               list_add_tail(&binding->node, &ppl->binding_list);
+       }
+
+       return ppl;
+
+init_err:
+       avs_path_pipeline_free(adev, ppl);
+       return ERR_PTR(ret);
+}
+
+static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
+                        struct avs_tplg_path *template, u32 dma_id)
+{
+       struct avs_tplg_pipeline *tppl;
+
+       path->owner = adev;
+       path->template = template;
+       path->dma_id = dma_id;
+       INIT_LIST_HEAD(&path->ppl_list);
+       INIT_LIST_HEAD(&path->node);
+
+       /* create all the pipelines */
+       list_for_each_entry(tppl, &template->ppl_list, node) {
+               struct avs_path_pipeline *ppl;
+
+               ppl = avs_path_pipeline_create(adev, path, tppl);
+               if (IS_ERR(ppl))
+                       return PTR_ERR(ppl);
+
+               list_add_tail(&ppl->node, &path->ppl_list);
+       }
+
+       spin_lock(&adev->path_list_lock);
+       list_add_tail(&path->node, &adev->path_list);
+       spin_unlock(&adev->path_list_lock);
+
+       return 0;
+}
+
+static int avs_path_arm(struct avs_dev *adev, struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_path_binding *binding;
+       int ret;
+
+       list_for_each_entry(ppl, &path->ppl_list, node) {
+               /*
+                * Arm all ppl bindings before binding internal modules
+                * as it costs no IPCs which isn't true for the latter.
+                */
+               list_for_each_entry(binding, &ppl->binding_list, node) {
+                       ret = avs_path_binding_arm(adev, binding);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               ret = avs_path_pipeline_arm(adev, ppl);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void avs_path_free_unlocked(struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl, *save;
+
+       spin_lock(&path->owner->path_list_lock);
+       list_del(&path->node);
+       spin_unlock(&path->owner->path_list_lock);
+
+       list_for_each_entry_safe(ppl, save, &path->ppl_list, node)
+               avs_path_pipeline_free(path->owner, ppl);
+
+       kfree(path);
+}
+
+static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_id,
+                                                struct avs_tplg_path *template)
+{
+       struct avs_path *path;
+       int ret;
+
+       path = kzalloc(sizeof(*path), GFP_KERNEL);
+       if (!path)
+               return ERR_PTR(-ENOMEM);
+
+       ret = avs_path_init(adev, path, template, dma_id);
+       if (ret < 0)
+               goto err;
+
+       ret = avs_path_arm(adev, path);
+       if (ret < 0)
+               goto err;
+
+       path->state = AVS_PPL_STATE_INVALID;
+       return path;
+err:
+       avs_path_free_unlocked(path);
+       return ERR_PTR(ret);
+}
+
+void avs_path_free(struct avs_path *path)
+{
+       struct avs_dev *adev = path->owner;
+
+       mutex_lock(&adev->path_mutex);
+       avs_path_free_unlocked(path);
+       mutex_unlock(&adev->path_mutex);
+}
+
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+                                struct avs_tplg_path_template *template,
+                                struct snd_pcm_hw_params *fe_params,
+                                struct snd_pcm_hw_params *be_params)
+{
+       struct avs_tplg_path *variant;
+       struct avs_path *path;
+
+       variant = avs_path_find_variant(adev, template, fe_params, be_params);
+       if (!variant) {
+               dev_err(adev->dev, "no matching variant found\n");
+               return ERR_PTR(-ENOENT);
+       }
+
+       /* Serialize path and its components creation. */
+       mutex_lock(&adev->path_mutex);
+       /* Satisfy needs of avs_path_find_tplg(). */
+       mutex_lock(&adev->comp_list_mutex);
+
+       path = avs_path_create_unlocked(adev, dma_id, variant);
+
+       mutex_unlock(&adev->comp_list_mutex);
+       mutex_unlock(&adev->path_mutex);
+
+       return path;
+}
+
+static int avs_path_bind_prepare(struct avs_dev *adev,
+                                struct avs_path_binding *binding)
+{
+       const struct avs_audio_format *src_fmt, *sink_fmt;
+       struct avs_tplg_module *tsource = binding->source->template;
+       struct avs_path_module *source = binding->source;
+       int ret;
+
+       /*
+        * only copier modules about to be bound
+        * to output pin other than 0 need preparation
+        */
+       if (!binding->source_pin)
+               return 0;
+       if (!guid_equal(&tsource->cfg_ext->type, &AVS_COPIER_MOD_UUID))
+               return 0;
+
+       src_fmt = tsource->in_fmt;
+       sink_fmt = binding->sink->template->in_fmt;
+
+       ret = avs_ipc_copier_set_sink_format(adev, source->module_id,
+                                            source->instance_id, binding->source_pin,
+                                            src_fmt, sink_fmt);
+       if (ret) {
+               dev_err(adev->dev, "config copier failed: %d\n", ret);
+               return AVS_IPC_RET(ret);
+       }
+
+       return 0;
+}
+
+int avs_path_bind(struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_dev *adev = path->owner;
+       int ret;
+
+       list_for_each_entry(ppl, &path->ppl_list, node) {
+               struct avs_path_binding *binding;
+
+               list_for_each_entry(binding, &ppl->binding_list, node) {
+                       struct avs_path_module *source, *sink;
+
+                       source = binding->source;
+                       sink = binding->sink;
+
+                       ret = avs_path_bind_prepare(adev, binding);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = avs_ipc_bind(adev, source->module_id,
+                                          source->instance_id, sink->module_id,
+                                          sink->instance_id, binding->sink_pin,
+                                          binding->source_pin);
+                       if (ret) {
+                               dev_err(adev->dev, "bind path failed: %d\n", ret);
+                               return AVS_IPC_RET(ret);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int avs_path_unbind(struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_dev *adev = path->owner;
+       int ret;
+
+       list_for_each_entry(ppl, &path->ppl_list, node) {
+               struct avs_path_binding *binding;
+
+               list_for_each_entry(binding, &ppl->binding_list, node) {
+                       struct avs_path_module *source, *sink;
+
+                       source = binding->source;
+                       sink = binding->sink;
+
+                       ret = avs_ipc_unbind(adev, source->module_id,
+                                            source->instance_id, sink->module_id,
+                                            sink->instance_id, binding->sink_pin,
+                                            binding->source_pin);
+                       if (ret) {
+                               dev_err(adev->dev, "unbind path failed: %d\n", ret);
+                               return AVS_IPC_RET(ret);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int avs_path_reset(struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_dev *adev = path->owner;
+       int ret;
+
+       if (path->state == AVS_PPL_STATE_RESET)
+               return 0;
+
+       list_for_each_entry(ppl, &path->ppl_list, node) {
+               ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+                                                AVS_PPL_STATE_RESET);
+               if (ret) {
+                       dev_err(adev->dev, "reset path failed: %d\n", ret);
+                       path->state = AVS_PPL_STATE_INVALID;
+                       return AVS_IPC_RET(ret);
+               }
+       }
+
+       path->state = AVS_PPL_STATE_RESET;
+       return 0;
+}
+
+int avs_path_pause(struct avs_path *path)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_dev *adev = path->owner;
+       int ret;
+
+       if (path->state == AVS_PPL_STATE_PAUSED)
+               return 0;
+
+       list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
+               ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+                                                AVS_PPL_STATE_PAUSED);
+               if (ret) {
+                       dev_err(adev->dev, "pause path failed: %d\n", ret);
+                       path->state = AVS_PPL_STATE_INVALID;
+                       return AVS_IPC_RET(ret);
+               }
+       }
+
+       path->state = AVS_PPL_STATE_PAUSED;
+       return 0;
+}
+
+int avs_path_run(struct avs_path *path, int trigger)
+{
+       struct avs_path_pipeline *ppl;
+       struct avs_dev *adev = path->owner;
+       int ret;
+
+       if (path->state == AVS_PPL_STATE_RUNNING && trigger == AVS_TPLG_TRIGGER_AUTO)
+               return 0;
+
+       list_for_each_entry(ppl, &path->ppl_list, node) {
+               if (ppl->template->cfg->trigger != trigger)
+                       continue;
+
+               ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+                                                AVS_PPL_STATE_RUNNING);
+               if (ret) {
+                       dev_err(adev->dev, "run path failed: %d\n", ret);
+                       path->state = AVS_PPL_STATE_INVALID;
+                       return AVS_IPC_RET(ret);
+               }
+       }
+
+       path->state = AVS_PPL_STATE_RUNNING;
+       return 0;
+}
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
new file mode 100644 (file)
index 0000000..197222c
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_PATH_H
+#define __SOUND_SOC_INTEL_AVS_PATH_H
+
+#include <linux/list.h>
+#include "avs.h"
+#include "topology.h"
+
+struct avs_path {
+       u32 dma_id;
+       struct list_head ppl_list;
+       u32 state;
+
+       struct avs_tplg_path *template;
+       struct avs_dev *owner;
+       /* device path management */
+       struct list_head node;
+};
+
+struct avs_path_pipeline {
+       u8 instance_id;
+       struct list_head mod_list;
+       struct list_head binding_list;
+
+       struct avs_tplg_pipeline *template;
+       struct avs_path *owner;
+       /* path pipelines management */
+       struct list_head node;
+};
+
+struct avs_path_module {
+       u16 module_id;
+       u16 instance_id;
+       union avs_gtw_attributes gtw_attrs;
+
+       struct avs_tplg_module *template;
+       struct avs_path_pipeline *owner;
+       /* pipeline modules management */
+       struct list_head node;
+};
+
+struct avs_path_binding {
+       struct avs_path_module *source;
+       u8 source_pin;
+       struct avs_path_module *sink;
+       u8 sink_pin;
+
+       struct avs_tplg_binding *template;
+       struct avs_path_pipeline *owner;
+       /* pipeline bindings management */
+       struct list_head node;
+};
+
+void avs_path_free(struct avs_path *path);
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+                                struct avs_tplg_path_template *template,
+                                struct snd_pcm_hw_params *fe_params,
+                                struct snd_pcm_hw_params *be_params);
+int avs_path_bind(struct avs_path *path);
+int avs_path_unbind(struct avs_path *path);
+int avs_path_reset(struct avs_path *path);
+int avs_path_pause(struct avs_path *path);
+int avs_path_run(struct avs_path *path, int trigger);
+
+#endif
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
new file mode 100644 (file)
index 0000000..668f533
--- /dev/null
@@ -0,0 +1,1182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+#include "path.h"
+#include "topology.h"
+
+struct avs_dma_data {
+       struct avs_tplg_path_template *template;
+       struct avs_path *path;
+       /*
+        * link stream is stored within substream's runtime
+        * private_data to fulfill the needs of codec BE path
+        *
+        * host stream assigned
+        */
+       struct hdac_ext_stream *host_stream;
+};
+
+static struct avs_tplg_path_template *
+avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
+{
+       struct snd_soc_dapm_widget *dw;
+       struct snd_soc_dapm_path *dp;
+       enum snd_soc_dapm_direction dir;
+
+       if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+               dw = dai->capture_widget;
+               dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN;
+       } else {
+               dw = dai->playback_widget;
+               dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT;
+       }
+
+       dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]);
+       if (!dp)
+               return NULL;
+
+       /* Get the other widget, with actual path template data */
+       dw = (dp->source == dw) ? dp->sink : dp->source;
+
+       return dw->priv;
+}
+
+static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe)
+{
+       struct avs_tplg_path_template *template;
+       struct avs_dma_data *data;
+
+       template = avs_dai_find_path_template(dai, is_fe, substream->stream);
+       if (!template) {
+               dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n",
+                       snd_pcm_stream_str(substream), dai->name);
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->template = template;
+       snd_soc_dai_set_dma_data(dai, substream, data);
+
+       return 0;
+}
+
+static int avs_dai_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *fe_hw_params,
+                            struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+                            int dma_id)
+{
+       struct avs_dma_data *data;
+       struct avs_path *path;
+       struct avs_dev *adev = to_avs_dev(dai->dev);
+       int ret;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+
+       dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p",
+               __func__, substream, substream->runtime);
+       dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+               params_rate(fe_hw_params), params_channels(fe_hw_params),
+               params_width(fe_hw_params), params_physical_width(fe_hw_params));
+
+       dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p",
+               __func__, substream, substream->runtime);
+       dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+               params_rate(be_hw_params), params_channels(be_hw_params),
+               params_width(be_hw_params), params_physical_width(be_hw_params));
+
+       path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params);
+       if (IS_ERR(path)) {
+               ret = PTR_ERR(path);
+               dev_err(dai->dev, "create path failed: %d\n", ret);
+               return ret;
+       }
+
+       data->path = path;
+       return 0;
+}
+
+static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+                               int dma_id)
+{
+       struct snd_pcm_hw_params *fe_hw_params = NULL;
+       struct snd_soc_pcm_runtime *fe, *be;
+       struct snd_soc_dpcm *dpcm;
+
+       be = asoc_substream_to_rtd(substream);
+       for_each_dpcm_fe(be, substream->stream, dpcm) {
+               fe = dpcm->fe;
+               fe_hw_params = &fe->dpcm[substream->stream].hw_params;
+       }
+
+       return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id);
+}
+
+static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       int ret;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (!data->path)
+               return 0;
+
+       ret = avs_path_reset(data->path);
+       if (ret < 0) {
+               dev_err(dai->dev, "reset path failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = avs_path_pause(data->path);
+       if (ret < 0)
+               dev_err(dai->dev, "pause path failed: %d\n", ret);
+       return ret;
+}
+
+static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+       kfree(data);
+}
+
+static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (data->path)
+               return 0;
+
+       /* Actual port-id comes from topology. */
+       return avs_dai_be_hw_params(substream, hw_params, dai, 0);
+}
+
+static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (data->path) {
+               avs_path_free(data->path);
+               data->path = NULL;
+       }
+
+       return 0;
+}
+
+static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+}
+
+static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+                                    struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       int ret = 0;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+               if (ret < 0)
+                       dev_err(dai->dev, "run BE path failed: %d\n", ret);
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = avs_path_pause(data->path);
+               if (ret < 0)
+                       dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+               if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+                       ret = avs_path_reset(data->path);
+                       if (ret < 0)
+                               dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+               }
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
+       .startup = avs_dai_nonhda_be_startup,
+       .shutdown = avs_dai_nonhda_be_shutdown,
+       .hw_params = avs_dai_nonhda_be_hw_params,
+       .hw_free = avs_dai_nonhda_be_hw_free,
+       .prepare = avs_dai_nonhda_be_prepare,
+       .trigger = avs_dai_nonhda_be_trigger,
+};
+
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       return avs_dai_nonhda_be_shutdown(substream, dai);
+}
+
+static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       struct hdac_ext_stream *link_stream;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (data->path)
+               return 0;
+
+       link_stream = substream->runtime->private_data;
+
+       return avs_dai_be_hw_params(substream, hw_params, dai,
+                                   hdac_stream(link_stream)->stream_tag - 1);
+}
+
+static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct hdac_ext_stream *link_stream;
+       struct hdac_ext_link *link;
+       struct hda_codec *codec;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (!data->path)
+               return 0;
+
+       link_stream = substream->runtime->private_data;
+       link_stream->link_prepared = false;
+       avs_path_free(data->path);
+       data->path = NULL;
+
+       /* clear link <-> stream mapping */
+       codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+       link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
+       if (!link)
+               return -EINVAL;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag);
+
+       return 0;
+}
+
+static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct hdac_ext_stream *link_stream = runtime->private_data;
+       struct hdac_ext_link *link;
+       struct hda_codec *codec;
+       struct hdac_bus *bus;
+       unsigned int format_val;
+       int ret;
+
+       if (link_stream->link_prepared)
+               return 0;
+
+       codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+       bus = &codec->bus->core;
+       format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+                                                runtime->sample_bits, 0);
+
+       snd_hdac_ext_stream_decouple(bus, link_stream, true);
+       snd_hdac_ext_link_stream_reset(link_stream);
+       snd_hdac_ext_link_stream_setup(link_stream, format_val);
+
+       link = snd_hdac_ext_bus_link_at(bus, codec->core.addr);
+       if (!link)
+               return -EINVAL;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag);
+
+       ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+       if (ret)
+               return ret;
+
+       link_stream->link_prepared = true;
+       return 0;
+}
+
+static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+                                 struct snd_soc_dai *dai)
+{
+       struct hdac_ext_stream *link_stream;
+       struct avs_dma_data *data;
+       int ret = 0;
+
+       dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       link_stream = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               snd_hdac_ext_link_stream_start(link_stream);
+
+               ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+               if (ret < 0)
+                       dev_err(dai->dev, "run BE path failed: %d\n", ret);
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = avs_path_pause(data->path);
+               if (ret < 0)
+                       dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+               snd_hdac_ext_link_stream_clear(link_stream);
+
+               if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+                       ret = avs_path_reset(data->path);
+                       if (ret < 0)
+                               dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+               }
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
+       .startup = avs_dai_hda_be_startup,
+       .shutdown = avs_dai_hda_be_shutdown,
+       .hw_params = avs_dai_hda_be_hw_params,
+       .hw_free = avs_dai_hda_be_hw_free,
+       .prepare = avs_dai_hda_be_prepare,
+       .trigger = avs_dai_hda_be_trigger,
+};
+
+static const unsigned int rates[] = {
+       8000, 11025, 12000, 16000,
+       22050, 24000, 32000, 44100,
+       48000, 64000, 88200, 96000,
+       128000, 176400, 192000,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list = rates,
+       .mask = 0,
+};
+
+static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct avs_dma_data *data;
+       struct avs_dev *adev = to_avs_dev(dai->dev);
+       struct hdac_bus *bus = &adev->base.core;
+       struct hdac_ext_stream *host_stream;
+       int ret;
+
+       ret = avs_dai_startup(substream, dai, true);
+       if (ret)
+               return ret;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+
+       host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST);
+       if (!host_stream) {
+               kfree(data);
+               return -EBUSY;
+       }
+
+       data->host_stream = host_stream;
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       /* avoid wrap-around with wall-clock */
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000);
+       snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates);
+       snd_pcm_set_sync(substream);
+
+       dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p",
+               __func__, hdac_stream(host_stream)->stream_tag, substream);
+
+       return 0;
+}
+
+static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+       snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+       kfree(data);
+}
+
+static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct snd_pcm_hw_params *be_hw_params = NULL;
+       struct snd_soc_pcm_runtime *fe, *be;
+       struct snd_soc_dpcm *dpcm;
+       struct avs_dma_data *data;
+       struct hdac_ext_stream *host_stream;
+       int ret;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (data->path)
+               return 0;
+
+       host_stream = data->host_stream;
+
+       hdac_stream(host_stream)->bufsize = 0;
+       hdac_stream(host_stream)->period_bytes = 0;
+       hdac_stream(host_stream)->format_val = 0;
+
+       fe = asoc_substream_to_rtd(substream);
+       for_each_dpcm_be(fe, substream->stream, dpcm) {
+               be = dpcm->be;
+               be_hw_params = &be->dpcm[substream->stream].hw_params;
+       }
+
+       ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai,
+                               hdac_stream(host_stream)->stream_tag - 1);
+       if (ret)
+               goto create_err;
+
+       ret = avs_path_bind(data->path);
+       if (ret < 0) {
+               dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret);
+               goto bind_err;
+       }
+
+       return 0;
+
+bind_err:
+       avs_path_free(data->path);
+       data->path = NULL;
+create_err:
+       snd_pcm_lib_free_pages(substream);
+       return ret;
+}
+
+static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       struct hdac_ext_stream *host_stream;
+       int ret;
+
+       dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p",
+               __func__, substream, substream->runtime);
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       if (!data->path)
+               return 0;
+
+       host_stream = data->host_stream;
+
+       ret = avs_path_unbind(data->path);
+       if (ret < 0)
+               dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret);
+
+       avs_path_free(data->path);
+       data->path = NULL;
+       snd_hdac_stream_cleanup(hdac_stream(host_stream));
+       hdac_stream(host_stream)->prepared = false;
+
+       ret = snd_pcm_lib_free_pages(substream);
+       if (ret < 0)
+               dev_dbg(dai->dev, "Failed to free pages!\n");
+
+       return ret;
+}
+
+static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct avs_dma_data *data;
+       struct avs_dev *adev = to_avs_dev(dai->dev);
+       struct hdac_ext_stream *host_stream;
+       struct hdac_bus *bus;
+       unsigned int format_val;
+       int ret;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       host_stream = data->host_stream;
+
+       if (hdac_stream(host_stream)->prepared)
+               return 0;
+
+       bus = hdac_stream(host_stream)->bus;
+       snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
+       snd_hdac_stream_reset(hdac_stream(host_stream));
+
+       format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+                                                runtime->sample_bits, 0);
+
+       ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_hdac_stream_setup(hdac_stream(host_stream));
+       if (ret < 0)
+               return ret;
+
+       ret = avs_dai_prepare(adev, substream, dai);
+       if (ret)
+               return ret;
+
+       hdac_stream(host_stream)->prepared = true;
+       return 0;
+}
+
+static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+       struct avs_dma_data *data;
+       struct hdac_ext_stream *host_stream;
+       struct hdac_bus *bus;
+       unsigned long flags;
+       int ret = 0;
+
+       data = snd_soc_dai_get_dma_data(dai, substream);
+       host_stream = data->host_stream;
+       bus = hdac_stream(host_stream)->bus;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               spin_lock_irqsave(&bus->reg_lock, flags);
+               snd_hdac_stream_start(hdac_stream(host_stream), true);
+               spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+               ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+               if (ret < 0)
+                       dev_err(dai->dev, "run FE path failed: %d\n", ret);
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = avs_path_pause(data->path);
+               if (ret < 0)
+                       dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+
+               spin_lock_irqsave(&bus->reg_lock, flags);
+               snd_hdac_stream_stop(hdac_stream(host_stream));
+               spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+               if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+                       ret = avs_path_reset(data->path);
+                       if (ret < 0)
+                               dev_err(dai->dev, "reset FE path failed: %d\n", ret);
+               }
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+const struct snd_soc_dai_ops avs_dai_fe_ops = {
+       .startup = avs_dai_fe_startup,
+       .shutdown = avs_dai_fe_shutdown,
+       .hw_params = avs_dai_fe_hw_params,
+       .hw_free = avs_dai_fe_hw_free,
+       .prepare = avs_dai_fe_prepare,
+       .trigger = avs_dai_fe_trigger,
+};
+
+static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
+                                 loff_t *ppos)
+{
+       struct snd_soc_component *component = file->private_data;
+       struct snd_soc_card *card = component->card;
+       struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+       char buf[64];
+       size_t len;
+
+       len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
+                      mach->tplg_filename);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations topology_name_fops = {
+       .open = simple_open,
+       .read = topology_name_read,
+       .llseek = default_llseek,
+};
+
+static int avs_component_load_libraries(struct avs_soc_component *acomp)
+{
+       struct avs_tplg *tplg = acomp->tplg;
+       struct avs_dev *adev = to_avs_dev(acomp->base.dev);
+       int ret;
+
+       if (!tplg->num_libs)
+               return 0;
+
+       /* Parent device may be asleep and library loading involves IPCs. */
+       ret = pm_runtime_resume_and_get(adev->dev);
+       if (ret < 0)
+               return ret;
+
+       avs_hda_clock_gating_enable(adev, false);
+       avs_hda_l1sen_enable(adev, false);
+
+       ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+
+       avs_hda_l1sen_enable(adev, true);
+       avs_hda_clock_gating_enable(adev, true);
+
+       if (!ret)
+               ret = avs_module_info_init(adev, false);
+
+       pm_runtime_mark_last_busy(adev->dev);
+       pm_runtime_put_autosuspend(adev->dev);
+
+       return ret;
+}
+
+static int avs_component_probe(struct snd_soc_component *component)
+{
+       struct snd_soc_card *card = component->card;
+       struct snd_soc_acpi_mach *mach;
+       struct avs_soc_component *acomp;
+       struct avs_dev *adev;
+       char *filename;
+       int ret;
+
+       dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
+       mach = dev_get_platdata(card->dev);
+       acomp = to_avs_soc_component(component);
+       adev = to_avs_dev(component->dev);
+
+       acomp->tplg = avs_tplg_new(component);
+       if (!acomp->tplg)
+               return -ENOMEM;
+
+       if (!mach->tplg_filename)
+               goto finalize;
+
+       /* Load specified topology and create debugfs for it. */
+       filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
+                            mach->tplg_filename);
+       if (!filename)
+               return -ENOMEM;
+
+       ret = avs_load_topology(component, filename);
+       kfree(filename);
+       if (ret < 0)
+               return ret;
+
+       ret = avs_component_load_libraries(acomp);
+       if (ret < 0) {
+               dev_err(card->dev, "libraries loading failed: %d\n", ret);
+               goto err_load_libs;
+       }
+
+finalize:
+       debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
+                           &topology_name_fops);
+
+       mutex_lock(&adev->comp_list_mutex);
+       list_add_tail(&acomp->node, &adev->comp_list);
+       mutex_unlock(&adev->comp_list_mutex);
+
+       return 0;
+
+err_load_libs:
+       avs_remove_topology(component);
+       return ret;
+}
+
+static void avs_component_remove(struct snd_soc_component *component)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(component);
+       struct snd_soc_acpi_mach *mach;
+       struct avs_dev *adev = to_avs_dev(component->dev);
+       int ret;
+
+       mach = dev_get_platdata(component->card->dev);
+
+       mutex_lock(&adev->comp_list_mutex);
+       list_del(&acomp->node);
+       mutex_unlock(&adev->comp_list_mutex);
+
+       if (mach->tplg_filename) {
+               ret = avs_remove_topology(component);
+               if (ret < 0)
+                       dev_err(component->dev, "unload topology failed: %d\n", ret);
+       }
+}
+
+static int avs_component_open(struct snd_soc_component *component,
+                             struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct snd_pcm_hardware hwparams;
+
+       /* only FE DAI links are handled here */
+       if (rtd->dai_link->no_pcm)
+               return 0;
+
+       hwparams.info = SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_PAUSE |
+                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+
+       hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE |
+                          SNDRV_PCM_FMTBIT_S24_LE |
+                          SNDRV_PCM_FMTBIT_S32_LE;
+       hwparams.period_bytes_min = 128;
+       hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2;
+       hwparams.periods_min = 2;
+       hwparams.periods_max = AZX_MAX_FRAG;
+       hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE;
+       hwparams.fifo_size = 0;
+
+       return snd_soc_set_runtime_hwparams(substream, &hwparams);
+}
+
+static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
+{
+       return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
+                    (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index));
+}
+
+static snd_pcm_uframes_t
+avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct avs_dma_data *data;
+       struct hdac_ext_stream *host_stream;
+       unsigned int pos;
+
+       data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+       if (!data->host_stream)
+               return 0;
+
+       host_stream = data->host_stream;
+       pos = avs_hda_stream_dpib_read(host_stream);
+
+       if (pos >= hdac_stream(host_stream)->bufsize)
+               pos = 0;
+
+       return bytes_to_frames(substream->runtime, pos);
+}
+
+static int avs_component_mmap(struct snd_soc_component *component,
+                             struct snd_pcm_substream *substream,
+                             struct vm_area_struct *vma)
+{
+       return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+#define MAX_PREALLOC_SIZE      (32 * 1024 * 1024)
+
+static int avs_component_construct(struct snd_soc_component *component,
+                                  struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+       struct snd_pcm *pcm = rtd->pcm;
+
+       if (dai->driver->playback.channels_min)
+               snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                                          SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+                                          MAX_PREALLOC_SIZE);
+
+       if (dai->driver->capture.channels_min)
+               snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                                          SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+                                          MAX_PREALLOC_SIZE);
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver avs_component_driver = {
+       .name                   = "avs-pcm",
+       .probe                  = avs_component_probe,
+       .remove                 = avs_component_remove,
+       .open                   = avs_component_open,
+       .pointer                = avs_component_pointer,
+       .mmap                   = avs_component_mmap,
+       .pcm_construct          = avs_component_construct,
+       .module_get_upon_open   = 1, /* increment refcount when a pcm is opened */
+       .topology_name_prefix   = "intel/avs",
+       .non_legacy_dai_naming  = true,
+};
+
+static int avs_soc_component_register(struct device *dev, const char *name,
+                                     const struct snd_soc_component_driver *drv,
+                                     struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+{
+       struct avs_soc_component *acomp;
+       int ret;
+
+       acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL);
+       if (!acomp)
+               return -ENOMEM;
+
+       ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+       if (ret < 0)
+               return ret;
+
+       /* force name change after ASoC is done with its init */
+       acomp->base.name = name;
+       INIT_LIST_HEAD(&acomp->node);
+
+       return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
+}
+
+static struct snd_soc_dai_driver dmic_cpu_dais[] = {
+{
+       .name = "DMIC Pin",
+       .ops = &avs_dai_nonhda_be_ops,
+       .capture = {
+               .stream_name    = "DMIC Rx",
+               .channels_min   = 1,
+               .channels_max   = 4,
+               .rates          = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "DMIC WoV Pin",
+       .ops = &avs_dai_nonhda_be_ops,
+       .capture = {
+               .stream_name    = "DMIC WoV Rx",
+               .channels_min   = 1,
+               .channels_max   = 4,
+               .rates          = SNDRV_PCM_RATE_16000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+};
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+{
+       return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+                                         ARRAY_SIZE(dmic_cpu_dais));
+}
+
+static const struct snd_soc_dai_driver i2s_dai_template = {
+       .ops = &avs_dai_nonhda_be_ops,
+       .playback = {
+               .channels_min   = 1,
+               .channels_max   = 8,
+               .rates          = SNDRV_PCM_RATE_8000_192000 |
+                                 SNDRV_PCM_RATE_KNOT,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .capture = {
+               .channels_min   = 1,
+               .channels_max   = 8,
+               .rates          = SNDRV_PCM_RATE_8000_192000 |
+                                 SNDRV_PCM_RATE_KNOT,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       },
+};
+
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+                             unsigned long *tdms)
+{
+       struct snd_soc_dai_driver *cpus, *dai;
+       size_t ssp_count, cpu_count;
+       int i, j;
+
+       ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+       cpu_count = hweight_long(port_mask);
+       if (tdms)
+               for_each_set_bit(i, &port_mask, ssp_count)
+                       cpu_count += hweight_long(tdms[i]);
+
+       cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL);
+       if (!cpus)
+               return -ENOMEM;
+
+       dai = cpus;
+       for_each_set_bit(i, &port_mask, ssp_count) {
+               memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+               dai->name =
+                       devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+               dai->playback.stream_name =
+                       devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+               dai->capture.stream_name =
+                       devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+
+               if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+                       return -ENOMEM;
+               dai++;
+       }
+
+       if (!tdms)
+               goto plat_register;
+
+       for_each_set_bit(i, &port_mask, ssp_count) {
+               for_each_set_bit(j, &tdms[i], ssp_count) {
+                       memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+                       dai->name =
+                               devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j);
+                       dai->playback.stream_name =
+                               devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j);
+                       dai->capture.stream_name =
+                               devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j);
+
+                       if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+                               return -ENOMEM;
+                       dai++;
+               }
+       }
+
+plat_register:
+       return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+}
+
+/* HD-Audio CPU DAI template */
+static const struct snd_soc_dai_driver hda_cpu_dai = {
+       .ops = &avs_dai_hda_be_ops,
+       .playback = {
+               .channels_min   = 1,
+               .channels_max   = 8,
+               .rates          = SNDRV_PCM_RATE_8000_192000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .capture = {
+               .channels_min   = 1,
+               .channels_max   = 8,
+               .rates          = SNDRV_PCM_RATE_8000_192000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       },
+};
+
+static void avs_component_hda_unregister_dais(struct snd_soc_component *component)
+{
+       struct snd_soc_acpi_mach *mach;
+       struct snd_soc_dai *dai, *save;
+       struct hda_codec *codec;
+       char name[32];
+
+       mach = dev_get_platdata(component->card->dev);
+       codec = mach->pdata;
+       sprintf(name, "%s-cpu", dev_name(&codec->core.dev));
+
+       for_each_component_dais_safe(component, dai, save) {
+               if (!strstr(dai->driver->name, name))
+                       continue;
+
+               if (dai->playback_widget)
+                       snd_soc_dapm_free_widget(dai->playback_widget);
+               if (dai->capture_widget)
+                       snd_soc_dapm_free_widget(dai->capture_widget);
+               snd_soc_unregister_dai(dai);
+       }
+}
+
+static int avs_component_hda_probe(struct snd_soc_component *component)
+{
+       struct snd_soc_dapm_context *dapm;
+       struct snd_soc_dai_driver *dais;
+       struct snd_soc_acpi_mach *mach;
+       struct hda_codec *codec;
+       struct hda_pcm *pcm;
+       const char *cname;
+       int pcm_count = 0, ret, i;
+
+       mach = dev_get_platdata(component->card->dev);
+       if (!mach)
+               return -EINVAL;
+
+       codec = mach->pdata;
+       if (list_empty(&codec->pcm_list_head))
+               return -EINVAL;
+       list_for_each_entry(pcm, &codec->pcm_list_head, list)
+               pcm_count++;
+
+       dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
+                           GFP_KERNEL);
+       if (!dais)
+               return -ENOMEM;
+
+       cname = dev_name(&codec->core.dev);
+       dapm = snd_soc_component_get_dapm(component);
+       pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+       for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+               struct snd_soc_dai *dai;
+
+               memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais));
+               dais[i].id = i;
+               dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
+                                             "%s-cpu%d", cname, i);
+               if (!dais[i].name) {
+                       ret = -ENOMEM;
+                       goto exit;
+               }
+
+               if (pcm->stream[0].substreams) {
+                       dais[i].playback.stream_name =
+                               devm_kasprintf(component->dev, GFP_KERNEL,
+                                              "%s-cpu%d Tx", cname, i);
+                       if (!dais[i].playback.stream_name) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+               }
+
+               if (pcm->stream[1].substreams) {
+                       dais[i].capture.stream_name =
+                               devm_kasprintf(component->dev, GFP_KERNEL,
+                                              "%s-cpu%d Rx", cname, i);
+                       if (!dais[i].capture.stream_name) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+               }
+
+               dai = snd_soc_register_dai(component, &dais[i], false);
+               if (!dai) {
+                       dev_err(component->dev, "register dai for %s failed\n",
+                               pcm->name);
+                       ret = -EINVAL;
+                       goto exit;
+               }
+
+               ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+               if (ret < 0) {
+                       dev_err(component->dev, "create widgets failed: %d\n",
+                               ret);
+                       goto exit;
+               }
+       }
+
+       ret = avs_component_probe(component);
+exit:
+       if (ret)
+               avs_component_hda_unregister_dais(component);
+
+       return ret;
+}
+
+static void avs_component_hda_remove(struct snd_soc_component *component)
+{
+       avs_component_hda_unregister_dais(component);
+       avs_component_remove(component);
+}
+
+static int avs_component_hda_open(struct snd_soc_component *component,
+                                 struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct hdac_ext_stream *link_stream;
+       struct hda_codec *codec;
+
+       /* only BE DAI links are handled here */
+       if (!rtd->dai_link->no_pcm)
+               return avs_component_open(component, substream);
+
+       codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+       link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
+                                            HDAC_EXT_STREAM_TYPE_LINK);
+       if (!link_stream)
+               return -EBUSY;
+
+       substream->runtime->private_data = link_stream;
+       return 0;
+}
+
+static int avs_component_hda_close(struct snd_soc_component *component,
+                                  struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct hdac_ext_stream *link_stream;
+
+       /* only BE DAI links are handled here */
+       if (!rtd->dai_link->no_pcm)
+               return 0;
+
+       link_stream = substream->runtime->private_data;
+       snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+       substream->runtime->private_data = NULL;
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver avs_hda_component_driver = {
+       .name                   = "avs-hda-pcm",
+       .probe                  = avs_component_hda_probe,
+       .remove                 = avs_component_hda_remove,
+       .open                   = avs_component_hda_open,
+       .close                  = avs_component_hda_close,
+       .pointer                = avs_component_pointer,
+       .mmap                   = avs_component_mmap,
+       .pcm_construct          = avs_component_construct,
+       /*
+        * hda platform component's probe() is dependent on
+        * codec->pcm_list_head, it needs to be initialized after codec
+        * component. remove_order is here for completeness sake
+        */
+       .probe_order            = SND_SOC_COMP_ORDER_LATE,
+       .remove_order           = SND_SOC_COMP_ORDER_EARLY,
+       .module_get_upon_open   = 1,
+       .topology_name_prefix   = "intel/avs",
+       .non_legacy_dai_naming  = true,
+};
+
+int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+{
+       return avs_soc_component_register(adev->dev, name,
+                                         &avs_hda_component_driver, NULL, 0);
+}
index 3fd0238..95be861 100644 (file)
@@ -14,6 +14,7 @@
 #define AZX_PGCTL_LSRMD_MASK           BIT(4)
 #define AZX_CGCTL_MISCBDCGE_MASK       BIT(6)
 #define AZX_VS_EM2_L1SEN               BIT(13)
+#define AZX_VS_EM2_DUM                 BIT(23)
 
 /* Intel HD Audio General DSP Registers */
 #define AVS_ADSP_GEN_BASE              0x0
 #define SKL_ADSP_HIPCIE_DONE           BIT(30)
 #define SKL_ADSP_HIPCT_BUSY            BIT(31)
 
+/* Intel HD Audio SRAM windows base addresses */
+#define SKL_ADSP_SRAM_BASE_OFFSET      0x8000
+#define SKL_ADSP_SRAM_WINDOW_SIZE      0x2000
+#define APL_ADSP_SRAM_BASE_OFFSET      0x80000
+#define APL_ADSP_SRAM_WINDOW_SIZE      0x20000
+
 /* Constants used when accessing SRAM, space shared with firmware */
 #define AVS_FW_REG_BASE(adev)          ((adev)->spec->sram_base_offset)
 #define AVS_FW_REG_STATUS(adev)                (AVS_FW_REG_BASE(adev) + 0x0)
@@ -58,6 +65,7 @@
 #define AVS_UPLINK_WINDOW              AVS_FW_REGS_WINDOW
 /* HOST -> DSP communication window */
 #define AVS_DOWNLINK_WINDOW            1
+#define AVS_DEBUG_WINDOW               2
 
 /* registry I/O helpers */
 #define avs_sram_offset(adev, window_idx) \
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
new file mode 100644 (file)
index 0000000..bda5ec7
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+
+static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+                          u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+       struct skl_log_state_info *info;
+       u32 size, num_cores = adev->hw_cfg.dsp_cores;
+       int ret, i;
+
+       if (fls_long(resource_mask) > num_cores)
+               return -EINVAL;
+       size = struct_size(info, logs_core, num_cores);
+       info = kzalloc(size, GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->core_mask = resource_mask;
+       if (enable)
+               for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) {
+                       info->logs_core[i].enable = enable;
+                       info->logs_core[i].min_priority = *priorities++;
+               }
+       else
+               for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0))
+                       info->logs_core[i].enable = enable;
+
+       ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+       kfree(info);
+       if (ret)
+               return AVS_IPC_RET(ret);
+
+       return 0;
+}
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+       return core * avs_log_buffer_size(adev);
+}
+
+/* fw DbgLogWp registers */
+#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
+
+static int
+skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+       unsigned long flags;
+       void __iomem *buf;
+       u16 size, write, offset;
+
+       spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+       if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
+               spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+               return 0;
+       }
+
+       size = avs_log_buffer_size(adev) / 2;
+       write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
+       /* determine buffer half */
+       offset = (write < size) ? size : 0;
+
+       /* Address is guaranteed to exist in SRAM2. */
+       buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
+       __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock);
+       wake_up(&adev->dbg.trace_waitq);
+       spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+
+       return 0;
+}
+
+static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+       u8 *dump;
+
+       dump = vzalloc(AVS_FW_REGS_SIZE);
+       if (!dump)
+               return -ENOMEM;
+
+       memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+       dev_coredumpv(adev->dev, dump, AVS_FW_REGS_SIZE, GFP_KERNEL);
+
+       return 0;
+}
+
+static bool
+skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+       /* unsupported on cAVS 1.5 hw */
+       return false;
+}
+
+static int skl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+       /* unsupported on cAVS 1.5 hw */
+       return 0;
+}
+
+const struct avs_dsp_ops skl_dsp_ops = {
+       .power = avs_dsp_core_power,
+       .reset = avs_dsp_core_reset,
+       .stall = avs_dsp_core_stall,
+       .irq_handler = avs_dsp_irq_handler,
+       .irq_thread = avs_dsp_irq_thread,
+       .int_control = avs_dsp_interrupt_control,
+       .load_basefw = avs_cldma_load_basefw,
+       .load_lib = avs_cldma_load_library,
+       .transfer_mods = avs_cldma_transfer_modules,
+       .enable_logs = skl_enable_logs,
+       .log_buffer_offset = skl_log_buffer_offset,
+       .log_buffer_status = skl_log_buffer_status,
+       .coredump = skl_coredump,
+       .d0ix_toggle = skl_d0ix_toggle,
+       .set_d0ix = skl_set_d0ix,
+};
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
new file mode 100644 (file)
index 0000000..0d11cc8
--- /dev/null
@@ -0,0 +1,1598 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/uuid.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-topology.h>
+#include <uapi/sound/intel/avs/tokens.h>
+#include "avs.h"
+#include "topology.h"
+
+/* Get pointer to vendor array at the specified offset. */
+#define avs_tplg_vendor_array_at(array, offset) \
+       ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset))
+
+/* Get pointer to vendor array that is next in line. */
+#define avs_tplg_vendor_array_next(array) \
+       (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size)))
+
+/*
+ * Scan provided block of tuples for the specified token. If found,
+ * @offset is updated with position at which first matching token is
+ * located.
+ *
+ * Returns 0 on success, -ENOENT if not found and error code otherwise.
+ */
+static int
+avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples,
+                            u32 block_size, u32 token, u32 *offset)
+{
+       u32 pos = 0;
+
+       while (block_size > 0) {
+               struct snd_soc_tplg_vendor_value_elem *tuple;
+               u32 tuples_size = le32_to_cpu(tuples->size);
+
+               if (tuples_size > block_size)
+                       return -EINVAL;
+
+               tuple = tuples->value;
+               if (le32_to_cpu(tuple->token) == token) {
+                       *offset = pos;
+                       return 0;
+               }
+
+               block_size -= tuples_size;
+               pos += tuples_size;
+               tuples = avs_tplg_vendor_array_next(tuples);
+       }
+
+       return -ENOENT;
+}
+
+/*
+ * See avs_tplg_vendor_array_lookup() for description.
+ *
+ * Behaves exactly like avs_tplg_vendor_lookup() but starts from the
+ * next vendor array in line. Useful when searching for the finish line
+ * of an arbitrary entry in a list of entries where each is composed of
+ * several vendor tuples and a specific token marks the beginning of
+ * a new entry block.
+ */
+static int
+avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples,
+                                 u32 block_size, u32 token, u32 *offset)
+{
+       u32 tuples_size = le32_to_cpu(tuples->size);
+       int ret;
+
+       if (tuples_size > block_size)
+               return -EINVAL;
+
+       tuples = avs_tplg_vendor_array_next(tuples);
+       block_size -= tuples_size;
+
+       ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset);
+       if (!ret)
+               *offset += tuples_size;
+       return ret;
+}
+
+/*
+ * Scan provided block of tuples for the specified token which marks
+ * the border of an entry block. Behavior is similar to
+ * avs_tplg_vendor_array_lookup() except 0 is also returned if no
+ * matching token has been found. In such case, returned @size is
+ * assigned to @block_size as the entire block belongs to the current
+ * entry.
+ *
+ * Returns 0 on success, error code otherwise.
+ */
+static int
+avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples,
+                          u32 block_size, u32 entry_id_token, u32 *size)
+{
+       int ret;
+
+       ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, entry_id_token, size);
+       if (ret == -ENOENT) {
+               *size = block_size;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/*
+ * Vendor tuple parsing descriptor.
+ *
+ * @token: vendor specific token that identifies tuple
+ * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX
+ * @offset: offset of a struct's field to initialize
+ * @parse: parsing function, extracts and assigns value to object's field
+ */
+struct avs_tplg_token_parser {
+       enum avs_tplg_token token;
+       u32 type;
+       u32 offset;
+       int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset);
+};
+
+static int
+avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       guid_t *val = (guid_t *)((u8 *)object + offset);
+
+       guid_copy((guid_t *)val, (const guid_t *)&tuple->value);
+
+       return 0;
+}
+
+static int
+avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       bool *val = (bool *)((u8 *)object + offset);
+
+       *val = le32_to_cpu(tuple->value);
+
+       return 0;
+}
+
+static int
+avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       u8 *val = ((u8 *)object + offset);
+
+       *val = le32_to_cpu(tuple->value);
+
+       return 0;
+}
+
+static int
+avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       u16 *val = (u16 *)((u8 *)object + offset);
+
+       *val = le32_to_cpu(tuple->value);
+
+       return 0;
+}
+
+static int
+avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       u32 *val = (u32 *)((u8 *)object + offset);
+
+       *val = le32_to_cpu(tuple->value);
+
+       return 0;
+}
+
+static int
+avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+       char *val = (char *)((u8 *)object + offset);
+
+       snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", tuple->string);
+
+       return 0;
+}
+
+static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object,
+                                const struct avs_tplg_token_parser *parsers, int count,
+                                struct snd_soc_tplg_vendor_array *tuples)
+{
+       struct snd_soc_tplg_vendor_uuid_elem *tuple;
+       int ret, i, j;
+
+       /* Parse element by element. */
+       for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+               tuple = &tuples->uuid[i];
+
+               for (j = 0; j < count; j++) {
+                       /* Ignore non-UUID tokens. */
+                       if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID ||
+                           parsers[j].token != le32_to_cpu(tuple->token))
+                               continue;
+
+                       ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object,
+                                  const struct avs_tplg_token_parser *parsers, int count,
+                                  struct snd_soc_tplg_vendor_array *tuples)
+{
+       struct snd_soc_tplg_vendor_string_elem *tuple;
+       int ret, i, j;
+
+       /* Parse element by element. */
+       for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+               tuple = &tuples->string[i];
+
+               for (j = 0; j < count; j++) {
+                       /* Ignore non-string tokens. */
+                       if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING ||
+                           parsers[j].token != le32_to_cpu(tuple->token))
+                               continue;
+
+                       ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object,
+                                const struct avs_tplg_token_parser *parsers, int count,
+                                struct snd_soc_tplg_vendor_array *tuples)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple;
+       int ret, i, j;
+
+       /* Parse element by element. */
+       for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+               tuple = &tuples->value[i];
+
+               for (j = 0; j < count; j++) {
+                       /* Ignore non-integer tokens. */
+                       if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
+                             parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
+                             parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
+                             parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL))
+                               continue;
+
+                       if (parsers[j].token != le32_to_cpu(tuple->token))
+                               continue;
+
+                       ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int avs_parse_tokens(struct snd_soc_component *comp, void *object,
+                           const struct avs_tplg_token_parser *parsers, size_t count,
+                           struct snd_soc_tplg_vendor_array *tuples, int priv_size)
+{
+       int array_size, ret;
+
+       while (priv_size > 0) {
+               array_size = le32_to_cpu(tuples->size);
+
+               if (array_size <= 0) {
+                       dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+                       return -EINVAL;
+               }
+
+               /* Make sure there is enough data before parsing. */
+               priv_size -= array_size;
+               if (priv_size < 0) {
+                       dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+                       return -EINVAL;
+               }
+
+               switch (le32_to_cpu(tuples->type)) {
+               case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+                       ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples);
+                       break;
+               case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+                       ret = avs_parse_string_tokens(comp, object, parsers, count, tuples);
+                       break;
+               case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+               case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+               case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+               case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+                       ret = avs_parse_word_tokens(comp, object, parsers, count, tuples);
+                       break;
+               default:
+                       dev_err(comp->dev, "unknown token type %d\n", tuples->type);
+                       ret = -EINVAL;
+               }
+
+               if (ret) {
+                       dev_err(comp->dev, "parsing %zu tokens of %d type failed: %d\n",
+                               count, tuples->type, ret);
+                       return ret;
+               }
+
+               tuples = avs_tplg_vendor_array_next(tuples);
+       }
+
+       return 0;
+}
+
+#define AVS_DEFINE_PTR_PARSER(name, type, member) \
+static int \
+avs_parse_##name##_ptr(struct snd_soc_component *comp, void *elem, void *object, u32 offset) \
+{ \
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;            \
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);   \
+       type **val = (type **)(object + offset);                        \
+       u32 idx;                                                        \
+                                                                       \
+       idx = le32_to_cpu(tuple->value);                                \
+       if (idx >= acomp->tplg->num_##member)                           \
+               return -EINVAL;                                         \
+                                                                       \
+       *val = &acomp->tplg->member[idx];                               \
+                                                                       \
+       return 0;                                                       \
+}
+
+AVS_DEFINE_PTR_PARSER(audio_format, struct avs_audio_format, fmts);
+AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base);
+AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext);
+AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs);
+AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings);
+
+static int
+parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *velem = elem;
+       struct avs_audio_format *audio_format = object;
+
+       switch (offset) {
+       case AVS_TKN_AFMT_NUM_CHANNELS_U32:
+               audio_format->num_channels = le32_to_cpu(velem->value);
+               break;
+       case AVS_TKN_AFMT_VALID_BIT_DEPTH_U32:
+               audio_format->valid_bit_depth = le32_to_cpu(velem->value);
+               break;
+       case AVS_TKN_AFMT_SAMPLE_TYPE_U32:
+               audio_format->sample_type = le32_to_cpu(velem->value);
+               break;
+       }
+
+       return 0;
+}
+
+static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem,
+                                      void *object, u32 offset)
+{
+       struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+       struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+       char *val = (char *)((u8 *)object + offset);
+
+       /*
+        * Dynamic naming - string formats, e.g.: ssp%d - supported only for
+        * topologies describing single device e.g.: an I2S codec on SSP0.
+        */
+       if (hweight_long(mach->mach_params.i2s_link_mask) != 1)
+               return avs_parse_string_token(comp, elem, object, offset);
+
+       snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string,
+                __ffs(mach->mach_params.i2s_link_mask));
+
+       return 0;
+}
+
+static int
+parse_dictionary_header(struct snd_soc_component *comp,
+                       struct snd_soc_tplg_vendor_array *tuples,
+                       void **dict, u32 *num_entries, size_t entry_size,
+                       u32 num_entries_token)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple;
+
+       /* Dictionary header consists of single tuple - entry count. */
+       tuple = tuples->value;
+       if (le32_to_cpu(tuple->token) != num_entries_token) {
+               dev_err(comp->dev, "invalid dictionary header, expected: %d\n",
+                       num_entries_token);
+               return -EINVAL;
+       }
+
+       *num_entries = le32_to_cpu(tuple->value);
+       *dict = devm_kcalloc(comp->card->dev, *num_entries, entry_size, GFP_KERNEL);
+       if (!*dict)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int
+parse_dictionary_entries(struct snd_soc_component *comp,
+                        struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+                        void *dict, u32 num_entries, size_t entry_size,
+                        u32 entry_id_token,
+                        const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+       void *pos = dict;
+       int i;
+
+       for (i = 0; i < num_entries; i++) {
+               u32 esize;
+               int ret;
+
+               ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                                entry_id_token, &esize);
+               if (ret)
+                       return ret;
+
+               ret = avs_parse_tokens(comp, pos, parsers, num_parsers, tuples, esize);
+               if (ret < 0) {
+                       dev_err(comp->dev, "parse entry: %d of type: %d failed: %d\n",
+                               i, entry_id_token, ret);
+                       return ret;
+               }
+
+               pos += entry_size;
+               block_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       }
+
+       return 0;
+}
+
+static int parse_dictionary(struct snd_soc_component *comp,
+                           struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+                           void **dict, u32 *num_entries, size_t entry_size,
+                           u32 num_entries_token, u32 entry_id_token,
+                           const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+       int ret;
+
+       ret = parse_dictionary_header(comp, tuples, dict, num_entries,
+                                     entry_size, num_entries_token);
+       if (ret)
+               return ret;
+
+       block_size -= le32_to_cpu(tuples->size);
+       /* With header parsed, move on to parsing entries. */
+       tuples = avs_tplg_vendor_array_next(tuples);
+
+       return parse_dictionary_entries(comp, tuples, block_size, *dict,
+                                       *num_entries, entry_size,
+                                       entry_id_token, parsers, num_parsers);
+}
+
+static const struct avs_tplg_token_parser library_parsers[] = {
+       {
+               .token = AVS_TKN_LIBRARY_NAME_STRING,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+               .offset = offsetof(struct avs_tplg_library, name),
+               .parse = avs_parse_string_token,
+       },
+};
+
+static int avs_tplg_parse_libraries(struct snd_soc_component *comp,
+                                   struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+
+       return parse_dictionary(comp, tuples, block_size, (void **)&tplg->libs,
+                               &tplg->num_libs, sizeof(*tplg->libs),
+                               AVS_TKN_MANIFEST_NUM_LIBRARIES_U32,
+                               AVS_TKN_LIBRARY_ID_U32,
+                               library_parsers, ARRAY_SIZE(library_parsers));
+}
+
+static const struct avs_tplg_token_parser audio_format_parsers[] = {
+       {
+               .token = AVS_TKN_AFMT_SAMPLE_RATE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_audio_format, sampling_freq),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_AFMT_BIT_DEPTH_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_audio_format, bit_depth),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_AFMT_CHANNEL_MAP_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_audio_format, channel_map),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_AFMT_CHANNEL_CFG_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_audio_format, channel_config),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_AFMT_INTERLEAVING_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_audio_format, interleaving),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+               .parse = parse_audio_format_bitfield,
+       },
+       {
+               .token = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+               .parse = parse_audio_format_bitfield,
+       },
+       {
+               .token = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+               .parse = parse_audio_format_bitfield,
+       },
+};
+
+static int avs_tplg_parse_audio_formats(struct snd_soc_component *comp,
+                                       struct snd_soc_tplg_vendor_array *tuples,
+                                       u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+
+       return parse_dictionary(comp, tuples, block_size, (void **)&tplg->fmts,
+                               &tplg->num_fmts, sizeof(*tplg->fmts),
+                               AVS_TKN_MANIFEST_NUM_AFMTS_U32,
+                               AVS_TKN_AFMT_ID_U32,
+                               audio_format_parsers, ARRAY_SIZE(audio_format_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_base_parsers[] = {
+       {
+               .token = AVS_TKN_MODCFG_BASE_CPC_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_base, cpc),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_BASE_IBS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_base, ibs),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_BASE_OBS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_base, obs),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_BASE_PAGES_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_base, is_pages),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static int avs_tplg_parse_modcfgs_base(struct snd_soc_component *comp,
+                                      struct snd_soc_tplg_vendor_array *tuples,
+                                      u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+
+       return parse_dictionary(comp, tuples, block_size, (void **)&tplg->modcfgs_base,
+                               &tplg->num_modcfgs_base, sizeof(*tplg->modcfgs_base),
+                               AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32,
+                               AVS_TKN_MODCFG_BASE_ID_U32,
+                               modcfg_base_parsers, ARRAY_SIZE(modcfg_base_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
+       {
+               .token = AVS_TKN_MODCFG_EXT_TYPE_UUID,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_UUID,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, type),
+               .parse = avs_parse_uuid_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.out_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.feature_mask),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_VINDEX_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.vindex),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_DMA_TYPE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_type),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_buffer_size),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, copier.blob_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, micsel.out_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, wov.cpc_lp_mode),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_SRC_OUT_FREQ_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, src.out_freq),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, mux.ref_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, mux.out_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, aec.ref_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, aec.out_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, aec.cpc_lp_mode),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.out_freq),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_ASRC_MODE_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.mode),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.disable_jitter_buffer),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.out_channel_config),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients_select),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[0]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[1]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[2]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[3]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[4]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[5]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[6]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[7]),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.channel_map),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_input_pins),
+               .parse = avs_parse_short_token,
+       },
+       {
+               .token = AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+               .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins),
+               .parse = avs_parse_short_token,
+       },
+};
+
+static const struct avs_tplg_token_parser pin_format_parsers[] = {
+       {
+               .token = AVS_TKN_PIN_FMT_INDEX_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pin_format, pin_index),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_PIN_FMT_IOBS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pin_format, iobs),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_PIN_FMT_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pin_format, fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+};
+
+static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp,
+                                    struct avs_tplg_modcfg_ext *cfg,
+                                    struct snd_soc_tplg_vendor_array *tuples,
+                                    u32 block_size)
+{
+       u32 esize;
+       int ret;
+
+       /* See where pin block starts. */
+       ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                        AVS_TKN_PIN_FMT_INDEX_U32, &esize);
+       if (ret)
+               return ret;
+
+       ret = avs_parse_tokens(comp, cfg, modcfg_ext_parsers,
+                              ARRAY_SIZE(modcfg_ext_parsers), tuples, esize);
+       if (ret)
+               return ret;
+
+       block_size -= esize;
+       /* Parse trailing in/out pin formats if any. */
+       if (block_size) {
+               struct avs_tplg_pin_format *pins;
+               u32 num_pins;
+
+               num_pins = cfg->generic.num_input_pins + cfg->generic.num_output_pins;
+               if (!num_pins)
+                       return -EINVAL;
+
+               pins = devm_kcalloc(comp->card->dev, num_pins, sizeof(*pins), GFP_KERNEL);
+               if (!pins)
+                       return -ENOMEM;
+
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+               ret = parse_dictionary_entries(comp, tuples, block_size,
+                                              pins, num_pins, sizeof(*pins),
+                                              AVS_TKN_PIN_FMT_INDEX_U32,
+                                              pin_format_parsers,
+                                              ARRAY_SIZE(pin_format_parsers));
+               if (ret)
+                       return ret;
+               cfg->generic.pin_fmts = pins;
+       }
+
+       return 0;
+}
+
+static int avs_tplg_parse_modcfgs_ext(struct snd_soc_component *comp,
+                                     struct snd_soc_tplg_vendor_array *tuples,
+                                     u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+       int ret, i;
+
+       ret = parse_dictionary_header(comp, tuples, (void **)&tplg->modcfgs_ext,
+                                     &tplg->num_modcfgs_ext,
+                                     sizeof(*tplg->modcfgs_ext),
+                                     AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32);
+       if (ret)
+               return ret;
+
+       block_size -= le32_to_cpu(tuples->size);
+       /* With header parsed, move on to parsing entries. */
+       tuples = avs_tplg_vendor_array_next(tuples);
+
+       for (i = 0; i < tplg->num_modcfgs_ext; i++) {
+               struct avs_tplg_modcfg_ext *cfg = &tplg->modcfgs_ext[i];
+               u32 esize;
+
+               ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                                AVS_TKN_MODCFG_EXT_ID_U32, &esize);
+               if (ret)
+                       return ret;
+
+               ret = avs_tplg_parse_modcfg_ext(comp, cfg, tuples, esize);
+               if (ret)
+                       return ret;
+
+               block_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       }
+
+       return 0;
+}
+
+static const struct avs_tplg_token_parser pplcfg_parsers[] = {
+       {
+               .token = AVS_TKN_PPLCFG_REQ_SIZE_U16,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+               .offset = offsetof(struct avs_tplg_pplcfg, req_size),
+               .parse = avs_parse_short_token,
+       },
+       {
+               .token = AVS_TKN_PPLCFG_PRIORITY_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_pplcfg, priority),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_PPLCFG_LOW_POWER_BOOL,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL,
+               .offset = offsetof(struct avs_tplg_pplcfg, lp),
+               .parse = avs_parse_bool_token,
+       },
+       {
+               .token = AVS_TKN_PPLCFG_ATTRIBUTES_U16,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+               .offset = offsetof(struct avs_tplg_pplcfg, attributes),
+               .parse = avs_parse_short_token,
+       },
+       {
+               .token = AVS_TKN_PPLCFG_TRIGGER_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pplcfg, trigger),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static int avs_tplg_parse_pplcfgs(struct snd_soc_component *comp,
+                                 struct snd_soc_tplg_vendor_array *tuples,
+                                 u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+
+       return parse_dictionary(comp, tuples, block_size, (void **)&tplg->pplcfgs,
+                               &tplg->num_pplcfgs, sizeof(*tplg->pplcfgs),
+                               AVS_TKN_MANIFEST_NUM_PPLCFGS_U32,
+                               AVS_TKN_PPLCFG_ID_U32,
+                               pplcfg_parsers, ARRAY_SIZE(pplcfg_parsers));
+}
+
+static const struct avs_tplg_token_parser binding_parsers[] = {
+       {
+               .token = AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+               .offset = offsetof(struct avs_tplg_binding, target_tplg_name),
+               .parse = parse_link_formatted_string,
+       },
+       {
+               .token = AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_binding, target_path_tmpl_id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_TARGET_PPL_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_binding, target_ppl_id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_TARGET_MOD_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_binding, target_mod_id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_TARGET_MOD_PIN_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_binding, target_mod_pin),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_MOD_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_binding, mod_id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_MOD_PIN_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_binding, mod_pin),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_BINDING_IS_SINK_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_binding, is_sink),
+               .parse = avs_parse_byte_token,
+       },
+};
+
+static int avs_tplg_parse_bindings(struct snd_soc_component *comp,
+                                  struct snd_soc_tplg_vendor_array *tuples,
+                                  u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+
+       return parse_dictionary(comp, tuples, block_size, (void **)&tplg->bindings,
+                               &tplg->num_bindings, sizeof(*tplg->bindings),
+                               AVS_TKN_MANIFEST_NUM_BINDINGS_U32,
+                               AVS_TKN_BINDING_ID_U32,
+                               binding_parsers, ARRAY_SIZE(binding_parsers));
+}
+
+static const struct avs_tplg_token_parser module_parsers[] = {
+       {
+               .token = AVS_TKN_MOD_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_module, id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_MOD_MODCFG_BASE_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_module, cfg_base),
+               .parse = avs_parse_modcfg_base_ptr,
+       },
+       {
+               .token = AVS_TKN_MOD_IN_AFMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_module, in_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_MOD_CORE_ID_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_module, core_id),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_MOD_PROC_DOMAIN_U8,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+               .offset = offsetof(struct avs_tplg_module, domain),
+               .parse = avs_parse_byte_token,
+       },
+       {
+               .token = AVS_TKN_MOD_MODCFG_EXT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_module, cfg_ext),
+               .parse = avs_parse_modcfg_ext_ptr,
+       },
+};
+
+static struct avs_tplg_module *
+avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner,
+                      struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+       struct avs_tplg_module *module;
+       int ret;
+
+       module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
+       if (!module)
+               return ERR_PTR(-ENOMEM);
+
+       ret = avs_parse_tokens(comp, module, module_parsers,
+                              ARRAY_SIZE(module_parsers), tuples, block_size);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       module->owner = owner;
+       INIT_LIST_HEAD(&module->node);
+
+       return module;
+}
+
+static const struct avs_tplg_token_parser pipeline_parsers[] = {
+       {
+               .token = AVS_TKN_PPL_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pipeline, id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_PPL_PPLCFG_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pipeline, cfg),
+               .parse = avs_parse_pplcfg_ptr,
+       },
+       {
+               .token = AVS_TKN_PPL_NUM_BINDING_IDS_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_pipeline, num_bindings),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static const struct avs_tplg_token_parser bindings_parsers[] = {
+       {
+               .token = AVS_TKN_PPL_BINDING_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = 0, /* to treat pipeline->bindings as dictionary */
+               .parse = avs_parse_binding_ptr,
+       },
+};
+
+static struct avs_tplg_pipeline *
+avs_tplg_pipeline_create(struct snd_soc_component *comp, struct avs_tplg_path *owner,
+                        struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+       struct avs_tplg_pipeline *pipeline;
+       u32 modblk_size, offset;
+       int ret;
+
+       pipeline = devm_kzalloc(comp->card->dev, sizeof(*pipeline), GFP_KERNEL);
+       if (!pipeline)
+               return ERR_PTR(-ENOMEM);
+
+       pipeline->owner = owner;
+       INIT_LIST_HEAD(&pipeline->mod_list);
+
+       /* Pipeline header MUST be followed by at least one module. */
+       ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+                                          AVS_TKN_MOD_ID_U32, &offset);
+       if (!ret && !offset)
+               ret = -EINVAL;
+       if (ret)
+               return ERR_PTR(ret);
+
+       /* Process header which precedes module sections. */
+       ret = avs_parse_tokens(comp, pipeline, pipeline_parsers,
+                              ARRAY_SIZE(pipeline_parsers), tuples, offset);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       block_size -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       /* Optionally, binding sections follow module ones. */
+       ret = avs_tplg_vendor_array_lookup_next(tuples, block_size,
+                                               AVS_TKN_PPL_BINDING_ID_U32, &offset);
+       if (ret) {
+               if (ret != -ENOENT)
+                       return ERR_PTR(ret);
+
+               /* Does header information match actual block layout? */
+               if (pipeline->num_bindings)
+                       return ERR_PTR(-EINVAL);
+
+               modblk_size = block_size;
+       } else {
+               pipeline->bindings = devm_kcalloc(comp->card->dev, pipeline->num_bindings,
+                                                 sizeof(*pipeline->bindings), GFP_KERNEL);
+               if (!pipeline->bindings)
+                       return ERR_PTR(-ENOMEM);
+
+               modblk_size = offset;
+       }
+
+       block_size -= modblk_size;
+       do {
+               struct avs_tplg_module *module;
+               u32 esize;
+
+               ret = avs_tplg_vendor_entry_size(tuples, modblk_size,
+                                                AVS_TKN_MOD_ID_U32, &esize);
+               if (ret)
+                       return ERR_PTR(ret);
+
+               module = avs_tplg_module_create(comp, pipeline, tuples, esize);
+               if (IS_ERR(module)) {
+                       dev_err(comp->dev, "parse module failed: %ld\n",
+                               PTR_ERR(module));
+                       return ERR_CAST(module);
+               }
+
+               list_add_tail(&module->node, &pipeline->mod_list);
+               modblk_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       } while (modblk_size > 0);
+
+       /* What's left is optional range of bindings. */
+       ret = parse_dictionary_entries(comp, tuples, block_size, pipeline->bindings,
+                                      pipeline->num_bindings, sizeof(*pipeline->bindings),
+                                      AVS_TKN_PPL_BINDING_ID_U32,
+                                      bindings_parsers, ARRAY_SIZE(bindings_parsers));
+       if (ret)
+               return ERR_PTR(ret);
+
+       return pipeline;
+}
+
+static const struct avs_tplg_token_parser path_parsers[] = {
+       {
+               .token = AVS_TKN_PATH_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_path, id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_PATH_FE_FMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_path, fe_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+       {
+               .token = AVS_TKN_PATH_BE_FMT_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_path, be_fmt),
+               .parse = avs_parse_audio_format_ptr,
+       },
+};
+
+static struct avs_tplg_path *
+avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
+                    struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+                    const struct avs_tplg_token_parser *parsers, u32 num_parsers)
+{
+       struct avs_tplg_pipeline *pipeline;
+       struct avs_tplg_path *path;
+       u32 offset;
+       int ret;
+
+       path = devm_kzalloc(comp->card->dev, sizeof(*path), GFP_KERNEL);
+       if (!path)
+               return ERR_PTR(-ENOMEM);
+
+       path->owner = owner;
+       INIT_LIST_HEAD(&path->ppl_list);
+       INIT_LIST_HEAD(&path->node);
+
+       /* Path header MAY be followed by one or more pipelines. */
+       ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+                                          AVS_TKN_PPL_ID_U32, &offset);
+       if (ret == -ENOENT)
+               offset = block_size;
+       else if (ret)
+               return ERR_PTR(ret);
+       else if (!offset)
+               return ERR_PTR(-EINVAL);
+
+       /* Process header which precedes pipeline sections. */
+       ret = avs_parse_tokens(comp, path, parsers, num_parsers, tuples, offset);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       block_size -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+       while (block_size > 0) {
+               u32 esize;
+
+               ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                                AVS_TKN_PPL_ID_U32, &esize);
+               if (ret)
+                       return ERR_PTR(ret);
+
+               pipeline = avs_tplg_pipeline_create(comp, path, tuples, esize);
+               if (IS_ERR(pipeline)) {
+                       dev_err(comp->dev, "parse pipeline failed: %ld\n",
+                               PTR_ERR(pipeline));
+                       return ERR_CAST(pipeline);
+               }
+
+               list_add_tail(&pipeline->node, &path->ppl_list);
+               block_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       }
+
+       return path;
+}
+
+static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
+       {
+               .token = AVS_TKN_PATH_TMPL_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_path_template, id),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static int parse_path_template(struct snd_soc_component *comp,
+                              struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+                              struct avs_tplg_path_template *template,
+                              const struct avs_tplg_token_parser *tmpl_tokens, u32 num_tmpl_tokens,
+                              const struct avs_tplg_token_parser *path_tokens, u32 num_path_tokens)
+{
+       struct avs_tplg_path *path;
+       u32 offset;
+       int ret;
+
+       /* Path template header MUST be followed by at least one path variant. */
+       ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+                                          AVS_TKN_PATH_ID_U32, &offset);
+       if (ret)
+               return ret;
+
+       /* Process header which precedes path variants sections. */
+       ret = avs_parse_tokens(comp, template, tmpl_tokens, num_tmpl_tokens, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       block_size -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+       do {
+               u32 esize;
+
+               ret = avs_tplg_vendor_entry_size(tuples, block_size,
+                                                AVS_TKN_PATH_ID_U32, &esize);
+               if (ret)
+                       return ret;
+
+               path = avs_tplg_path_create(comp, template, tuples, esize, path_tokens,
+                                           num_path_tokens);
+               if (IS_ERR(path)) {
+                       dev_err(comp->dev, "parse path failed: %ld\n", PTR_ERR(path));
+                       return PTR_ERR(path);
+               }
+
+               list_add_tail(&path->node, &template->path_list);
+               block_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       } while (block_size > 0);
+
+       return 0;
+}
+
+static struct avs_tplg_path_template *
+avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *owner,
+                             struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+       struct avs_tplg_path_template *template;
+       int ret;
+
+       template = devm_kzalloc(comp->card->dev, sizeof(*template), GFP_KERNEL);
+       if (!template)
+               return ERR_PTR(-ENOMEM);
+
+       template->owner = owner; /* Used to access component tplg is assigned to. */
+       INIT_LIST_HEAD(&template->path_list);
+       INIT_LIST_HEAD(&template->node);
+
+       ret = parse_path_template(comp, tuples, block_size, template, path_tmpl_parsers,
+                                 ARRAY_SIZE(path_tmpl_parsers), path_parsers,
+                                 ARRAY_SIZE(path_parsers));
+       if (ret)
+               return ERR_PTR(ret);
+
+       return template;
+}
+
+static int avs_route_load(struct snd_soc_component *comp, int index,
+                         struct snd_soc_dapm_route *route)
+{
+       struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+       size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
+       char buf[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       u32 port;
+
+       /* See parse_link_formatted_string() for dynamic naming when(s). */
+       if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
+               port = __ffs(mach->mach_params.i2s_link_mask);
+
+               snprintf(buf, len, route->source, port);
+               strncpy((char *)route->source, buf, len);
+               snprintf(buf, len, route->sink, port);
+               strncpy((char *)route->sink, buf, len);
+               if (route->control) {
+                       snprintf(buf, len, route->control, port);
+                       strncpy((char *)route->control, buf, len);
+               }
+       }
+
+       return 0;
+}
+
+static int avs_widget_load(struct snd_soc_component *comp, int index,
+                          struct snd_soc_dapm_widget *w,
+                          struct snd_soc_tplg_dapm_widget *dw)
+{
+       struct snd_soc_acpi_mach *mach;
+       struct avs_tplg_path_template *template;
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg;
+
+       if (!le32_to_cpu(dw->priv.size))
+               return 0;
+
+       tplg = acomp->tplg;
+       mach = dev_get_platdata(comp->card->dev);
+
+       /* See parse_link_formatted_string() for dynamic naming when(s). */
+       if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
+               kfree(w->name);
+               /* w->name is freed later by soc_tplg_dapm_widget_create() */
+               w->name = kasprintf(GFP_KERNEL, dw->name, __ffs(mach->mach_params.i2s_link_mask));
+               if (!w->name)
+                       return -ENOMEM;
+       }
+
+       template = avs_tplg_path_template_create(comp, tplg, dw->priv.array,
+                                                le32_to_cpu(dw->priv.size));
+       if (IS_ERR(template)) {
+               dev_err(comp->dev, "widget %s load failed: %ld\n", dw->name,
+                       PTR_ERR(template));
+               return PTR_ERR(template);
+       }
+
+       w->priv = template; /* link path information to widget */
+       list_add_tail(&template->node, &tplg->path_tmpl_list);
+       return 0;
+}
+
+static int avs_dai_load(struct snd_soc_component *comp, int index,
+                       struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
+                       struct snd_soc_dai *dai)
+{
+       if (pcm)
+               dai_drv->ops = &avs_dai_fe_ops;
+       return 0;
+}
+
+static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link,
+                        struct snd_soc_tplg_link_config *cfg)
+{
+       if (!link->no_pcm) {
+               /* Stream control handled by IPCs. */
+               link->nonatomic = true;
+
+               /* Open LINK (BE) pipes last and close them first to prevent xruns. */
+               link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
+               link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
+       }
+
+       return 0;
+}
+
+static const struct avs_tplg_token_parser manifest_parsers[] = {
+       {
+               .token = AVS_TKN_MANIFEST_NAME_STRING,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+               .offset = offsetof(struct avs_tplg, name),
+               .parse = parse_link_formatted_string,
+       },
+       {
+               .token = AVS_TKN_MANIFEST_VERSION_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg, version),
+               .parse = avs_parse_word_token,
+       },
+};
+
+static int avs_manifest(struct snd_soc_component *comp, int index,
+                       struct snd_soc_tplg_manifest *manifest)
+{
+       struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       size_t remaining = le32_to_cpu(manifest->priv.size);
+       u32 offset;
+       int ret;
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, &offset);
+       /* Manifest MUST begin with a header. */
+       if (!ret && !offset)
+               ret = -EINVAL;
+       if (ret) {
+               dev_err(comp->dev, "incorrect manifest format: %d\n", ret);
+               return ret;
+       }
+
+       /* Process header which precedes any of the dictionaries. */
+       ret = avs_parse_tokens(comp, acomp->tplg, manifest_parsers,
+                              ARRAY_SIZE(manifest_parsers), tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_AFMTS_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "audio formats lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Libraries dictionary. */
+       ret = avs_tplg_parse_libraries(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "modcfgs_base lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Audio formats dictionary. */
+       ret = avs_tplg_parse_audio_formats(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "modcfgs_ext lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Module configs-base dictionary. */
+       ret = avs_tplg_parse_modcfgs_base(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "pplcfgs lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Module configs-ext dictionary. */
+       ret = avs_tplg_parse_modcfgs_ext(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_BINDINGS_U32, &offset);
+       if (ret) {
+               dev_err(comp->dev, "bindings lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Pipeline configs dictionary. */
+       ret = avs_tplg_parse_pplcfgs(comp, tuples, offset);
+       if (ret < 0)
+               return ret;
+
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       /* Bindings dictionary. */
+       return avs_tplg_parse_bindings(comp, tuples, remaining);
+}
+
+static struct snd_soc_tplg_ops avs_tplg_ops = {
+       .dapm_route_load        = avs_route_load,
+       .widget_load            = avs_widget_load,
+       .dai_load               = avs_dai_load,
+       .link_load              = avs_link_load,
+       .manifest               = avs_manifest,
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp)
+{
+       struct avs_tplg *tplg;
+
+       tplg = devm_kzalloc(comp->card->dev, sizeof(*tplg), GFP_KERNEL);
+       if (!tplg)
+               return NULL;
+
+       tplg->comp = comp;
+       INIT_LIST_HEAD(&tplg->path_tmpl_list);
+
+       return tplg;
+}
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename)
+{
+       const struct firmware *fw;
+       int ret;
+
+       ret = request_firmware(&fw, filename, comp->dev);
+       if (ret < 0) {
+               dev_err(comp->dev, "request topology \"%s\" failed: %d\n", filename, ret);
+               return ret;
+       }
+
+       ret = snd_soc_tplg_component_load(comp, &avs_tplg_ops, fw);
+       if (ret < 0)
+               dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret);
+
+       release_firmware(fw);
+       return ret;
+}
+
+int avs_remove_topology(struct snd_soc_component *comp)
+{
+       snd_soc_tplg_component_remove(comp);
+
+       return 0;
+}
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
new file mode 100644 (file)
index 0000000..68e5f63
--- /dev/null
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_TPLG_H
+#define __SOUND_SOC_INTEL_AVS_TPLG_H
+
+#include <linux/list.h>
+#include "messages.h"
+
+#define INVALID_OBJECT_ID      UINT_MAX
+
+struct snd_soc_component;
+
+struct avs_tplg {
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       u32 version;
+       struct snd_soc_component *comp;
+
+       struct avs_tplg_library *libs;
+       u32 num_libs;
+       struct avs_audio_format *fmts;
+       u32 num_fmts;
+       struct avs_tplg_modcfg_base *modcfgs_base;
+       u32 num_modcfgs_base;
+       struct avs_tplg_modcfg_ext *modcfgs_ext;
+       u32 num_modcfgs_ext;
+       struct avs_tplg_pplcfg *pplcfgs;
+       u32 num_pplcfgs;
+       struct avs_tplg_binding *bindings;
+       u32 num_bindings;
+
+       struct list_head path_tmpl_list;
+};
+
+struct avs_tplg_library {
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+/* Matches header of struct avs_mod_cfg_base. */
+struct avs_tplg_modcfg_base {
+       u32 cpc;
+       u32 ibs;
+       u32 obs;
+       u32 is_pages;
+};
+
+struct avs_tplg_pin_format {
+       u32 pin_index;
+       u32 iobs;
+       struct avs_audio_format *fmt;
+};
+
+struct avs_tplg_modcfg_ext {
+       guid_t type;
+
+       union {
+               struct {
+                       u16 num_input_pins;
+                       u16 num_output_pins;
+                       struct avs_tplg_pin_format *pin_fmts;
+               } generic;
+               struct {
+                       struct avs_audio_format *out_fmt;
+                       struct avs_audio_format *blob_fmt; /* optional override */
+                       u32 feature_mask;
+                       union avs_virtual_index vindex;
+                       u32 dma_type;
+                       u32 dma_buffer_size;
+                       u32 config_length;
+                       /* config_data part of priv data */
+               } copier;
+               struct {
+                       u32 out_channel_config;
+                       u32 coefficients_select;
+                       s32 coefficients[AVS_CHANNELS_MAX];
+                       u32 channel_map;
+               } updown_mix;
+               struct {
+                       u32 out_freq;
+               } src;
+               struct {
+                       u32 out_freq;
+                       u8 mode;
+                       u8 disable_jitter_buffer;
+               } asrc;
+               struct {
+                       u32 cpc_lp_mode;
+               } wov;
+               struct {
+                       struct avs_audio_format *ref_fmt;
+                       struct avs_audio_format *out_fmt;
+                       u32 cpc_lp_mode;
+               } aec;
+               struct {
+                       struct avs_audio_format *ref_fmt;
+                       struct avs_audio_format *out_fmt;
+               } mux;
+               struct {
+                       struct avs_audio_format *out_fmt;
+               } micsel;
+       };
+};
+
+/* Specifies path behaviour during PCM ->trigger(START) command. */
+enum avs_tplg_trigger {
+       AVS_TPLG_TRIGGER_AUTO = 0,
+};
+
+struct avs_tplg_pplcfg {
+       u16 req_size;
+       u8 priority;
+       bool lp;
+       u16 attributes;
+       enum avs_tplg_trigger trigger;
+};
+
+struct avs_tplg_binding {
+       char target_tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       u32 target_path_tmpl_id;
+       u32 target_ppl_id;
+       u32 target_mod_id;
+       u8 target_mod_pin;
+       u32 mod_id;
+       u8 mod_pin;
+       u8 is_sink;
+};
+
+struct avs_tplg_path_template_id {
+       u32 id;
+       char tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+struct avs_tplg_path_template {
+       u32 id;
+
+       struct list_head path_list;
+
+       struct avs_tplg *owner;
+       /* Driver path templates management. */
+       struct list_head node;
+};
+
+struct avs_tplg_path {
+       u32 id;
+
+       /* Path format requirements. */
+       struct avs_audio_format *fe_fmt;
+       struct avs_audio_format *be_fmt;
+
+       struct list_head ppl_list;
+
+       struct avs_tplg_path_template *owner;
+       /* Path template path-variants management. */
+       struct list_head node;
+};
+
+struct avs_tplg_pipeline {
+       u32 id;
+
+       struct avs_tplg_pplcfg *cfg;
+       struct avs_tplg_binding **bindings;
+       u32 num_bindings;
+       struct list_head mod_list;
+
+       struct avs_tplg_path *owner;
+       /* Path pipelines management. */
+       struct list_head node;
+};
+
+struct avs_tplg_module {
+       u32 id;
+
+       struct avs_tplg_modcfg_base *cfg_base;
+       struct avs_audio_format *in_fmt;
+       u8 core_id;
+       u8 domain;
+       struct avs_tplg_modcfg_ext *cfg_ext;
+
+       struct avs_tplg_pipeline *owner;
+       /* Pipeline modules management. */
+       struct list_head node;
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp);
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename);
+int avs_remove_topology(struct snd_soc_component *comp);
+
+#endif
diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c
new file mode 100644 (file)
index 0000000..fcb7cfc
--- /dev/null
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//         Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/types.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#define BYTES_PER_LINE 16
+#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */ \
+                       / (2 * BYTES_PER_LINE + 4) /* chars per line */ \
+                       * BYTES_PER_LINE)
+
+void trace_avs_msg_payload(const void *data, size_t size)
+{
+       size_t remaining = size;
+       size_t offset = 0;
+
+       while (remaining > 0) {
+               u32 chunk;
+
+               chunk = min(remaining, (size_t)MAX_CHUNK_SIZE);
+               trace_avs_ipc_msg_payload(data, chunk, offset, size);
+
+               remaining -= chunk;
+               offset += chunk;
+       }
+}
diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h
new file mode 100644 (file)
index 0000000..855b06b
--- /dev/null
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_avs
+
+#if !defined(_TRACE_INTEL_AVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_INTEL_AVS_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(avs_dsp_core_op,
+
+       TP_PROTO(unsigned int reg, unsigned int mask, const char *op, bool flag),
+
+       TP_ARGS(reg, mask, op, flag),
+
+       TP_STRUCT__entry(
+               __field(unsigned int,   reg     )
+               __field(unsigned int,   mask    )
+               __string(op,            op      )
+               __field(bool,           flag    )
+       ),
+
+       TP_fast_assign(
+               __entry->reg = reg;
+               __entry->mask = mask;
+               __assign_str(op, op);
+               __entry->flag = flag;
+       ),
+
+       TP_printk("%s: %d, core mask: 0x%X, prev state: 0x%08X",
+                 __get_str(op), __entry->flag, __entry->mask, __entry->reg)
+);
+
+#ifndef __TRACE_INTEL_AVS_TRACE_HELPER
+#define __TRACE_INTEL_AVS_TRACE_HELPER
+
+void trace_avs_msg_payload(const void *data, size_t size);
+
+#define trace_avs_request(msg, fwregs) \
+({ \
+       trace_avs_ipc_request_msg((msg)->header, fwregs); \
+       trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_reply(msg, fwregs) \
+({ \
+       trace_avs_ipc_reply_msg((msg)->header, fwregs); \
+       trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_notify(msg, fwregs) \
+({ \
+       trace_avs_ipc_notify_msg((msg)->header, fwregs); \
+       trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+#endif
+
+DECLARE_EVENT_CLASS(avs_ipc_msg_hdr,
+
+       TP_PROTO(u64 header, u64 fwregs),
+
+       TP_ARGS(header, fwregs),
+
+       TP_STRUCT__entry(
+               __field(u64,    header)
+               __field(u64,    fwregs)
+       ),
+
+       TP_fast_assign(
+               __entry->header = header;
+               __entry->fwregs = fwregs;
+       ),
+
+       TP_printk("primary: 0x%08X, extension: 0x%08X,\n"
+                 "fwstatus: 0x%08X, fwerror: 0x%08X",
+                 lower_32_bits(__entry->header), upper_32_bits(__entry->header),
+                 lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs))
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg,
+       TP_PROTO(u64 header, u64 fwregs),
+       TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg,
+       TP_PROTO(u64 header, u64 fwregs),
+       TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg,
+       TP_PROTO(u64 header, u64 fwregs),
+       TP_ARGS(header, fwregs)
+);
+
+TRACE_EVENT_CONDITION(avs_ipc_msg_payload,
+
+       TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total),
+
+       TP_ARGS(data, size, offset, total),
+
+       TP_CONDITION(data && size),
+
+       TP_STRUCT__entry(
+               __dynamic_array(u8,     buf,    size    )
+               __field(size_t,         offset          )
+               __field(size_t,         pos             )
+               __field(size_t,         total           )
+       ),
+
+       TP_fast_assign(
+               memcpy(__get_dynamic_array(buf), data + offset, size);
+               __entry->offset = offset;
+               __entry->pos = offset + size;
+               __entry->total = total;
+       ),
+
+       TP_printk("range %zu-%zu out of %zu bytes%s",
+                 __entry->offset, __entry->pos, __entry->total,
+                 __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
+                                  __get_dynamic_array(buf),
+                                  __get_dynamic_array_len(buf), false))
+);
+
+TRACE_EVENT(avs_d0ix,
+
+       TP_PROTO(const char *op, bool proceed, u64 header),
+
+       TP_ARGS(op, proceed, header),
+
+       TP_STRUCT__entry(
+               __string(op,    op      )
+               __field(bool,   proceed )
+               __field(u64,    header  )
+       ),
+
+       TP_fast_assign(
+               __assign_str(op, op);
+               __entry->proceed = proceed;
+               __entry->header = header;
+       ),
+
+       TP_printk("%s%s for request: 0x%08X 0x%08X",
+                 __entry->proceed ? "" : "ignore ", __get_str(op),
+                 lower_32_bits(__entry->header), upper_32_bits(__entry->header))
+);
+
+#endif /* _TRACE_INTEL_AVS_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
index 6473e3a..13611de 100644 (file)
@@ -7,6 +7,7 @@
 //
 
 #include <linux/firmware.h>
+#include <linux/kfifo.h>
 #include <linux/slab.h>
 #include "avs.h"
 #include "messages.h"
@@ -299,3 +300,25 @@ void avs_release_firmwares(struct avs_dev *adev)
                kfree(entry);
        }
 }
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+                                  spinlock_t *lock)
+{
+       struct __kfifo *__fifo = &fifo->kfifo;
+       unsigned long flags;
+       unsigned int l, off;
+
+       spin_lock_irqsave(lock, flags);
+       len = min(len, kfifo_avail(fifo));
+       off = __fifo->in & __fifo->mask;
+       l = min(len, kfifo_size(fifo) - off);
+
+       memcpy_fromio(__fifo->data + off, src, l);
+       memcpy_fromio(__fifo->data, src + l, len - l);
+       /* Make sure data copied from SRAM is visible to all CPUs. */
+       smp_mb();
+       __fifo->in += len;
+       spin_unlock_irqrestore(lock, flags);
+
+       return len;
+}
index bc0eab1..aae857f 100644 (file)
@@ -192,15 +192,15 @@ static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Create and initialize headphone jack */
-       if (snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+       if (snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
                        SND_JACK_HEADPHONE, &headphone_jack,
                        &headphone_jack_pin, 1)) {
                dev_err(component->dev, "Can't create headphone jack\n");
        }
 
        /* Create and initialize mic jack */
-       if (snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
-                       &mic_jack, &mic_jack_pin, 1)) {
+       if (snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+                       SND_JACK_MICROPHONE, &mic_jack, &mic_jack_pin, 1)) {
                dev_err(component->dev, "Can't create mic jack\n");
        }
 
index 071557f..d0ecbba 100644 (file)
@@ -256,7 +256,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Create and initialize headphone jack */
-       if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+       if (!snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
                        SND_JACK_HEADPHONE, &headphone_jack,
                        &headphone_jack_pin, 1)) {
                headphone_jack_gpio.gpiod_dev = component->dev;
@@ -268,7 +268,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Create and initialize mic jack */
-       if (!snd_soc_card_jack_new(rtd->card, "Mic Jack",
+       if (!snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
                        SND_JACK_MICROPHONE, &mic_jack,
                        &mic_jack_pin, 1)) {
                mic_jack_gpio.gpiod_dev = component->dev;
index d37c74f..c30a9dc 100644 (file)
@@ -69,7 +69,7 @@ static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
        int ret = 0;
-       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
                SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
                broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
        if (ret)
index 9bc7b88..d98376d 100644 (file)
@@ -234,7 +234,7 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
        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 | SND_JACK_LINEOUT,
-                       &broxton_headset, NULL, 0);
+                       &broxton_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -720,8 +720,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &broxton_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &broxton_hdmi[i]);
 
                if (err)
                        return err;
index 05e8330..75995d1 100644 (file)
@@ -168,7 +168,7 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
        int ret = 0;
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
                SND_JACK_HEADSET | SND_JACK_BTN_0,
                &broxton_headset,
                broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins));
@@ -544,8 +544,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &broxton_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &broxton_hdmi[i]);
 
                if (err)
                        return err;
index 96d3201..0eed68a 100644 (file)
@@ -87,11 +87,11 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(card, "Headset",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &byt_cht_cx2072x_headset,
-                                   byt_cht_cx2072x_headset_pins,
-                                   ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                        &byt_cht_cx2072x_headset,
+                                        byt_cht_cx2072x_headset_pins,
+                                        ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
        if (ret)
                return ret;
 
index e18371b..a085077 100644 (file)
@@ -219,10 +219,10 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(card, "Headset",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &priv->jack, byt_cht_es8316_jack_pins,
-                                   ARRAY_SIZE(byt_cht_es8316_jack_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                        &priv->jack, byt_cht_es8316_jack_pins,
+                                        ARRAY_SIZE(byt_cht_es8316_jack_pins));
        if (ret) {
                dev_err(card->dev, "jack creation failed %d\n", ret);
                return ret;
index d76a505..7b948a2 100644 (file)
@@ -773,6 +773,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
                                        BYT_RT5640_OVCD_SF_0P75 |
                                        BYT_RT5640_MCLK_EN),
        },
+       {       /* HP Pro Tablet 408 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Pro Tablet 408"),
+               },
+               .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+                                       BYT_RT5640_JD_SRC_JD2_IN4N |
+                                       BYT_RT5640_OVCD_TH_1500UA |
+                                       BYT_RT5640_OVCD_SF_0P75 |
+                                       BYT_RT5640_SSP0_AIF1 |
+                                       BYT_RT5640_MCLK_EN),
+       },
        {       /* HP Stream 7 */
                .matches = {
                        DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
@@ -1300,10 +1312,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        }
 
        if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
-               ret = snd_soc_card_jack_new(card, "Headset",
-                                           SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                           &priv->jack, rt5640_pins,
-                                           ARRAY_SIZE(rt5640_pins));
+               ret = snd_soc_card_jack_new_pins(card, "Headset",
+                                                SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                                &priv->jack, rt5640_pins,
+                                                ARRAY_SIZE(rt5640_pins));
                if (ret) {
                        dev_err(card->dev, "Jack creation failed %d\n", ret);
                        return ret;
@@ -1321,17 +1333,17 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        }
 
        if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
-               ret = snd_soc_card_jack_new(card, "Headset",
-                                           SND_JACK_HEADSET,
-                                           &priv->jack, rt5640_pins,
-                                           ARRAY_SIZE(rt5640_pins));
+               ret = snd_soc_card_jack_new_pins(card, "Headset",
+                                                SND_JACK_HEADSET,
+                                                &priv->jack, rt5640_pins,
+                                                ARRAY_SIZE(rt5640_pins));
                if (ret)
                        return ret;
 
-               ret = snd_soc_card_jack_new(card, "Headset 2",
-                                           SND_JACK_HEADSET,
-                                           &priv->jack2, rt5640_pins2,
-                                           ARRAY_SIZE(rt5640_pins2));
+               ret = snd_soc_card_jack_new_pins(card, "Headset 2",
+                                                SND_JACK_HEADSET,
+                                                &priv->jack2, rt5640_pins2,
+                                                ARRAY_SIZE(rt5640_pins2));
                if (ret)
                        return ret;
 
index 39348d2..d467fca 100644 (file)
@@ -652,9 +652,10 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
                report = SND_JACK_HEADSET;
 
        if (report) {
-               ret = snd_soc_card_jack_new(runtime->card, "Headset",
-                                   report, &priv->jack, bytcr_jack_pins,
-                                   ARRAY_SIZE(bytcr_jack_pins));
+               ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+                                                report, &priv->jack,
+                                                bytcr_jack_pins,
+                                                ARRAY_SIZE(bytcr_jack_pins));
                if (ret) {
                        dev_err(runtime->dev, "jack creation failed %d\n", ret);
                        return ret;
index 8d8e96e..00384c6 100644 (file)
@@ -226,9 +226,9 @@ static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
 
        jack_type = ARIZONA_JACK_MASK | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                    SND_JACK_BTN_2 | SND_JACK_BTN_3;
-       ret = snd_soc_card_jack_new(card, "Headset", jack_type,
-                                   &priv->jack, byt_wm5102_pins,
-                                   ARRAY_SIZE(byt_wm5102_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset", jack_type,
+                                        &priv->jack, byt_wm5102_pins,
+                                        ARRAY_SIZE(byt_wm5102_pins));
        if (ret) {
                dev_err(card->dev, "Error creating jack: %d\n", ret);
                return ret;
index b3d7a07..a5160f2 100644 (file)
@@ -201,9 +201,10 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 
        jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 
-       ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
-                                   jack_type, jack,
-                                   hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+       ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack",
+                                        jack_type, jack,
+                                        hs_jack_pins,
+                                        ARRAY_SIZE(hs_jack_pins));
        if (ret) {
                dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -306,8 +307,7 @@ static int cht_max98090_headset_init(struct snd_soc_component *component)
                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                    SND_JACK_BTN_2 | SND_JACK_BTN_3;
 
-       ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type,
-                                   jack, NULL, 0);
+       ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack);
        if (ret) {
                dev_err(card->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -538,7 +538,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        const char *platform_name;
        bool sof_parent;
 
-       drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+       drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
        if (!drv)
                return -ENOMEM;
 
@@ -559,8 +559,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        }
 
        /* override platform name, if required */
-       snd_soc_card_cht.dev = &pdev->dev;
-       mach = pdev->dev.platform_data;
+       snd_soc_card_cht.dev = dev;
+       mach = dev->platform_data;
        platform_name = mach->mach_params.platform;
 
        ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
@@ -576,9 +576,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        else
                mclk_name = "pmc_plt_clk_3";
 
-       drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
+       drv->mclk = devm_clk_get(dev, mclk_name);
        if (IS_ERR(drv->mclk)) {
-               dev_err(&pdev->dev,
+               dev_err(dev,
                        "Failed to get MCLK from %s: %ld\n",
                        mclk_name, PTR_ERR(drv->mclk));
                return PTR_ERR(drv->mclk);
@@ -594,12 +594,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        if (drv->quirks & QUIRK_PMC_PLT_CLK_0) {
                ret_val = clk_prepare_enable(drv->mclk);
                if (ret_val < 0) {
-                       dev_err(&pdev->dev, "MCLK enable error: %d\n", ret_val);
+                       dev_err(dev, "MCLK enable error: %d\n", ret_val);
                        return ret_val;
                }
        }
 
-       sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+       sof_parent = snd_soc_acpi_sof_parent(dev);
 
        /* set card and driver name */
        if (sof_parent) {
@@ -614,9 +614,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        if (sof_parent)
                dev->driver->pm = &snd_soc_pm_ops;
 
-       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+       ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht);
        if (ret_val) {
-               dev_err(&pdev->dev,
+               dev_err(dev,
                        "snd_soc_register_card failed %d\n", ret_val);
                return ret_val;
        }
index da6c659..4c1d83b 100644 (file)
@@ -108,8 +108,8 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
         */
        jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                SND_JACK_BTN_2 | SND_JACK_BTN_3;
-       ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack,
-               cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
+       ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+               jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
        if (ret) {
                dev_err(runtime->dev,
                        "Headset Jack creation failed %d\n", ret);
index c21561c..45c301e 100644 (file)
@@ -302,9 +302,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
        else
                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 
-       ret = snd_soc_card_jack_new(runtime->card, "Headset",
-                                   jack_type, &ctx->jack,
-                                   cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
+       ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+                                        &ctx->jack, cht_bsw_jack_pins,
+                                        ARRAY_SIZE(cht_bsw_jack_pins));
        if (ret) {
                dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
                return ret;
index 9882aeb..c80324f 100644 (file)
@@ -221,12 +221,12 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
        if (ret)
                return ret;
 
-        ret = snd_soc_card_jack_new(runtime->card, "Headset",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                                   SND_JACK_BTN_1 | SND_JACK_BTN_2,
-                                   &ctx->headset,
-                                   cht_bsw_headset_pins,
-                                   ARRAY_SIZE(cht_bsw_headset_pins));
+       ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                        SND_JACK_BTN_1 | SND_JACK_BTN_2,
+                                        &ctx->headset,
+                                        cht_bsw_headset_pins,
+                                        ARRAY_SIZE(cht_bsw_headset_pins));
         if (ret)
                 return ret;
 
index 27615ac..a99f74a 100644 (file)
@@ -141,7 +141,7 @@ static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3,
-                                   &ctx->headset, NULL, 0);
+                                   &ctx->headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -338,8 +338,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                         "HDMI/DP, pcm=%d Jack", pcm->device);
                ret = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_AVOUT, &hdmi_jack[i],
-                                           NULL, 0);
+                                           SND_JACK_AVOUT, &hdmi_jack[i]);
                if (ret)
                        return ret;
 
index e4bfb0f..170164b 100644 (file)
@@ -176,7 +176,7 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
        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 | SND_JACK_LINEOUT,
-                       &ctx->geminilake_headset, NULL, 0);
+                       &ctx->geminilake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -571,8 +571,7 @@ static int glk_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &geminilake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &geminilake_hdmi[i]);
 
                if (err)
                        return err;
index a4bdf63..ceabed8 100644 (file)
@@ -182,7 +182,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
        ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
                        SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                        SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
-                       &ctx->kabylake_headset, NULL, 0);
+                       &ctx->kabylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -587,8 +587,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &skylake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &skylake_hdmi[i]);
 
                if (err)
                        return err;
index 620a9fb..703ccff 100644 (file)
@@ -357,7 +357,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
        ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
                        SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                        SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
-                       &ctx->kabylake_headset, NULL, 0);
+                       &ctx->kabylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -965,8 +965,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &kabylake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &kabylake_hdmi[i]);
 
                if (err)
                        return err;
index 1cb56ec..2c7a547 100644 (file)
@@ -173,9 +173,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Create and initialize headphone jack, this jack is not mandatory, don't return if fails */
-       ret = snd_soc_card_jack_new(rtd->card, "Lineout Jack",
-                                   SND_JACK_LINEOUT, &lineout_jack,
-                                   &lineout_jack_pin, 1);
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Lineout Jack",
+                                        SND_JACK_LINEOUT, &lineout_jack,
+                                        &lineout_jack_pin, 1);
        if (ret)
                dev_warn(component->dev, "Can't create Lineout jack\n");
        else {
@@ -187,9 +187,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Create and initialize mic jack, this jack is not mandatory, don't return if fails */
-       ret = snd_soc_card_jack_new(rtd->card, "Mic Jack",
-                                   SND_JACK_MICROPHONE, &mic_jack,
-                                   &mic_jack_pin, 1);
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+                                        SND_JACK_MICROPHONE, &mic_jack,
+                                        &mic_jack_pin, 1);
        if (ret)
                dev_warn(component->dev, "Can't create mic jack\n");
        else {
@@ -485,8 +485,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &skylake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &skylake_hdmi[i]);
 
                if (err)
                        return err;
index f24e0ce..8d37b26 100644 (file)
@@ -273,8 +273,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd)
         */
        ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
                        SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset,
-                       NULL, 0);
+                       SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                       &ctx->kabylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -919,8 +919,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &skylake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &skylake_hdmi[i]);
 
                if (err)
                        return err;
index 6874e98..564c70a 100644 (file)
@@ -230,8 +230,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd)
         */
        ret = snd_soc_card_jack_new(&kabylake_audio_card, "Headset Jack",
                        SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset,
-                       NULL, 0);
+                       SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                       &ctx->kabylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -743,8 +743,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP,pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                               SND_JACK_AVOUT, &ctx->kabylake_hdmi[i],
-                               NULL, 0);
+                               SND_JACK_AVOUT, &ctx->kabylake_hdmi[i]);
 
                if (err)
                        return err;
index 07bfb2e..e9cefa4 100644 (file)
@@ -150,17 +150,11 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                         "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_AVOUT, &pcm->hdmi_jack,
-                                           NULL, 0);
+                                           SND_JACK_AVOUT, &pcm->hdmi_jack);
 
                if (err)
                        return err;
 
-               err = snd_jack_add_new_kctl(pcm->hdmi_jack.jack,
-                                           jack_name, SND_JACK_AVOUT);
-               if (err)
-                       dev_warn(component->dev, "failed creating Jack kctl\n");
-
                err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
                                          &pcm->hdmi_jack);
                if (err < 0)
index 7297eb0..8e2d03e 100644 (file)
@@ -165,8 +165,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
         */
        ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
                        SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
-                       NULL, 0);
+                       SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -610,8 +609,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
                                        SND_JACK_AVOUT,
-                                       &skylake_hdmi[i],
-                                       NULL, 0);
+                                       &skylake_hdmi[i]);
 
                if (err)
                        return err;
index 68efde1..501f0bb 100644 (file)
@@ -184,8 +184,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
         */
        ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
                SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-               SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
-               NULL, 0);
+               SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
                return ret;
@@ -651,8 +650,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
                                        SND_JACK_AVOUT,
-                                       &skylake_hdmi[i],
-                                       NULL, 0);
+                                       &skylake_hdmi[i]);
 
                if (err)
                        return err;
index eca4a78..e9f9520 100644 (file)
@@ -125,7 +125,7 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
        int ret;
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
                SND_JACK_HEADSET | SND_JACK_BTN_0,
                &skylake_headset,
                skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
@@ -491,8 +491,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                        "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                       SND_JACK_AVOUT, &skylake_hdmi[i],
-                                       NULL, 0);
+                                       SND_JACK_AVOUT, &skylake_hdmi[i]);
 
                if (err)
                        return err;
index ce78c18..6a979c3 100644 (file)
@@ -102,7 +102,7 @@ static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3,
-                                   jack, NULL, 0);
+                                   jack);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -186,8 +186,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                         "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_AVOUT, &pcm->hdmi_jack,
-                                           NULL, 0);
+                                           SND_JACK_AVOUT, &pcm->hdmi_jack);
 
                if (err)
                        return err;
index b7b3b0b..a83f30b 100644 (file)
@@ -160,7 +160,7 @@ static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3 | SND_JACK_LINEOUT,
-                                   &headset, NULL, 0);
+                                   &headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
index 9d61783..23d03e0 100644 (file)
@@ -245,10 +245,10 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
        if (ret)
                return ret;
 
-       ret = snd_soc_card_jack_new(card, "Headset",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &priv->jack, sof_es8316_jack_pins,
-                                   ARRAY_SIZE(sof_es8316_jack_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                        &priv->jack, sof_es8316_jack_pins,
+                                        ARRAY_SIZE(sof_es8316_jack_pins));
        if (ret) {
                dev_err(card->dev, "jack creation failed %d\n", ret);
                return ret;
index 33de043..97dcd20 100644 (file)
@@ -97,7 +97,7 @@ static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3,
-                                   &ctx->sof_headset, NULL, 0);
+                                   &ctx->sof_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
index a2bcbee..2ab568c 100644 (file)
@@ -459,5 +459,44 @@ void sof_rt1308_dai_link(struct snd_soc_dai_link *link)
 }
 EXPORT_SYMBOL_NS(sof_rt1308_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON);
 
+/*
+ * 2-amp Configuration for RT1019
+ */
+
+static const struct snd_soc_dapm_route rt1019_dapm_routes[] = {
+       /* speaker */
+       { "Left Spk", NULL, "Speaker" },
+       { "Right Spk", NULL, "Speaker" },
+};
+
+static struct snd_soc_dai_link_component rt1019_components[] = {
+       {
+               .name = RT1019_DEV0_NAME,
+               .dai_name = RT1019_CODEC_DAI,
+       },
+};
+
+static int rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       int ret;
+
+       ret = snd_soc_dapm_add_routes(&card->dapm, rt1019_dapm_routes,
+                                     ARRAY_SIZE(rt1019_dapm_routes));
+       if (ret) {
+               dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
+               return ret;
+       }
+       return ret;
+}
+
+void sof_rt1019_dai_link(struct snd_soc_dai_link *link)
+{
+       link->codecs = rt1019_components;
+       link->num_codecs = ARRAY_SIZE(rt1019_components);
+       link->init = rt1019_init;
+}
+EXPORT_SYMBOL_NS(sof_rt1019_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON);
+
 MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers");
 MODULE_LICENSE("GPL");
index e0a5518..ec3eea6 100644 (file)
@@ -39,4 +39,9 @@ void sof_rt1015_codec_conf(struct snd_soc_card *card);
 #define RT1308_DEV0_NAME       "i2c-10EC1308:00"
 void sof_rt1308_dai_link(struct snd_soc_dai_link *link);
 
+#define RT1019_CODEC_DAI       "HiFi"
+#define RT1019_DEV0_NAME       "RTL1019:00"
+
+void sof_rt1019_dai_link(struct snd_soc_dai_link *link);
+
 #endif /* __SOF_REALTEK_COMMON_H */
index 7126fcb..5d67a2c 100644 (file)
@@ -60,6 +60,7 @@
 #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT    BIT(23)
 #define SOF_MAX98390_SPEAKER_AMP_PRESENT       BIT(24)
 #define SOF_MAX98390_TWEETER_SPEAKER_PRESENT   BIT(25)
+#define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26)
 
 
 /* Default: MCLK on, MCLK 19.2M, SSP0  */
@@ -298,7 +299,7 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3,
-                                   &ctx->sof_headset, NULL, 0);
+                                   &ctx->sof_headset);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -454,8 +455,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
                snprintf(jack_name, sizeof(jack_name),
                         "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_AVOUT, &sof_hdmi[i],
-                                           NULL, 0);
+                                           SND_JACK_AVOUT, &sof_hdmi[i]);
 
                if (err)
                        return err;
@@ -734,6 +734,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
                                        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 & SOF_RT1019_SPEAKER_AMP_PRESENT) {
+                       sof_rt1019_dai_link(&links[id]);
                } else if (sof_rt5682_quirk &
                                SOF_MAX98373_SPEAKER_AMP_PRESENT) {
                        links[id].codecs = max_98373_components;
@@ -1071,6 +1073,16 @@ static const struct platform_device_id board_ids[] = {
                                        SOF_BT_OFFLOAD_SSP(2) |
                                        SOF_SSP_BT_OFFLOAD_PRESENT),
        },
+       {
+               .name = "adl_rt1019_rt5682s",
+               .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+                                       SOF_RT5682_SSP_CODEC(0) |
+                                       SOF_RT5682S_HEADPHONE_CODEC_PRESENT |
+                                       SOF_SPEAKER_AMP_PRESENT |
+                                       SOF_RT1019_SPEAKER_AMP_PRESENT |
+                                       SOF_RT5682_SSP_AMP(1) |
+                                       SOF_RT5682_NUM_HDMIDEV(4)),
+       },
        { }
 };
 MODULE_DEVICE_TABLE(platform, board_ids);
index ea55479..3a9be82 100644 (file)
@@ -82,13 +82,13 @@ static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                                   SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-                                   SND_JACK_BTN_3,
-                                   &ctx->sdw_headset,
-                                   rt5682_jack_pins,
-                                   ARRAY_SIZE(rt5682_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                        SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+                                        SND_JACK_BTN_3,
+                                        &ctx->sdw_headset,
+                                        rt5682_jack_pins,
+                                        ARRAY_SIZE(rt5682_jack_pins));
        if (ret) {
                dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
                        ret);
index bb9584c..c93b1f5 100644 (file)
@@ -82,13 +82,13 @@ static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                                   SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-                                   SND_JACK_BTN_3,
-                                   &ctx->sdw_headset,
-                                   rt700_jack_pins,
-                                   ARRAY_SIZE(rt700_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                        SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+                                        SND_JACK_BTN_3,
+                                        &ctx->sdw_headset,
+                                        rt700_jack_pins,
+                                        ARRAY_SIZE(rt700_jack_pins));
        if (ret) {
                dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
                        ret);
index c38b70c..49ff087 100644 (file)
@@ -106,13 +106,13 @@ static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                                   SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-                                   SND_JACK_BTN_3,
-                                   &ctx->sdw_headset,
-                                   rt711_jack_pins,
-                                   ARRAY_SIZE(rt711_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                        SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+                                        SND_JACK_BTN_3,
+                                        &ctx->sdw_headset,
+                                        rt711_jack_pins,
+                                        ARRAY_SIZE(rt711_jack_pins));
        if (ret) {
                dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
                        ret);
index 4215ddc..b3fc32b 100644 (file)
@@ -107,13 +107,13 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                                   SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-                                   SND_JACK_BTN_3,
-                                   &ctx->sdw_headset,
-                                   rt711_sdca_jack_pins,
-                                   ARRAY_SIZE(rt711_sdca_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                        SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+                                        SND_JACK_BTN_3,
+                                        &ctx->sdw_headset,
+                                        rt711_sdca_jack_pins,
+                                        ARRAY_SIZE(rt711_sdca_jack_pins));
        if (ret) {
                dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
                        ret);
index 88530e9..4a762e0 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/acpi.h>
 #include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <sound/core.h>
@@ -78,6 +79,16 @@ struct sof_card_private {
        bool idisp_codec;
 };
 
+static const struct dmi_system_id chromebook_platforms[] = {
+       {
+               .ident = "Google Chromebooks",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+               }
+       },
+       {},
+};
+
 static const struct snd_soc_dapm_widget sof_ssp_amp_dapm_widgets[] = {
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
 };
@@ -94,7 +105,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
        char jack_name[NAME_SIZE];
        struct sof_hdmi_pcm *pcm;
        int err;
-       int i = 0;
+       int i;
 
        if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT))
                return 0;
@@ -113,13 +124,13 @@ static int sof_card_late_probe(struct snd_soc_card *card)
                return hda_dsp_hdmi_build_controls(card, component);
        }
 
+       i = 0;
        list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
                component = pcm->codec_dai->component;
                snprintf(jack_name, sizeof(jack_name),
                         "HDMI/DP, pcm=%d Jack", pcm->device);
                err = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_AVOUT, &pcm->sof_hdmi,
-                                           NULL, 0);
+                                           SND_JACK_AVOUT, &pcm->sof_hdmi);
 
                if (err)
                        return err;
@@ -247,6 +258,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
                sof_rt1308_dai_link(&links[id]);
        } else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) {
                cs35l41_set_dai_link(&links[id]);
+
+               /* feedback from amplifier */
+               links[id].dpcm_capture = 1;
        }
        links[id].platforms = platform_component;
        links[id].num_platforms = ARRAY_SIZE(platform_component);
@@ -371,7 +385,7 @@ static int sof_ssp_amp_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 = 0;
+       int dmic_be_num = 0, hdmi_num = 0;
        int ret, ssp_codec;
 
        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@@ -383,7 +397,8 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
 
        mach = pdev->dev.platform_data;
 
-       dmic_be_num = mach->mach_params.dmic_num;
+       if (dmi_check_system(chromebook_platforms) || mach->mach_params.dmic_num > 0)
+               dmic_be_num = 2;
 
        ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
 
index 978a20b..c17e948 100644 (file)
@@ -219,11 +219,9 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id);
 enum catpt_ssp_iface {
        CATPT_SSP_IFACE_0 = 0,
        CATPT_SSP_IFACE_1 = 1,
-       CATPT_SSP_IFACE_LAST = CATPT_SSP_IFACE_1,
+       CATPT_SSP_COUNT,
 };
 
-#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1)
-
 enum catpt_mclk_frequency {
        CATPT_MCLK_OFF = 0,
        CATPT_MCLK_FREQ_6_MHZ = 1,
index 8bfe707..e6e52c7 100644 (file)
@@ -456,6 +456,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
                .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg",
        },
        {
+               .id = "RTL5682",
+               .drv_name = "adl_rt1019_rt5682s",
+               .machine_quirk = snd_soc_acpi_codec_list,
+               .quirk_data = &adl_rt1019p_amp,
+               .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg",
+       },
+       {
                .id = "10508825",
                .drv_name = "sof_nau8825",
                .sof_tplg_filename = "sof-adl-nau8825.tplg",
index 0d15435..9e5ce1a 100644 (file)
@@ -179,6 +179,7 @@ config SND_SOC_MT8192_MT6359_RT1015_RT5682
        select SND_SOC_RT1015
        select SND_SOC_RT1015P
        select SND_SOC_RT5682_I2C
+       select SND_SOC_RT5682S
        select SND_SOC_DMIC
        help
          This adds ASoC driver for Mediatek MT8192 boards
@@ -198,34 +199,20 @@ config SND_SOC_MT8195
          Select Y if you have such device.
          If unsure select "N".
 
-config SND_SOC_MT8195_MT6359_RT1019_RT5682
-       tristate "ASoC Audio driver for MT8195 with MT6359 RT1019 RT5682 codec"
-       depends on I2C && GPIOLIB
-       depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
-       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
-         This adds ASoC driver for Mediatek MT8195 boards
-         with the MT6359 RT1019 RT5682 audio codec.
-         Select Y if you have such device.
-         If unsure select "N".
-
-config SND_SOC_MT8195_MT6359_RT1011_RT5682
-       tristate "ASoC Audio driver for MT8195 with MT6359 RT1011 RT5682 codec"
+config SND_SOC_MT8195_MT6359
+       tristate "ASoC Audio driver for MT8195 with MT6359 and I2S codecs"
        depends on I2C && GPIOLIB
        depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
        select SND_SOC_MT6359
        select SND_SOC_RT1011
+       select SND_SOC_RT1015P
        select SND_SOC_RT5682_I2C
        select SND_SOC_RT5682S
+       select SND_SOC_MAX98390
        select SND_SOC_DMIC
        select SND_SOC_HDMI_CODEC
        help
-         This adds ASoC driver for Mediatek MT8195 boards
-         with the MT6359 RT1011 RT5682 audio codec.
+         This adds support for ASoC machine driver for Mediatek MT8195
+         boards with the MT6359 and other I2S audio codecs.
          Select Y if you have such device.
          If unsure select "N".
index f56de1b..0cdf2ae 100644 (file)
@@ -129,7 +129,8 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
        if (!codec_node) {
                dev_err(&pdev->dev,
                        "Property 'audio-codec' missing or invalid\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto put_platform_node;
        }
        for_each_card_prelinks(card, i, dai_link) {
                if (dai_link->codecs->name)
@@ -140,7 +141,7 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
        ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
        if (ret) {
                dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
-               return ret;
+               goto put_codec_node;
        }
 
        ret = devm_snd_soc_register_card(&pdev->dev, card);
@@ -148,6 +149,10 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
                        __func__, ret);
 
+put_codec_node:
+       of_node_put(codec_node);
+put_platform_node:
+       of_node_put(platform_node);
        return ret;
 }
 
index 4cb90da..c2b0619 100644 (file)
@@ -70,10 +70,10 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
        struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
 
        /* enable jack detection */
-       ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
-                                   &mt8173_max98090_jack,
-                                   mt8173_max98090_jack_pins,
-                                   ARRAY_SIZE(mt8173_max98090_jack_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADPHONE,
+                                        &mt8173_max98090_jack,
+                                        mt8173_max98090_jack_pins,
+                                        ARRAY_SIZE(mt8173_max98090_jack_pins));
        if (ret) {
                dev_err(card->dev, "Can't create a new Jack %d\n", ret);
                return ret;
@@ -167,7 +167,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
        if (!codec_node) {
                dev_err(&pdev->dev,
                        "Property 'audio-codec' missing or invalid\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto put_platform_node;
        }
        for_each_card_prelinks(card, i, dai_link) {
                if (dai_link->codecs->name)
@@ -179,6 +180,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
        ret = devm_snd_soc_register_card(&pdev->dev, card);
 
        of_node_put(codec_node);
+
+put_platform_node:
        of_node_put(platform_node);
        return ret;
 }
index b55122b..12f40c8 100644 (file)
@@ -86,7 +86,7 @@ static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime)
                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &mt8173_rt5650_rt5514_jack, NULL, 0);
+                                   &mt8173_rt5650_rt5514_jack);
        if (ret) {
                dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
                return ret;
index 5716d92..70bf312 100644 (file)
@@ -99,7 +99,7 @@ static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &mt8173_rt5650_rt5676_jack, NULL, 0);
+                                   &mt8173_rt5650_rt5676_jack);
        if (ret) {
                dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
                return ret;
index fc164f4..d1c94ac 100644 (file)
@@ -132,7 +132,7 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &mt8173_rt5650_jack, NULL, 0);
+                                   &mt8173_rt5650_jack);
        if (ret) {
                dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
                return ret;
@@ -149,7 +149,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &mt8173_rt5650_hdmi_jack, NULL, 0);
+                                   &mt8173_rt5650_hdmi_jack);
        if (ret)
                return ret;
 
index f090dee..b33cc9a 100644 (file)
@@ -364,7 +364,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &priv->hdmi_jack, NULL, 0);
+                                   &priv->hdmi_jack);
        if (ret)
                return ret;
 
@@ -546,8 +546,7 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component)
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                    SND_JACK_LINEOUT,
-                                   &priv->headset_jack,
-                                   NULL, 0);
+                                   &priv->headset_jack);
        if (ret)
                return ret;
 
index 889f9e4..ab157db 100644 (file)
@@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &priv->hdmi_jack, NULL, 0);
+                                   &priv->hdmi_jack);
        if (ret)
                return ret;
 
@@ -613,8 +613,7 @@ mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component)
                                    SND_JACK_HEADSET |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &priv->headset_jack,
-                                   NULL, 0);
+                                   &priv->headset_jack);
        if (ret)
                return ret;
 
index ee91569..d0f9d66 100644 (file)
 #define RT1015_DEV0_NAME       "rt1015.1-0028"
 #define RT1015_DEV1_NAME       "rt1015.1-0029"
 
-#define RT5682_CODEC_DAI       "rt5682-aif1"
-#define RT5682_DEV0_NAME       "rt5682.1-001a"
+#define RT1015_RT5682_CARD_NAME "mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_CARD_NAME "mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_CARD_NAME "mt8192_mt6359_rt1015p_rt5682s"
+
+#define RT1015_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682s"
 
 struct mt8192_mt6359_priv {
        struct snd_soc_jack headset_jack;
@@ -71,8 +76,8 @@ static int mt8192_rt1015_i2s_hw_params(struct snd_pcm_substream *substream,
        return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
 }
 
-static int mt8192_rt5682_i2s_hw_params(struct snd_pcm_substream *substream,
-                                      struct snd_pcm_hw_params *params)
+static int mt8192_rt5682x_i2s_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;
@@ -121,8 +126,8 @@ static const struct snd_soc_ops mt8192_rt1015_i2s_ops = {
        .hw_params = mt8192_rt1015_i2s_hw_params,
 };
 
-static const struct snd_soc_ops mt8192_rt5682_i2s_ops = {
-       .hw_params = mt8192_rt5682_i2s_hw_params,
+static const struct snd_soc_ops mt8192_rt5682x_i2s_ops = {
+       .hw_params = mt8192_rt5682x_i2s_hw_params,
 };
 
 static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
@@ -316,7 +321,7 @@ static int mt8192_rt5682_init(struct snd_soc_pcm_runtime *rtd)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0 |
                                    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                                    SND_JACK_BTN_3,
-                                   jack, NULL, 0);
+                                   jack);
        if (ret) {
                dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
                return ret;
@@ -338,7 +343,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &priv->hdmi_jack, NULL, 0);
+                                   &priv->hdmi_jack);
        if (ret) {
                dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
                return ret;
@@ -604,17 +609,9 @@ SND_SOC_DAILINK_DEFS(i2s2,
                     DAILINK_COMP_ARRAY(COMP_DUMMY()),
                     DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
-SND_SOC_DAILINK_DEFS(i2s3_rt1015,
-                    DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC(RT1015_DEV0_NAME,
-                                                  RT1015_CODEC_DAI),
-                                       COMP_CODEC(RT1015_DEV1_NAME,
-                                                  RT1015_CODEC_DAI)),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(i2s3_rt1015p,
+SND_SOC_DAILINK_DEFS(i2s3,
                     DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi")),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()),
                     DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
 SND_SOC_DAILINK_DEFS(i2s5,
@@ -634,14 +631,12 @@ SND_SOC_DAILINK_DEFS(i2s7,
 
 SND_SOC_DAILINK_DEFS(i2s8,
                     DAILINK_COMP_ARRAY(COMP_CPU("I2S8")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
-                                                  RT5682_CODEC_DAI)),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()),
                     DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
 SND_SOC_DAILINK_DEFS(i2s9,
                     DAILINK_COMP_ARRAY(COMP_CPU("I2S9")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
-                                                  RT5682_CODEC_DAI)),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()),
                     DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
 SND_SOC_DAILINK_DEFS(connsys_i2s,
@@ -929,6 +924,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
                .dpcm_playback = 1,
                .ignore_suspend = 1,
                .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+               SND_SOC_DAILINK_REG(i2s3),
        },
        {
                .name = "I2S5",
@@ -962,7 +958,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
                .init = mt8192_rt5682_init,
                .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
                SND_SOC_DAILINK_REG(i2s8),
-               .ops = &mt8192_rt5682_i2s_ops,
+               .ops = &mt8192_rt5682x_i2s_ops,
        },
        {
                .name = "I2S9",
@@ -971,7 +967,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
                .ignore_suspend = 1,
                .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
                SND_SOC_DAILINK_REG(i2s9),
-               .ops = &mt8192_rt5682_i2s_ops,
+               .ops = &mt8192_rt5682x_i2s_ops,
        },
        {
                .name = "CONNSYS_I2S",
@@ -1051,7 +1047,7 @@ static struct snd_soc_codec_conf rt1015_amp_conf[] = {
 };
 
 static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = {
-       .name = "mt8192_mt6359_rt1015_rt5682",
+       .name = RT1015_RT5682_CARD_NAME,
        .owner = THIS_MODULE,
        .dai_link = mt8192_mt6359_dai_links,
        .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
@@ -1065,14 +1061,13 @@ static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = {
        .num_configs = ARRAY_SIZE(rt1015_amp_conf),
 };
 
-static const struct snd_soc_dapm_widget
-mt8192_mt6359_rt1015p_rt5682_widgets[] = {
+static const struct snd_soc_dapm_widget mt8192_mt6359_rt1015p_rt5682x_widgets[] = {
        SND_SOC_DAPM_SPK("Speakers", NULL),
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 };
 
-static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = {
+static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682x_routes[] = {
        /* speaker */
        { "Speakers", NULL, "Speaker" },
        /* headset */
@@ -1081,74 +1076,107 @@ static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = {
        { "IN1P", NULL, "Headset Mic" },
 };
 
-static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682_controls[] = {
+static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682x_controls[] = {
        SOC_DAPM_PIN_SWITCH("Speakers"),
        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 };
 
-static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682_card = {
-       .name = "mt8192_mt6359_rt1015p_rt5682",
+static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = {
        .owner = THIS_MODULE,
        .dai_link = mt8192_mt6359_dai_links,
        .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
-       .controls = mt8192_mt6359_rt1015p_rt5682_controls,
-       .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_controls),
-       .dapm_widgets = mt8192_mt6359_rt1015p_rt5682_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_widgets),
-       .dapm_routes = mt8192_mt6359_rt1015p_rt5682_routes,
-       .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_routes),
+       .controls = mt8192_mt6359_rt1015p_rt5682x_controls,
+       .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_controls),
+       .dapm_widgets = mt8192_mt6359_rt1015p_rt5682x_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_widgets),
+       .dapm_routes = mt8192_mt6359_rt1015p_rt5682x_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes),
 };
 
+static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card,
+                                         struct snd_soc_dai_link *link,
+                                         struct device_node *node,
+                                         char *link_name)
+{
+       int ret;
+
+       if (node && strcmp(link->name, link_name) == 0) {
+               ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link);
+               if (ret < 0) {
+                       dev_err_probe(card->dev, ret, "get dai link codecs fail\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card;
-       struct device_node *platform_node, *hdmi_codec;
+       struct device_node *platform_node, *hdmi_codec, *headset_codec, *speaker_codec;
        int ret, i;
        struct snd_soc_dai_link *dai_link;
        struct mt8192_mt6359_priv *priv;
 
-       platform_node = of_parse_phandle(pdev->dev.of_node,
-                                        "mediatek,platform", 0);
-       if (!platform_node) {
-               dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+       card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+       if (!card)
                return -EINVAL;
+       card->dev = &pdev->dev;
+
+       if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682_OF_NAME))
+               card->name = RT1015P_RT5682_CARD_NAME;
+       else if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682S_OF_NAME))
+               card->name = RT1015P_RT5682S_CARD_NAME;
+       else
+               dev_dbg(&pdev->dev, "No need to set card name\n");
+
+       hdmi_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0);
+       if (!hdmi_codec)
+               dev_dbg(&pdev->dev, "The machine has no hdmi-codec\n");
+
+       platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
+       if (!platform_node) {
+               ret = -EINVAL;
+               dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n");
+               goto err_platform_node;
        }
 
-       card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
-       if (!card) {
+       speaker_codec = of_get_child_by_name(pdev->dev.of_node, "speaker-codecs");
+       if (!speaker_codec) {
                ret = -EINVAL;
-               goto put_platform_node;
+               dev_err_probe(&pdev->dev, ret, "Property 'speaker-codecs' missing or invalid\n");
+               goto err_speaker_codec;
        }
-       card->dev = &pdev->dev;
 
-       hdmi_codec = of_parse_phandle(pdev->dev.of_node,
-                                     "mediatek,hdmi-codec", 0);
+       headset_codec = of_get_child_by_name(pdev->dev.of_node, "headset-codec");
+       if (!headset_codec) {
+               ret = -EINVAL;
+               dev_err_probe(&pdev->dev, ret, "Property 'headset-codec' missing or invalid\n");
+               goto err_headset_codec;
+       }
 
        for_each_card_prelinks(card, i, dai_link) {
-               if (strcmp(dai_link->name, "I2S3") == 0) {
-                       if (card == &mt8192_mt6359_rt1015_rt5682_card) {
-                               dai_link->ops = &mt8192_rt1015_i2s_ops;
-                               dai_link->cpus = i2s3_rt1015_cpus;
-                               dai_link->num_cpus =
-                                       ARRAY_SIZE(i2s3_rt1015_cpus);
-                               dai_link->codecs = i2s3_rt1015_codecs;
-                               dai_link->num_codecs =
-                                       ARRAY_SIZE(i2s3_rt1015_codecs);
-                               dai_link->platforms = i2s3_rt1015_platforms;
-                               dai_link->num_platforms =
-                                       ARRAY_SIZE(i2s3_rt1015_platforms);
-                       } else if (card == &mt8192_mt6359_rt1015p_rt5682_card) {
-                               dai_link->cpus = i2s3_rt1015p_cpus;
-                               dai_link->num_cpus =
-                                       ARRAY_SIZE(i2s3_rt1015p_cpus);
-                               dai_link->codecs = i2s3_rt1015p_codecs;
-                               dai_link->num_codecs =
-                                       ARRAY_SIZE(i2s3_rt1015p_codecs);
-                               dai_link->platforms = i2s3_rt1015p_platforms;
-                               dai_link->num_platforms =
-                                       ARRAY_SIZE(i2s3_rt1015p_platforms);
-                       }
+               ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3");
+               if (ret) {
+                       dev_err_probe(&pdev->dev, ret, "%s set speaker_codec fail\n",
+                                     dai_link->name);
+                       goto err_probe;
+               }
+
+               ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8");
+               if (ret) {
+                       dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n",
+                                     dai_link->name);
+                       goto err_probe;
+               }
+
+               ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9");
+               if (ret) {
+                       dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n",
+                                     dai_link->name);
+                       goto err_probe;
                }
 
                if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
@@ -1156,6 +1184,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
                        dai_link->ignore = 0;
                }
 
+               if (strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
+                       dai_link->ops = &mt8192_rt1015_i2s_ops;
+
                if (!dai_link->platforms->name)
                        dai_link->platforms->of_node = platform_node;
        }
@@ -1163,34 +1194,44 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                ret = -ENOMEM;
-               goto put_hdmi_codec;
+               goto err_probe;
        }
        snd_soc_card_set_drvdata(card, priv);
 
        ret = mt8192_afe_gpio_init(&pdev->dev);
        if (ret) {
-               dev_err(&pdev->dev, "init gpio error %d\n", ret);
-               goto put_hdmi_codec;
+               dev_err_probe(&pdev->dev, ret, "%s init gpio error\n", __func__);
+               goto err_probe;
        }
 
        ret = devm_snd_soc_register_card(&pdev->dev, card);
-
-put_hdmi_codec:
-       of_node_put(hdmi_codec);
-put_platform_node:
+       if (ret)
+               dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
+
+err_probe:
+       of_node_put(headset_codec);
+err_headset_codec:
+       of_node_put(speaker_codec);
+err_speaker_codec:
        of_node_put(platform_node);
+err_platform_node:
+       of_node_put(hdmi_codec);
        return ret;
 }
 
 #ifdef CONFIG_OF
 static const struct of_device_id mt8192_mt6359_dt_match[] = {
        {
-               .compatible = "mediatek,mt8192_mt6359_rt1015_rt5682",
+               .compatible = RT1015_RT5682_OF_NAME,
                .data = &mt8192_mt6359_rt1015_rt5682_card,
        },
        {
-               .compatible = "mediatek,mt8192_mt6359_rt1015p_rt5682",
-               .data = &mt8192_mt6359_rt1015p_rt5682_card,
+               .compatible = RT1015P_RT5682_OF_NAME,
+               .data = &mt8192_mt6359_rt1015p_rt5682x_card,
+       },
+       {
+               .compatible = RT1015P_RT5682S_OF_NAME,
+               .data = &mt8192_mt6359_rt1015p_rt5682x_card,
        },
        {}
 };
index e5f0df5..aae673e 100644 (file)
@@ -12,5 +12,4 @@ snd-soc-mt8195-afe-objs := \
 obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o
 
 # machine driver
-obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1019_RT5682) += mt8195-mt6359-rt1019-rt5682.o
-obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1011_RT5682) += mt8195-mt6359-rt1011-rt5682.o
+obj-$(CONFIG_SND_SOC_MT8195_MT6359) += mt8195-mt6359.o
index 151914c..12644de 100644 (file)
@@ -213,8 +213,6 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
 static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
-       int ret;
-
        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);
@@ -222,11 +220,7 @@ static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
        if (dai->playback_widget->active || dai->capture_widget->active)
                return 0;
 
-       ret = mtk_dai_pcm_configure(substream, dai);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mtk_dai_pcm_configure(substream, dai);
 }
 
 static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
deleted file mode 100644 (file)
index 5443a29..0000000
+++ /dev/null
@@ -1,1198 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// mt8195-mt6359-rt1011-rt5682.c  --
-//     MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver
-//
-// Copyright (c) 2021 MediaTek Inc.
-// Author: Trevor Wu <trevor.wu@mediatek.com>
-//
-
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-#include <sound/rt5682.h>
-#include <sound/soc.h>
-#include "../../codecs/mt6359.h"
-#include "../../codecs/rt1011.h"
-#include "../../codecs/rt5682.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "mt8195-afe-clk.h"
-#include "mt8195-afe-common.h"
-
-#define RT1011_CODEC_DAI       "rt1011-aif"
-#define RT1011_DEV0_NAME       "rt1011.2-0038"
-#define RT1011_DEV1_NAME       "rt1011.2-0039"
-
-#define RT5682_CODEC_DAI       "rt5682-aif1"
-#define RT5682_DEV0_NAME       "rt5682.2-001a"
-
-#define RT5682S_CODEC_DAI      "rt5682s-aif1"
-#define RT5682S_DEV0_NAME      "rt5682s.2-001a"
-
-struct mt8195_mt6359_rt1011_rt5682_priv {
-       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
-mt8195_mt6359_rt1011_rt5682_widgets[] = {
-       SND_SOC_DAPM_SPK("Left Speaker", NULL),
-       SND_SOC_DAPM_SPK("Right Speaker", NULL),
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route mt8195_mt6359_rt1011_rt5682_routes[] = {
-       /* speaker */
-       { "Left Speaker", NULL, "Left SPO" },
-       { "Right Speaker", NULL, "Right SPO" },
-       /* headset */
-       { "Headphone Jack", NULL, "HPOL" },
-       { "Headphone Jack", NULL, "HPOR" },
-       { "IN1P", NULL, "Headset Mic" },
-};
-
-static const struct snd_kcontrol_new mt8195_mt6359_rt1011_rt5682_controls[] = {
-       SOC_DAPM_PIN_SWITCH("Left Speaker"),
-       SOC_DAPM_PIN_SWITCH("Right Speaker"),
-       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
-       SOC_DAPM_PIN_SWITCH("Headset Mic"),
-};
-
-static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_card *card = rtd->card;
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       unsigned int rate = params_rate(params);
-       int bitwidth;
-       int ret;
-
-       bitwidth = snd_pcm_format_width(params_format(params));
-       if (bitwidth < 0) {
-               dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
-               return bitwidth;
-       }
-
-       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
-       if (ret) {
-               dev_err(card->dev, "failed to set tdm slot\n");
-               return ret;
-       }
-
-       ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_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,
-                                    rate * 512, SND_SOC_CLOCK_IN);
-       if (ret) {
-               dev_err(card->dev, "failed to set sysclk\n");
-               return ret;
-       }
-
-       return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
-                                     SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
-       .hw_params = mt8195_rt5682_etdm_hw_params,
-};
-
-static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_soc_dai *codec_dai;
-       struct snd_soc_card *card = rtd->card;
-       int srate, i, ret = 0;
-
-       srate = params_rate(params);
-
-       for_each_rtd_codec_dais(rtd, i, codec_dai) {
-               ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
-                                         64 * srate, 256 * srate);
-               if (ret < 0) {
-                       dev_err(card->dev, "codec_dai clock not set\n");
-                       return ret;
-               }
-
-               ret = snd_soc_dai_set_sysclk(codec_dai,
-                                            RT1011_FS_SYS_PRE_S_PLL1,
-                                            256 * srate, SND_SOC_CLOCK_IN);
-               if (ret < 0) {
-                       dev_err(card->dev, "codec_dai clock not set\n");
-                       return ret;
-               }
-       }
-       return ret;
-}
-
-static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
-       .hw_params = mt8195_rt1011_etdm_hw_params,
-};
-
-#define CKSYS_AUD_TOP_CFG 0x032c
-#define CKSYS_AUD_TOP_MON 0x0330
-
-static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_component *cmpnt_afe =
-               snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-       struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
-       struct mt8195_afe_private *afe_priv = afe->platform_priv;
-       struct mtkaif_param *param = &afe_priv->mtkaif_params;
-       int chosen_phase_1, chosen_phase_2, chosen_phase_3;
-       int prev_cycle_1, prev_cycle_2, prev_cycle_3;
-       int test_done_1, test_done_2, test_done_3;
-       int cycle_1, cycle_2, cycle_3;
-       int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
-       int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
-       int mtkaif_calibration_num_phase;
-       bool mtkaif_calibration_ok;
-       unsigned int monitor;
-       int counter;
-       int phase;
-       int i;
-
-       dev_dbg(afe->dev, "%s(), start\n", __func__);
-
-       param->mtkaif_calibration_ok = false;
-       for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
-               param->mtkaif_chosen_phase[i] = -1;
-               param->mtkaif_phase_cycle[i] = 0;
-               mtkaif_chosen_phase[i] = -1;
-               mtkaif_phase_cycle[i] = 0;
-       }
-
-       if (IS_ERR(afe_priv->topckgen)) {
-               dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
-                        __func__);
-               return 0;
-       }
-
-       pm_runtime_get_sync(afe->dev);
-       mt6359_mtkaif_calibration_enable(cmpnt_codec);
-
-       /* set test type to synchronizer pulse */
-       regmap_update_bits(afe_priv->topckgen,
-                          CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
-       mtkaif_calibration_num_phase = 42;      /* mt6359: 0 ~ 42 */
-       mtkaif_calibration_ok = true;
-
-       for (phase = 0;
-            phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
-            phase++) {
-               mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
-                                                   phase, phase, phase);
-
-               regmap_update_bits(afe_priv->topckgen,
-                                  CKSYS_AUD_TOP_CFG, 0x1, 0x1);
-
-               test_done_1 = 0;
-               test_done_2 = 0;
-               test_done_3 = 0;
-               cycle_1 = -1;
-               cycle_2 = -1;
-               cycle_3 = -1;
-               counter = 0;
-               while (!(test_done_1 & test_done_2 & test_done_3)) {
-                       regmap_read(afe_priv->topckgen,
-                                   CKSYS_AUD_TOP_MON, &monitor);
-                       test_done_1 = (monitor >> 28) & 0x1;
-                       test_done_2 = (monitor >> 29) & 0x1;
-                       test_done_3 = (monitor >> 30) & 0x1;
-                       if (test_done_1 == 1)
-                               cycle_1 = monitor & 0xf;
-
-                       if (test_done_2 == 1)
-                               cycle_2 = (monitor >> 4) & 0xf;
-
-                       if (test_done_3 == 1)
-                               cycle_3 = (monitor >> 8) & 0xf;
-
-                       /* handle if never test done */
-                       if (++counter > 10000) {
-                               dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n",
-                                        __func__,
-                                        cycle_1, cycle_2, cycle_3, monitor);
-                               mtkaif_calibration_ok = false;
-                               break;
-                       }
-               }
-
-               if (phase == 0) {
-                       prev_cycle_1 = cycle_1;
-                       prev_cycle_2 = cycle_2;
-                       prev_cycle_3 = cycle_3;
-               }
-
-               if (cycle_1 != prev_cycle_1 &&
-                   mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
-                       mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = phase - 1;
-                       mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] = prev_cycle_1;
-               }
-
-               if (cycle_2 != prev_cycle_2 &&
-                   mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
-                       mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = phase - 1;
-                       mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] = prev_cycle_2;
-               }
-
-               if (cycle_3 != prev_cycle_3 &&
-                   mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
-                       mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = phase - 1;
-                       mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] = prev_cycle_3;
-               }
-
-               regmap_update_bits(afe_priv->topckgen,
-                                  CKSYS_AUD_TOP_CFG, 0x1, 0x0);
-
-               if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] >= 0 &&
-                   mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] >= 0 &&
-                   mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] >= 0)
-                       break;
-       }
-
-       if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
-               mtkaif_calibration_ok = false;
-               chosen_phase_1 = 0;
-       } else {
-               chosen_phase_1 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0];
-       }
-
-       if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
-               mtkaif_calibration_ok = false;
-               chosen_phase_2 = 0;
-       } else {
-               chosen_phase_2 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1];
-       }
-
-       if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
-               mtkaif_calibration_ok = false;
-               chosen_phase_3 = 0;
-       } else {
-               chosen_phase_3 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2];
-       }
-
-       mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
-                                           chosen_phase_1,
-                                           chosen_phase_2,
-                                           chosen_phase_3);
-
-       mt6359_mtkaif_calibration_disable(cmpnt_codec);
-       pm_runtime_put(afe->dev);
-
-       param->mtkaif_calibration_ok = mtkaif_calibration_ok;
-       param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = chosen_phase_1;
-       param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = chosen_phase_2;
-       param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = chosen_phase_3;
-       for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++)
-               param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
-
-       dev_info(afe->dev, "%s(), end, calibration ok %d\n",
-                __func__, param->mtkaif_calibration_ok);
-
-       return 0;
-}
-
-static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-
-       /* set mtkaif protocol */
-       mt6359_set_mtkaif_protocol(cmpnt_codec,
-                                  MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
-
-       /* mtkaif calibration */
-       mt8195_mt6359_mtkaif_calibration(rtd);
-
-       return 0;
-}
-
-static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-       struct mt8195_mt6359_rt1011_rt5682_priv *priv =
-               snd_soc_card_get_drvdata(rtd->card);
-       struct snd_soc_jack *jack = &priv->headset_jack;
-       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 |
-                                   SND_JACK_BTN_3,
-                                   jack, NULL, 0);
-       if (ret) {
-               dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
-               return ret;
-       }
-
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
-
-       ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
-       if (ret) {
-               dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-};
-
-static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                      struct snd_pcm_hw_params *params)
-{
-       /* fix BE i2s format to 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);
-
-       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
-       return 0;
-}
-
-static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
-{
-       static const unsigned int rates[] = {
-               48000
-       };
-       static const unsigned int channels[] = {
-               2, 4, 6, 8
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_rates = {
-               .count = ARRAY_SIZE(rates),
-               .list  = rates,
-               .mask = 0,
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_channels = {
-               .count = ARRAY_SIZE(channels),
-               .list  = channels,
-               .mask = 0,
-       };
-
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int ret;
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_RATE,
-                                        &constraints_rates);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list rate failed\n");
-               return ret;
-       }
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_CHANNELS,
-                                        &constraints_channels);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list channel failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct snd_soc_ops mt8195_hdmitx_dptx_playback_ops = {
-       .startup = mt8195_hdmitx_dptx_startup,
-};
-
-static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-
-       return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
-                                     SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_ops mt8195_dptx_ops = {
-       .hw_params = mt8195_dptx_hw_params,
-};
-
-static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct mt8195_mt6359_rt1011_rt5682_priv *priv =
-               snd_soc_card_get_drvdata(rtd->card);
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-       int ret;
-
-       ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
-                                   &priv->dp_jack, NULL, 0);
-       if (ret)
-               return ret;
-
-       return snd_soc_component_set_jack(cmpnt_codec, &priv->dp_jack, NULL);
-}
-
-static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct mt8195_mt6359_rt1011_rt5682_priv *priv =
-               snd_soc_card_get_drvdata(rtd->card);
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-       int ret;
-
-       ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &priv->hdmi_jack, NULL, 0);
-       if (ret)
-               return ret;
-
-       return snd_soc_component_set_jack(cmpnt_codec, &priv->hdmi_jack, NULL);
-}
-
-static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                      struct snd_pcm_hw_params *params)
-
-{
-       /* fix BE i2s format to 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);
-
-       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
-       return 0;
-}
-
-static int mt8195_playback_startup(struct snd_pcm_substream *substream)
-{
-       static const unsigned int rates[] = {
-               48000
-       };
-       static const unsigned int channels[] = {
-               2
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_rates = {
-               .count = ARRAY_SIZE(rates),
-               .list  = rates,
-               .mask = 0,
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_channels = {
-               .count = ARRAY_SIZE(channels),
-               .list  = channels,
-               .mask = 0,
-       };
-
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int ret;
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_RATE,
-                                        &constraints_rates);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list rate failed\n");
-               return ret;
-       }
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_CHANNELS,
-                                        &constraints_channels);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list channel failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct snd_soc_ops mt8195_playback_ops = {
-       .startup = mt8195_playback_startup,
-};
-
-static int mt8195_capture_startup(struct snd_pcm_substream *substream)
-{
-       static const unsigned int rates[] = {
-               48000
-       };
-       static const unsigned int channels[] = {
-               1, 2
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_rates = {
-               .count = ARRAY_SIZE(rates),
-               .list  = rates,
-               .mask = 0,
-       };
-       static const struct snd_pcm_hw_constraint_list constraints_channels = {
-               .count = ARRAY_SIZE(channels),
-               .list  = channels,
-               .mask = 0,
-       };
-
-       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int ret;
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_RATE,
-                                        &constraints_rates);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list rate failed\n");
-               return ret;
-       }
-
-       ret = snd_pcm_hw_constraint_list(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_CHANNELS,
-                                        &constraints_channels);
-       if (ret < 0) {
-               dev_err(rtd->dev, "hw_constraint_list channel failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct snd_soc_ops mt8195_capture_ops = {
-       .startup = mt8195_capture_startup,
-};
-
-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,
-       DAI_LINK_DL6_FE,
-       DAI_LINK_DL7_FE,
-       DAI_LINK_DL8_FE,
-       DAI_LINK_DL10_FE,
-       DAI_LINK_DL11_FE,
-       DAI_LINK_UL1_FE,
-       DAI_LINK_UL2_FE,
-       DAI_LINK_UL3_FE,
-       DAI_LINK_UL4_FE,
-       DAI_LINK_UL5_FE,
-       DAI_LINK_UL6_FE,
-       DAI_LINK_UL8_FE,
-       DAI_LINK_UL9_FE,
-       DAI_LINK_UL10_FE,
-       DAI_LINK_DL_SRC_BE,
-       DAI_LINK_DPTX_BE,
-       DAI_LINK_ETDM1_IN_BE,
-       DAI_LINK_ETDM2_IN_BE,
-       DAI_LINK_ETDM1_OUT_BE,
-       DAI_LINK_ETDM2_OUT_BE,
-       DAI_LINK_ETDM3_OUT_BE,
-       DAI_LINK_PCM1_BE,
-       DAI_LINK_UL_SRC1_BE,
-       DAI_LINK_UL_SRC2_BE,
-};
-
-/* FE */
-SND_SOC_DAILINK_DEFS(DL2_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL3_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL6_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL7_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL8_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL10_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL11_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL1_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL2_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL3_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL4_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL5_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL6_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL8_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL9_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL10_FE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-/* BE */
-SND_SOC_DAILINK_DEFS(DL_SRC_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
-                                                  "mt6359-snd-codec-aif1")),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DPTX_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
-                                                  RT1011_CODEC_DAI),
-                                       COMP_CODEC(RT1011_DEV1_NAME,
-                                                  RT1011_CODEC_DAI)),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(PCM1_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
-                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL_SRC1_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC1")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
-                                                  "mt6359-snd-codec-aif1"),
-                                       COMP_CODEC("dmic-codec",
-                                                  "dmic-hifi")),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL_SRC2_BE,
-                    DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC2")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
-                                                  "mt6359-snd-codec-aif2")),
-                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = {
-       /* FE */
-       [DAI_LINK_DL2_FE] = {
-               .name = "DL2_FE",
-               .stream_name = "DL2 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_playback_ops,
-               SND_SOC_DAILINK_REG(DL2_FE),
-       },
-       [DAI_LINK_DL3_FE] = {
-               .name = "DL3_FE",
-               .stream_name = "DL3 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_playback_ops,
-               SND_SOC_DAILINK_REG(DL3_FE),
-       },
-       [DAI_LINK_DL6_FE] = {
-               .name = "DL6_FE",
-               .stream_name = "DL6 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_playback_ops,
-               SND_SOC_DAILINK_REG(DL6_FE),
-       },
-       [DAI_LINK_DL7_FE] = {
-               .name = "DL7_FE",
-               .stream_name = "DL7 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_PRE,
-                       SND_SOC_DPCM_TRIGGER_PRE,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               SND_SOC_DAILINK_REG(DL7_FE),
-       },
-       [DAI_LINK_DL8_FE] = {
-               .name = "DL8_FE",
-               .stream_name = "DL8 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_playback_ops,
-               SND_SOC_DAILINK_REG(DL8_FE),
-       },
-       [DAI_LINK_DL10_FE] = {
-               .name = "DL10_FE",
-               .stream_name = "DL10 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_hdmitx_dptx_playback_ops,
-               SND_SOC_DAILINK_REG(DL10_FE),
-       },
-       [DAI_LINK_DL11_FE] = {
-               .name = "DL11_FE",
-               .stream_name = "DL11 Playback",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_playback_ops,
-               SND_SOC_DAILINK_REG(DL11_FE),
-       },
-       [DAI_LINK_UL1_FE] = {
-               .name = "UL1_FE",
-               .stream_name = "UL1 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_PRE,
-                       SND_SOC_DPCM_TRIGGER_PRE,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(UL1_FE),
-       },
-       [DAI_LINK_UL2_FE] = {
-               .name = "UL2_FE",
-               .stream_name = "UL2 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL2_FE),
-       },
-       [DAI_LINK_UL3_FE] = {
-               .name = "UL3_FE",
-               .stream_name = "UL3 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL3_FE),
-       },
-       [DAI_LINK_UL4_FE] = {
-               .name = "UL4_FE",
-               .stream_name = "UL4 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL4_FE),
-       },
-       [DAI_LINK_UL5_FE] = {
-               .name = "UL5_FE",
-               .stream_name = "UL5 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL5_FE),
-       },
-       [DAI_LINK_UL6_FE] = {
-               .name = "UL6_FE",
-               .stream_name = "UL6 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_PRE,
-                       SND_SOC_DPCM_TRIGGER_PRE,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(UL6_FE),
-       },
-       [DAI_LINK_UL8_FE] = {
-               .name = "UL8_FE",
-               .stream_name = "UL8 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL8_FE),
-       },
-       [DAI_LINK_UL9_FE] = {
-               .name = "UL9_FE",
-               .stream_name = "UL9 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL9_FE),
-       },
-       [DAI_LINK_UL10_FE] = {
-               .name = "UL10_FE",
-               .stream_name = "UL10 Capture",
-               .trigger = {
-                       SND_SOC_DPCM_TRIGGER_POST,
-                       SND_SOC_DPCM_TRIGGER_POST,
-               },
-               .dynamic = 1,
-               .dpcm_capture = 1,
-               .ops = &mt8195_capture_ops,
-               SND_SOC_DAILINK_REG(UL10_FE),
-       },
-       /* BE */
-       [DAI_LINK_DL_SRC_BE] = {
-               .name = "DL_SRC_BE",
-               .init = mt8195_mt6359_init,
-               .no_pcm = 1,
-               .dpcm_playback = 1,
-               SND_SOC_DAILINK_REG(DL_SRC_BE),
-       },
-       [DAI_LINK_DPTX_BE] = {
-               .name = "DPTX_BE",
-               .no_pcm = 1,
-               .dpcm_playback = 1,
-               .ops = &mt8195_dptx_ops,
-               .be_hw_params_fixup = mt8195_dptx_hw_params_fixup,
-               SND_SOC_DAILINK_REG(DPTX_BE),
-       },
-       [DAI_LINK_ETDM1_IN_BE] = {
-               .name = "ETDM1_IN_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(ETDM1_IN_BE),
-       },
-       [DAI_LINK_ETDM2_IN_BE] = {
-               .name = "ETDM2_IN_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_capture = 1,
-               .init = mt8195_rt5682_init,
-               .ops = &mt8195_rt5682_etdm_ops,
-               .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
-               SND_SOC_DAILINK_REG(ETDM2_IN_BE),
-       },
-       [DAI_LINK_ETDM1_OUT_BE] = {
-               .name = "ETDM1_OUT_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_playback = 1,
-               .ops = &mt8195_rt5682_etdm_ops,
-               .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
-               SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
-       },
-       [DAI_LINK_ETDM2_OUT_BE] = {
-               .name = "ETDM2_OUT_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_playback = 1,
-               .ops = &mt8195_rt1011_etdm_ops,
-               .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
-               SND_SOC_DAILINK_REG(ETDM2_OUT_BE),
-       },
-       [DAI_LINK_ETDM3_OUT_BE] = {
-               .name = "ETDM3_OUT_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_playback = 1,
-               SND_SOC_DAILINK_REG(ETDM3_OUT_BE),
-       },
-       [DAI_LINK_PCM1_BE] = {
-               .name = "PCM1_BE",
-               .no_pcm = 1,
-               .dai_fmt = SND_SOC_DAIFMT_I2S |
-                       SND_SOC_DAIFMT_NB_NF |
-                       SND_SOC_DAIFMT_CBS_CFS,
-               .dpcm_playback = 1,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(PCM1_BE),
-       },
-       [DAI_LINK_UL_SRC1_BE] = {
-               .name = "UL_SRC1_BE",
-               .no_pcm = 1,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(UL_SRC1_BE),
-       },
-       [DAI_LINK_UL_SRC2_BE] = {
-               .name = "UL_SRC2_BE",
-               .no_pcm = 1,
-               .dpcm_capture = 1,
-               SND_SOC_DAILINK_REG(UL_SRC2_BE),
-       },
-};
-
-static struct snd_soc_codec_conf rt1011_amp_conf[] = {
-       {
-               .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
-               .name_prefix = "Left",
-       },
-       {
-               .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
-               .name_prefix = "Right",
-       },
-};
-
-static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = {
-       .name = "mt8195_r1011_5682",
-       .owner = THIS_MODULE,
-       .dai_link = mt8195_mt6359_rt1011_rt5682_dai_links,
-       .num_links = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_dai_links),
-       .controls = mt8195_mt6359_rt1011_rt5682_controls,
-       .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_controls),
-       .dapm_widgets = mt8195_mt6359_rt1011_rt5682_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_widgets),
-       .dapm_routes = mt8195_mt6359_rt1011_rt5682_routes,
-       .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes),
-       .codec_conf = rt1011_amp_conf,
-       .num_configs = ARRAY_SIZE(rt1011_amp_conf),
-       .set_bias_level_post = mt8195_set_bias_level_post,
-};
-
-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;
-
-       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 = platform_node;
-
-               if (strcmp(dai_link->name, "DPTX_BE") == 0) {
-                       if (!dp_node) {
-                               dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
-                       } else {
-                               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;
-                       }
-               } 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 = 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);
-
-       of_node_put(platform_node);
-       of_node_put(dp_node);
-       of_node_put(hdmi_node);
-       return ret;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = {
-       {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",},
-       {}
-};
-#endif
-
-static const struct dev_pm_ops mt8195_mt6359_rt1011_rt5682_pm_ops = {
-       .poweroff = snd_soc_poweroff,
-       .restore = snd_soc_resume,
-};
-
-static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = {
-       .driver = {
-               .name = "mt8195_mt6359_rt1011_rt5682",
-#ifdef CONFIG_OF
-               .of_match_table = mt8195_mt6359_rt1011_rt5682_dt_match,
-#endif
-               .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops,
-       },
-       .probe = mt8195_mt6359_rt1011_rt5682_dev_probe,
-};
-
-module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver);
-
-/* Module information */
-MODULE_DESCRIPTION("MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver");
-MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("mt8195_mt6359_rt1011_rt5682 soc card");
@@ -1,30 +1,43 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * mt8195-mt6359-rt1019-rt5682.c  --
- *     MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver
+ * mt8195-mt6359.c  --
+ *     MT8195-MT6359 ALSA SoC machine driver code
  *
- * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2022 MediaTek Inc.
  * Author: Trevor Wu <trevor.wu@mediatek.com>
  *        YC Hung <yc.hung@mediatek.com>
  */
 
 #include <linux/input.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #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/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_SPEAKER_AMP_PRESENT             BIT(0)
+#define RT1019_SPEAKER_AMP_PRESENT             BIT(1)
+#define MAX98390_SPEAKER_AMP_PRESENT           BIT(2)
+
+#define RT1011_CODEC_DAI       "rt1011-aif"
+#define RT1011_DEV0_NAME       "rt1011.2-0038"
+#define RT1011_DEV1_NAME       "rt1011.2-0039"
+
 #define RT1019_CODEC_DAI       "HiFi"
 #define RT1019_DEV0_NAME       "rt1019p"
 
+#define MAX98390_CODEC_DAI     "max98390-aif1"
+#define MAX98390_DEV0_NAME     "max98390.2-0038" /* right */
+#define MAX98390_DEV1_NAME     "max98390.2-0039" /* left */
+
 #define RT5682_CODEC_DAI       "rt5682-aif1"
 #define RT5682_DEV0_NAME       "rt5682.2-001a"
 
 #define SOF_DMA_UL4 "SOF_DMA_UL4"
 #define SOF_DMA_UL5 "SOF_DMA_UL5"
 
+struct mt8195_card_data {
+       const char *name;
+       unsigned long quirk;
+};
+
 struct sof_conn_stream {
        const char *normal_link;
        const char *sof_link;
@@ -43,17 +61,15 @@ struct sof_conn_stream {
        int stream_dir;
 };
 
-struct mt8195_mt6359_rt1019_rt5682_priv {
+struct mt8195_mt6359_priv {
        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
-       mt8195_mt6359_rt1019_rt5682_widgets[] = {
-       SND_SOC_DAPM_SPK("Speakers", NULL),
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+static const struct snd_soc_dapm_widget mt8195_mt6359_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", 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),
@@ -61,12 +77,10 @@ static const struct snd_soc_dapm_widget
        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[] = {
-       /* speaker */
-       { "Speakers", NULL, "Speaker" },
+static const struct snd_soc_dapm_route mt8195_mt6359_routes[] = {
        /* headset */
-       { "Headphone Jack", NULL, "HPOL" },
-       { "Headphone Jack", NULL, "HPOR" },
+       { "Headphone", NULL, "HPOL" },
+       { "Headphone", NULL, "HPOR" },
        { "IN1P", NULL, "Headset Mic" },
        /* SOF Uplink */
        {SOF_DMA_UL4, NULL, "O034"},
@@ -80,55 +94,41 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = {
        {"I021", NULL, SOF_DMA_DL3},
 };
 
-static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = {
-       SOC_DAPM_PIN_SWITCH("Speakers"),
-       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+static const struct snd_kcontrol_new mt8195_mt6359_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 };
 
-static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_card *card = rtd->card;
-       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       unsigned int rate = params_rate(params);
-       int bitwidth;
-       int ret;
+static const struct snd_soc_dapm_widget mt8195_dual_speaker_widgets[] = {
+       SND_SOC_DAPM_SPK("Left Spk", NULL),
+       SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
 
-       bitwidth = snd_pcm_format_width(params_format(params));
-       if (bitwidth < 0) {
-               dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
-               return bitwidth;
-       }
+static const struct snd_kcontrol_new mt8195_dual_speaker_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Left Spk"),
+       SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
 
-       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
-       if (ret) {
-               dev_err(card->dev, "failed to set tdm slot\n");
-               return ret;
-       }
+static const struct snd_soc_dapm_widget mt8195_speaker_widgets[] = {
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
 
-       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;
-       }
+static const struct snd_kcontrol_new mt8195_speaker_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
 
-       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;
-       }
+static const struct snd_soc_dapm_route mt8195_rt1011_routes[] = {
+       { "Left Spk", NULL, "Left SPO" },
+       { "Right Spk", NULL, "Right SPO" },
+};
 
-       return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
-                                     SND_SOC_CLOCK_OUT);
-}
+static const struct snd_soc_dapm_route mt8195_rt1019_routes[] = {
+       { "Ext Spk", NULL, "Speaker" },
+};
 
-static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
-       .hw_params = mt8195_rt5682_etdm_hw_params,
+static const struct snd_soc_dapm_route mt8195_max98390_routes[] = {
+       { "Left Spk", NULL, "Left BE_OUT" },
+       { "Right Spk", NULL, "Right BE_OUT" },
 };
 
 #define CKSYS_AUD_TOP_CFG 0x032c
@@ -143,20 +143,20 @@ static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
        struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
        struct mt8195_afe_private *afe_priv = afe->platform_priv;
        struct mtkaif_param *param = &afe_priv->mtkaif_params;
-       int phase;
-       unsigned int monitor;
-       int mtkaif_calibration_num_phase;
+       int chosen_phase_1, chosen_phase_2, chosen_phase_3;
+       int prev_cycle_1, prev_cycle_2, prev_cycle_3;
        int test_done_1, test_done_2, test_done_3;
        int cycle_1, cycle_2, cycle_3;
-       int prev_cycle_1, prev_cycle_2, prev_cycle_3;
-       int chosen_phase_1, chosen_phase_2, chosen_phase_3;
-       int counter;
-       bool mtkaif_calibration_ok;
        int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
        int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
+       int mtkaif_calibration_num_phase;
+       bool mtkaif_calibration_ok;
+       unsigned int monitor;
+       int counter;
+       int phase;
        int i;
 
-       dev_info(afe->dev, "%s(), start\n", __func__);
+       dev_dbg(afe->dev, "%s(), start\n", __func__);
 
        param->mtkaif_calibration_ok = false;
        for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
@@ -312,57 +312,6 @@ static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
-static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_component *cmpnt_codec =
-               asoc_rtd_to_codec(rtd, 0)->component;
-       struct mt8195_mt6359_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 |
-                                   SND_JACK_BTN_3,
-                                   jack, NULL, 0);
-       if (ret) {
-               dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
-               return ret;
-       }
-
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
-       snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
-
-       ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
-       if (ret) {
-               dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-};
-
-static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                      struct snd_pcm_hw_params *params)
-{
-       /* fix BE i2s format to 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);
-
-       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
-       return 0;
-}
-
 static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
 {
        static const unsigned int rates[] = {
@@ -414,11 +363,8 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-       unsigned int rate = params_rate(params);
-       unsigned int mclk_fs_ratio = 256;
-       unsigned int mclk_fs = rate * mclk_fs_ratio;
 
-       return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs,
+       return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
                                      SND_SOC_CLOCK_OUT);
 }
 
@@ -428,14 +374,13 @@ static const struct snd_soc_ops mt8195_dptx_ops = {
 
 static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct mt8195_mt6359_rt1019_rt5682_priv *priv =
-               snd_soc_card_get_drvdata(rtd->card);
+       struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_component *cmpnt_codec =
                asoc_rtd_to_codec(rtd, 0)->component;
-       int ret = 0;
+       int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
-                                   &priv->dp_jack, NULL, 0);
+                                   &priv->dp_jack);
        if (ret)
                return ret;
 
@@ -444,14 +389,13 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct mt8195_mt6359_rt1019_rt5682_priv *priv =
-               snd_soc_card_get_drvdata(rtd->card);
+       struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_component *cmpnt_codec =
                asoc_rtd_to_codec(rtd, 0)->component;
-       int ret = 0;
+       int ret;
 
        ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &priv->hdmi_jack, NULL, 0);
+                                   &priv->hdmi_jack);
        if (ret)
                return ret;
 
@@ -460,7 +404,6 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
                                       struct snd_pcm_hw_params *params)
-
 {
        /* fix BE i2s format to S24_LE, clean param mask first */
        snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
@@ -563,12 +506,223 @@ static const struct snd_soc_ops mt8195_capture_ops = {
        .startup = mt8195_capture_startup,
 };
 
+static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+       unsigned int rate = params_rate(params);
+       int bitwidth;
+       int ret;
+
+       bitwidth = snd_pcm_format_width(params_format(params));
+       if (bitwidth < 0) {
+               dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+               return bitwidth;
+       }
+
+       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+       if (ret) {
+               dev_err(card->dev, "failed to set tdm slot\n");
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_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,
+                                    rate * 512, SND_SOC_CLOCK_IN);
+       if (ret) {
+               dev_err(card->dev, "failed to set sysclk\n");
+               return ret;
+       }
+
+       return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
+                                     SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
+       .hw_params = mt8195_rt5682_etdm_hw_params,
+};
+
+static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_component *cmpnt_codec =
+               asoc_rtd_to_codec(rtd, 0)->component;
+       struct mt8195_mt6359_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 |
+                                   SND_JACK_BTN_3,
+                                   jack);
+       if (ret) {
+               dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+               return ret;
+       }
+
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+       ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+       if (ret) {
+               dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+};
+
+static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_card *card = rtd->card;
+       int srate, i, ret;
+
+       srate = params_rate(params);
+
+       for_each_rtd_codec_dais(rtd, i, codec_dai) {
+               ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+                                         64 * srate, 256 * srate);
+               if (ret < 0) {
+                       dev_err(card->dev, "codec_dai clock not set\n");
+                       return ret;
+               }
+
+               ret = snd_soc_dai_set_sysclk(codec_dai,
+                                            RT1011_FS_SYS_PRE_S_PLL1,
+                                            256 * srate, SND_SOC_CLOCK_IN);
+               if (ret < 0) {
+                       dev_err(card->dev, "codec_dai clock not set\n");
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
+       .hw_params = mt8195_rt1011_etdm_hw_params,
+};
+
+static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets,
+                                       ARRAY_SIZE(mt8195_dual_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, mt8195_dual_speaker_controls,
+                                       ARRAY_SIZE(mt8195_dual_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, mt8195_rt1011_routes,
+                                     ARRAY_SIZE(mt8195_rt1011_routes));
+       if (ret)
+               dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+       return ret;
+}
+
+static int mt8195_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_speaker_widgets,
+                                       ARRAY_SIZE(mt8195_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, mt8195_speaker_controls,
+                                       ARRAY_SIZE(mt8195_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, mt8195_rt1019_routes,
+                                     ARRAY_SIZE(mt8195_rt1019_routes));
+       if (ret)
+               dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+       return ret;
+}
+
+static int mt8195_max98390_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets,
+                                       ARRAY_SIZE(mt8195_dual_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, mt8195_dual_speaker_controls,
+                                       ARRAY_SIZE(mt8195_dual_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, mt8195_max98390_routes,
+                                     ARRAY_SIZE(mt8195_max98390_routes));
+       if (ret)
+               dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+       return ret;
+}
+
+static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                                      struct snd_pcm_hw_params *params)
+{
+       /* 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);
+
+       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+       return 0;
+}
+
 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);
+       struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
        int ret;
 
        /*
@@ -581,7 +735,6 @@ static int mt8195_set_bias_level_post(struct snd_soc_card *card,
            strcmp(component->name, RT5682S_DEV0_NAME)))
                return 0;
 
-
        switch (level) {
        case SND_SOC_BIAS_OFF:
                if (!__clk_is_enabled(priv->i2so1_mclk))
@@ -753,8 +906,7 @@ SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
 
 SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
                     DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
-                    DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME,
-                                                  RT1019_CODEC_DAI)),
+                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
                     DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
 SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
@@ -787,149 +939,45 @@ SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
                     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;
-}
+                    DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")),
+                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
-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;
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL4,
+                    DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")),
+                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
-       /* 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;
-                       }
-               }
-       }
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL5,
+                    DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")),
+                    DAILINK_COMP_ARRAY(COMP_DUMMY()),
+                    DAILINK_COMP_ARRAY(COMP_EMPTY()));
 
-       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;
+/* codec */
+SND_SOC_DAILINK_DEF(rt1019_comps,
+                   DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME,
+                                                 RT1019_CODEC_DAI)));
 
-               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;
+SND_SOC_DAILINK_DEF(rt1011_comps,
+                   DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
+                                                 RT1011_CODEC_DAI),
+                                      COMP_CODEC(RT1011_DEV1_NAME,
+                                                 RT1011_CODEC_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;
-               }
-       }
+SND_SOC_DAILINK_DEF(max98390_comps,
+                   DAILINK_COMP_ARRAY(COMP_CODEC(MAX98390_DEV0_NAME,
+                                                 MAX98390_CODEC_DAI),
+                                      COMP_CODEC(MAX98390_DEV1_NAME,
+                                                 MAX98390_CODEC_DAI)));
 
-       return 0;
-}
+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},
+};
 
-static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
+static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
        /* FE */
        [DAI_LINK_DL2_FE] = {
                .name = "DL2_FE",
@@ -1234,20 +1282,162 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
        },
 };
 
-static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = {
-       .name = "mt8195_r1019_5682",
+static struct snd_soc_codec_conf rt1011_codec_conf[] = {
+       {
+               .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+               .name_prefix = "Left",
+       },
+       {
+               .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+               .name_prefix = "Right",
+       },
+};
+
+static struct snd_soc_codec_conf max98390_codec_conf[] = {
+       {
+               .dlc = COMP_CODEC_CONF(MAX98390_DEV0_NAME),
+               .name_prefix = "Right",
+       },
+       {
+               .dlc = COMP_CODEC_CONF(MAX98390_DEV1_NAME),
+               .name_prefix = "Left",
+       },
+};
+
+static struct snd_soc_card mt8195_mt6359_soc_card = {
        .owner = THIS_MODULE,
-       .dai_link = mt8195_mt6359_rt1019_rt5682_dai_links,
-       .num_links = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links),
-       .controls = mt8195_mt6359_rt1019_rt5682_controls,
-       .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_controls),
-       .dapm_widgets = mt8195_mt6359_rt1019_rt5682_widgets,
-       .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),
+       .dai_link = mt8195_mt6359_dai_links,
+       .num_links = ARRAY_SIZE(mt8195_mt6359_dai_links),
+       .controls = mt8195_mt6359_controls,
+       .num_controls = ARRAY_SIZE(mt8195_mt6359_controls),
+       .dapm_widgets = mt8195_mt6359_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_widgets),
+       .dapm_routes = mt8195_mt6359_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_routes),
        .set_bias_level_post = mt8195_set_bias_level_post,
 };
 
+/* 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_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 int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
                                   const char *propname)
 {
@@ -1258,7 +1448,7 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
 
        num_links = of_property_count_strings(np, "mediatek,dai-link");
 
-       if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) {
+       if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_dai_links)) {
                dev_dbg(dev, "number of dai-link is invalid\n");
                return -EINVAL;
        }
@@ -1278,9 +1468,9 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
                        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],
+               for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_dai_links); j++) {
+                       if (!strcmp(dai_name, mt8195_mt6359_dai_links[j].name)) {
+                               memcpy(link, &mt8195_mt6359_dai_links[j],
                                       sizeof(struct snd_soc_dai_link));
                                link++;
                                card->num_links++;
@@ -1295,17 +1485,19 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
        return 0;
 }
 
-static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
+static int mt8195_mt6359_dev_probe(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card;
+       struct snd_soc_card *card = &mt8195_mt6359_soc_card;
        struct snd_soc_dai_link *dai_link;
-       struct mt8195_mt6359_rt1019_rt5682_priv *priv;
+       struct mt8195_mt6359_priv *priv;
        struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node;
+       struct mt8195_card_data *card_data;
        int is5682s = 0;
        int init6359 = 0;
        int sof_on = 0;
        int ret, i;
 
+       card_data = (struct mt8195_card_data *)of_device_get_match_data(&pdev->dev);
        card->dev = &pdev->dev;
 
        ret = snd_soc_of_parse_card_name(card, "model");
@@ -1315,6 +1507,9 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
                return ret;
        }
 
+       if (!card->name)
+               card->name = card_data->name;
+
        if (strstr(card->name, "_5682s"))
                is5682s = 1;
 
@@ -1322,6 +1517,18 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
+       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;
+       }
+
        platform_node = of_parse_phandle(pdev->dev.of_node,
                                         "mediatek,platform", 0);
        if (!platform_node) {
@@ -1337,19 +1544,6 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
        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");
-                       ret = -EINVAL;
-                       goto put_node;
-               }
-       } else {
-               if (!sof_on)
-                       card->num_links = DAI_LINK_REGULAR_NUM;
-       }
-
        for_each_card_prelinks(card, i, dai_link) {
                if (!dai_link->platforms->name) {
                        if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on)
@@ -1389,17 +1583,42 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
                                dai_link->init = mt8195_mt6359_init;
                                init6359 = 1;
                        }
+               } else if (strcmp(dai_link->name, "ETDM2_OUT_BE") == 0) {
+                       switch (card_data->quirk) {
+                       case RT1011_SPEAKER_AMP_PRESENT:
+                               dai_link->codecs = rt1011_comps;
+                               dai_link->num_codecs = ARRAY_SIZE(rt1011_comps);
+                               dai_link->init = mt8195_rt1011_init;
+                               dai_link->ops = &mt8195_rt1011_etdm_ops;
+                               dai_link->be_hw_params_fixup = mt8195_etdm_hw_params_fixup;
+                               card->codec_conf = rt1011_codec_conf;
+                               card->num_configs = ARRAY_SIZE(rt1011_codec_conf);
+                               break;
+                       case RT1019_SPEAKER_AMP_PRESENT:
+                               dai_link->codecs = rt1019_comps;
+                               dai_link->num_codecs = ARRAY_SIZE(rt1019_comps);
+                               dai_link->init = mt8195_rt1019_init;
+                               break;
+                       case MAX98390_SPEAKER_AMP_PRESENT:
+                               dai_link->codecs = max98390_comps;
+                               dai_link->num_codecs = ARRAY_SIZE(max98390_comps);
+                               dai_link->init = mt8195_max98390_init;
+                               card->codec_conf = max98390_codec_conf;
+                               card->num_configs = ARRAY_SIZE(max98390_codec_conf);
+                               break;
+                       default:
+                               break;
+                       }
                }
        }
 
        if (sof_on)
-               card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe;
+               card->late_probe = mt8195_mt6359_card_late_probe;
 
        snd_soc_card_set_drvdata(card, priv);
 
        ret = devm_snd_soc_register_card(&pdev->dev, card);
 
-put_node:
        of_node_put(platform_node);
        of_node_put(adsp_node);
        of_node_put(dp_node);
@@ -1407,34 +1626,56 @@ put_node:
        return ret;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = {
-       {.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",},
-       {}
+static struct mt8195_card_data mt8195_mt6359_rt1019_rt5682_card = {
+       .name = "mt8195_r1019_5682",
+       .quirk = RT1019_SPEAKER_AMP_PRESENT,
+};
+
+static struct mt8195_card_data mt8195_mt6359_rt1011_rt5682_card = {
+       .name = "mt8195_r1011_5682",
+       .quirk = RT1011_SPEAKER_AMP_PRESENT,
+};
+
+static struct mt8195_card_data mt8195_mt6359_max98390_rt5682_card = {
+       .name = "mt8195_m98390_r5682",
+       .quirk = MAX98390_SPEAKER_AMP_PRESENT,
+};
+
+static const struct of_device_id mt8195_mt6359_dt_match[] = {
+       {
+               .compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",
+               .data = &mt8195_mt6359_rt1019_rt5682_card,
+       },
+       {
+               .compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",
+               .data = &mt8195_mt6359_rt1011_rt5682_card,
+       },
+       {
+               .compatible = "mediatek,mt8195_mt6359_max98390_rt5682",
+               .data = &mt8195_mt6359_max98390_rt5682_card,
+       },
+       {},
 };
-#endif
 
-static const struct dev_pm_ops mt8195_mt6359_rt1019_rt5682_pm_ops = {
+static const struct dev_pm_ops mt8195_mt6359_pm_ops = {
        .poweroff = snd_soc_poweroff,
        .restore = snd_soc_resume,
 };
 
-static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = {
+static struct platform_driver mt8195_mt6359_driver = {
        .driver = {
-               .name = "mt8195_mt6359_rt1019_rt5682",
-#ifdef CONFIG_OF
-               .of_match_table = mt8195_mt6359_rt1019_rt5682_dt_match,
-#endif
-               .pm = &mt8195_mt6359_rt1019_rt5682_pm_ops,
+               .name = "mt8195_mt6359",
+               .of_match_table = mt8195_mt6359_dt_match,
+               .pm = &mt8195_mt6359_pm_ops,
        },
-       .probe = mt8195_mt6359_rt1019_rt5682_dev_probe,
+       .probe = mt8195_mt6359_dev_probe,
 };
 
-module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver);
+module_platform_driver(mt8195_mt6359_driver);
 
 /* Module information */
-MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver");
+MODULE_DESCRIPTION("MT8195-MT6359 ALSA SoC machine driver");
 MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
 MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card");
+MODULE_ALIAS("mt8195_mt6359 soc card");
index 879c122..7afe1a1 100644 (file)
@@ -754,6 +754,7 @@ static int mxs_saif_probe(struct platform_device *pdev)
                saif->master_id = saif->id;
        } else {
                ret = of_alias_get_id(master, "saif");
+               of_node_put(master);
                if (ret < 0)
                        return ret;
                else
index cecbec2..a045693 100644 (file)
@@ -45,7 +45,7 @@ config SND_PXA2XX_SOC_CORGI
        tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
        depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C
        select SND_PXA2XX_SOC_I2S
-       select SND_SOC_WM8731
+       select SND_SOC_WM8731_I2C
        help
          Say Y if you want to add support for SoC audio on Sharp
          Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
@@ -71,7 +71,7 @@ config SND_PXA2XX_SOC_POODLE
        tristate "SoC Audio support for Poodle"
        depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
        select SND_PXA2XX_SOC_I2S
-       select SND_SOC_WM8731
+       select SND_SOC_WM8731_I2C
        help
          Say Y if you want to add support for SoC audio on Sharp
          Zaurus SL-5600 model (Poodle).
index 7334fac..9a81615 100644 (file)
@@ -122,9 +122,9 @@ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
        int err;
 
        /* Jack detection API stuff */
-       err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
-                                   SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
-                                   ARRAY_SIZE(hs_jack_pin));
+       err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+                                        SND_JACK_HEADPHONE, &hs_jack,
+                                        hs_jack_pin, ARRAY_SIZE(hs_jack_pin));
        if (err)
                return err;
 
index b92ea1a..65257f7 100644 (file)
@@ -71,9 +71,10 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
        int err;
 
        /* Jack detection API stuff */
-       err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
-                                   SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins,
-                                   ARRAY_SIZE(hs_jack_pins));
+       err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+                                        SND_JACK_HEADPHONE, &hs_jack,
+                                        hs_jack_pins,
+                                        ARRAY_SIZE(hs_jack_pins));
        if (err)
                return err;
 
index d5f2961..6cc970b 100644 (file)
@@ -64,12 +64,14 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
 
        /* Headset jack detection */
-       snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE |
-                             SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
-                             &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
-       snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE,
-                             &mic_jack, mic_jack_pins,
-                             ARRAY_SIZE(mic_jack_pins));
+       snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+                                  SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
+                                  SND_JACK_BTN_1 | SND_JACK_BTN_2,
+                                  &hs_jack,
+                                  hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+       snd_soc_card_jack_new_pins(rtd->card, "Microphone Jack",
+                                  SND_JACK_MICROPHONE, &mic_jack,
+                                  mic_jack_pins, ARRAY_SIZE(mic_jack_pins));
 
        /* headphone, microphone detection & headset short detection */
        pm860x_hs_jack_detect(component, &hs_jack, SND_JACK_HEADPHONE,
index edf2b9e..f4a7cfe 100644 (file)
@@ -132,9 +132,10 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
        int ret;
 
        /* Jack detection API stuff */
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
-                                   &hs_jack, hs_jack_pins,
-                                   ARRAY_SIZE(hs_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET, &hs_jack,
+                                        hs_jack_pins,
+                                        ARRAY_SIZE(hs_jack_pins));
        if (ret)
                goto err;
 
index 28d0dfb..7506534 100644 (file)
@@ -197,6 +197,8 @@ config SND_SOC_SC7280
        select SND_SOC_LPASS_MACRO_COMMON
        imply SND_SOC_LPASS_RX_MACRO
        imply SND_SOC_LPASS_TX_MACRO
+       select SND_SOC_RT5682_I2C
+       select SND_SOC_RT5682S
        help
          Add support for audio on Qualcomm Technologies Inc.
          SC7280 SoC-based systems.
index f9d6937..b0a4f7c 100644 (file)
@@ -96,7 +96,7 @@ static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s)
                                             SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                             SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                             SND_JACK_BTN_4,
-                                            &pdata->jack, NULL, 0);
+                                            &pdata->jack);
 
                if (rval < 0) {
                        dev_err(card->dev, "Unable to add Headphone Jack\n");
index 74d62f3..f03a7ae 100644 (file)
@@ -1160,7 +1160,7 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp
                break;
        }
 
-       buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WT);
+       buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WC);
 
        return 0;
 }
index 37225ef..efccb5c 100644 (file)
@@ -57,7 +57,7 @@ static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
                        SND_JACK_HEADPHONE |
                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                        SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                       &pdata->hs_jack, NULL, 0);
+                       &pdata->hs_jack);
 
        if (rval < 0) {
                dev_err(card->dev, "Unable to add Headset Jack\n");
@@ -89,7 +89,7 @@ static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        rval = snd_soc_card_jack_new(
                        card, "HDMI Jack",
                        SND_JACK_LINEOUT,
-                       &pdata->hdmi_jack, NULL, 0);
+                       &pdata->hdmi_jack);
 
        if (rval < 0) {
                dev_err(card->dev, "Unable to add HDMI Jack\n");
index bd0bf9c..34cdb99 100644 (file)
 #include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/rt5682s.h>
 #include <linux/soundwire/sdw.h>
 
+#include "../codecs/rt5682.h"
+#include "../codecs/rt5682s.h"
 #include "common.h"
 #include "lpass.h"
 
+#define DEFAULT_MCLK_RATE              19200000
+#define RT5682_PLL_FREQ (48000 * 512)
+
 struct sc7280_snd_data {
        struct snd_soc_card card;
        struct sdw_stream_runtime *sruntime[LPASS_MAX_PORTS];
+       u32 pri_mi2s_clk_count;
        struct snd_soc_jack hs_jack;
        struct snd_soc_jack hdmi_jack;
        bool jack_setup;
@@ -50,7 +57,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
                                             SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                             SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                             SND_JACK_BTN_4 | SND_JACK_BTN_5,
-                                            &pdata->hs_jack, NULL, 0);
+                                            &pdata->hs_jack);
 
                if (rval < 0) {
                        dev_err(card->dev, "Unable to add Headset Jack\n");
@@ -69,6 +76,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
                pdata->jack_setup = true;
        }
        switch (cpu_dai->id) {
+       case MI2S_PRIMARY:
        case LPASS_CDC_DMA_RX0:
        case LPASS_CDC_DMA_TX3:
                for_each_rtd_codec_dais(rtd, i, codec_dai) {
@@ -96,7 +104,7 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        int rval;
 
        rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
-                                    &pdata->hdmi_jack, NULL, 0);
+                                    &pdata->hdmi_jack);
 
        if (rval < 0) {
                dev_err(card->dev, "Unable to add HDMI Jack\n");
@@ -110,11 +118,51 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
 }
 
+static int sc7280_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+       struct snd_soc_card *card = rtd->card;
+       struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+       int ret;
+
+       if (++data->pri_mi2s_clk_count == 1) {
+               snd_soc_dai_set_sysclk(cpu_dai,
+                       LPASS_MCLK0,
+                       DEFAULT_MCLK_RATE,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+       }
+       snd_soc_dai_set_fmt(codec_dai,
+                               SND_SOC_DAIFMT_CBC_CFC |
+                               SND_SOC_DAIFMT_NB_NF |
+                               SND_SOC_DAIFMT_I2S);
+
+       ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+                                       DEFAULT_MCLK_RATE, RT5682_PLL_FREQ);
+       if (ret) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+                                       RT5682_PLL_FREQ,
+                                       SND_SOC_CLOCK_IN);
+
+       if (ret) {
+               dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int sc7280_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
 
        switch (cpu_dai->id) {
+       case MI2S_PRIMARY:
        case LPASS_CDC_DMA_TX3:
                return sc7280_headset_init(rtd);
        case LPASS_CDC_DMA_RX0:
@@ -227,10 +275,54 @@ static int sc7280_snd_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static void sc7280_snd_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_card *card = rtd->card;
+       struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+       switch (cpu_dai->id) {
+       case MI2S_PRIMARY:
+               if (--data->pri_mi2s_clk_count == 0) {
+                       snd_soc_dai_set_sysclk(cpu_dai,
+                                              LPASS_MCLK0,
+                                              0,
+                                              SNDRV_PCM_STREAM_PLAYBACK);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int sc7280_snd_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+       int ret = 0;
+
+       switch (cpu_dai->id) {
+       case MI2S_PRIMARY:
+               ret = sc7280_rt5682_init(rtd);
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
 static const struct snd_soc_ops sc7280_ops = {
+       .startup = sc7280_snd_startup,
        .hw_params = sc7280_snd_hw_params,
        .hw_free = sc7280_snd_hw_free,
        .prepare = sc7280_snd_prepare,
+       .shutdown = sc7280_snd_shutdown,
+};
+
+static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
 };
 
 static int sc7280_snd_platform_probe(struct platform_device *pdev)
@@ -252,6 +344,9 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev)
        card->driver_name = "SC7280";
        card->dev = dev;
 
+       card->dapm_widgets = sc7280_snd_widgets;
+       card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets);
+
        ret = qcom_snd_parse_of(card);
        if (ret)
                return ret;
index 5c1d13e..61fda79 100644 (file)
@@ -247,7 +247,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
                                SND_JACK_HEADPHONE |
                                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                               &pdata->jack, NULL, 0);
+                               &pdata->jack);
 
                if (rval < 0) {
                        dev_err(card->dev, "Unable to add Headphone Jack\n");
index 114a29e..6e1184c 100644 (file)
@@ -41,7 +41,7 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
                                             SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                             SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                             SND_JACK_BTN_4 | SND_JACK_BTN_5,
-                                            &data->jack, NULL, 0);
+                                            &data->jack);
 
                if (rval < 0) {
                        dev_err(card->dev, "Unable to add Headphone Jack\n");
index b052642..bcdedde 100644 (file)
@@ -124,10 +124,10 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
 
        /* Enable Headset Jack detection */
        if (gpio_is_valid(machine->gpio_hp_det)) {
-               snd_soc_card_jack_new(runtime->card, "Headphone Jack",
-                                     SND_JACK_HEADPHONE, &headphone_jack,
-                                     headphone_jack_pins,
-                                     ARRAY_SIZE(headphone_jack_pins));
+               snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
+                                          SND_JACK_HEADPHONE, &headphone_jack,
+                                          headphone_jack_pins,
+                                          ARRAY_SIZE(headphone_jack_pins));
                rk_hp_jack_gpio.gpio = machine->gpio_hp_det;
                snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
        }
index eeef3ed..2540b9b 100644 (file)
@@ -174,7 +174,7 @@ static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd)
 
        /* Enable jack detection. */
        ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT,
-                                   &cdn_dp_card_jack, NULL, 0);
+                                   &cdn_dp_card_jack);
        if (ret) {
                dev_err(card->dev, "Can't create DP Jack %d\n", ret);
                return ret;
@@ -204,13 +204,13 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* Enable Headset and 4 Buttons Jack detection */
-       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                   SND_JACK_HEADSET | SND_JACK_LINEOUT |
-                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &rockchip_sound_jack,
-                                   rockchip_sound_jack_pins,
-                                   ARRAY_SIZE(rockchip_sound_jack_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                        SND_JACK_HEADSET | SND_JACK_LINEOUT |
+                                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                        SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                        &rockchip_sound_jack,
+                                        rockchip_sound_jack_pins,
+                                        ARRAY_SIZE(rockchip_sound_jack_pins));
 
        if (ret) {
                dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);
index c8f1a28..150ac52 100644 (file)
@@ -231,7 +231,7 @@ static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)
 
        /* enable jack detection */
        ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
-                                   &rk_hdmi_jack, NULL, 0);
+                                   &rk_hdmi_jack);
        if (ret) {
                dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
                return ret;
@@ -345,13 +345,13 @@ static int rk_98090_headset_init(struct snd_soc_component *component)
        int ret;
 
        /* Enable Headset and 4 Buttons Jack detection */
-       ret = snd_soc_card_jack_new(component->card, "Headset Jack",
-                                   SND_JACK_HEADSET |
-                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &headset_jack,
-                                   headset_jack_pins,
-                                   ARRAY_SIZE(headset_jack_pins));
+       ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack",
+                                        SND_JACK_HEADSET |
+                                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                        SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                        &headset_jack,
+                                        headset_jack_pins,
+                                        ARRAY_SIZE(headset_jack_pins));
        if (ret)
                return ret;
 
index 16ca2ad..d07cc5c 100644 (file)
@@ -107,7 +107,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                                   &headset_jack, NULL, 0);
+                                   &headset_jack);
        if (ret) {
                dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
                return ret;
index 5265e54..bb0cf42 100644 (file)
@@ -343,7 +343,7 @@ static int aries_late_probe(struct snd_soc_card *card)
        struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
        int ret, irq;
 
-       ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT,
+       ret = snd_soc_card_jack_new_pins(card, "Dock", SND_JACK_LINEOUT,
                        &aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
        if (ret)
                return ret;
@@ -361,7 +361,7 @@ static int aries_late_probe(struct snd_soc_card *card)
        else
                snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
 
-       ret = snd_soc_card_jack_new(card, "Headset",
+       ret = snd_soc_card_jack_new_pins(card, "Headset",
                        SND_JACK_HEADSET | SND_JACK_BTN_0,
                        &aries_headset,
                        jack_pins, ARRAY_SIZE(jack_pins));
@@ -585,10 +585,10 @@ 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);
+       of_node_put(extcon_np);
        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))
index 8b83f39..76998a4 100644 (file)
@@ -386,11 +386,11 @@ static struct snd_soc_codec_conf bells_codec_conf[] = {
        },
 };
 
-static struct snd_soc_dapm_widget bells_widgets[] = {
+static const struct snd_soc_dapm_widget bells_widgets[] = {
        SND_SOC_DAPM_MIC("DMIC", NULL),
 };
 
-static struct snd_soc_dapm_route bells_routes[] = {
+static const struct snd_soc_dapm_route bells_routes[] = {
        { "Sub CLK_SYS", NULL, "OPCLK" },
        { "CLKIN", NULL, "OPCLK" },
 
index c994e67..907266a 100644 (file)
@@ -151,7 +151,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
 
 static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
-       snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+       snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+               SND_JACK_HEADPHONE,
                &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
 
        snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
index 34067cc..411e25c 100644 (file)
@@ -228,7 +228,7 @@ static const struct snd_kcontrol_new controls[] = {
        SOC_DAPM_PIN_SWITCH("WM1250 Output"),
 };
 
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
 
        SND_SOC_DAPM_MIC("AMIC", NULL),
@@ -239,7 +239,7 @@ static struct snd_soc_dapm_widget widgets[] = {
                              SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 };
 
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
        { "Headphone", NULL, "HPOUT1L" },
        { "Headphone", NULL, "HPOUT1R" },
 
@@ -285,7 +285,7 @@ static int littlemill_late_probe(struct snd_soc_card *card)
                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
                                    SND_JACK_BTN_2 | SND_JACK_BTN_3 |
                                    SND_JACK_BTN_4 | SND_JACK_BTN_5,
-                                   &littlemill_headset, NULL, 0);
+                                   &littlemill_headset);
        if (ret)
                return ret;
 
index 7b12ccd..b44f5e9 100644 (file)
@@ -51,10 +51,11 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &lowland_headset, lowland_headset_pins,
-                                   ARRAY_SIZE(lowland_headset_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+                                        SND_JACK_LINEOUT | SND_JACK_HEADSET |
+                                        SND_JACK_BTN_0,
+                                        &lowland_headset, lowland_headset_pins,
+                                        ARRAY_SIZE(lowland_headset_pins));
        if (ret)
                return ret;
 
@@ -140,7 +141,7 @@ static const struct snd_kcontrol_new controls[] = {
        SOC_DAPM_PIN_SWITCH("Headphone"),
 };
 
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 
@@ -150,7 +151,7 @@ static struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_MIC("Main DMIC", NULL),
 };
 
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
        { "Sub IN1", NULL, "HPOUT2L" },
        { "Sub IN2", NULL, "HPOUT2R" },
 
index 5e9dc18..6931b9a 100644 (file)
@@ -309,7 +309,7 @@ static int midas_late_probe(struct snd_soc_card *card)
                        SND_JACK_HEADSET | SND_JACK_MECHANICAL |
                        SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
                        SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5,
-                       &priv->headset_jack, NULL, 0);
+                       &priv->headset_jack);
        if (ret)
                return ret;
 
index 6ea1c8c..ff3acc9 100644 (file)
@@ -201,7 +201,8 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
 
 static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
-       snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+       snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+               SND_JACK_HEADPHONE,
                &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
 
        snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
index cee39ad..29bf917 100644 (file)
@@ -139,10 +139,10 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "ROUT1");
 
        /* Headphone jack detection */
-       err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
-                                   SND_JACK_HEADPHONE, &smartq_jack,
-                                   smartq_jack_pins,
-                                   ARRAY_SIZE(smartq_jack_pins));
+       err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+                                        SND_JACK_HEADPHONE, &smartq_jack,
+                                        smartq_jack_pins,
+                                        ARRAY_SIZE(smartq_jack_pins));
        if (err)
                return err;
 
index 226c359..47b6d19 100644 (file)
@@ -467,8 +467,7 @@ static int spdif_remove(struct platform_device *pdev)
        iounmap(spdif->regs);
 
        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (mem_res)
-               release_mem_region(mem_res->start, resource_size(mem_res));
+       release_mem_region(mem_res->start, resource_size(mem_res));
 
        clk_disable_unprepare(spdif->sclk);
        clk_disable_unprepare(spdif->pclk);
index 37b1f4f..69d7b01 100644 (file)
@@ -156,10 +156,12 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
                pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
        gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
 
-       ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                   &speyside_headset, speyside_headset_pins,
-                                   ARRAY_SIZE(speyside_headset_pins));
+       ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+                                        SND_JACK_LINEOUT | SND_JACK_HEADSET |
+                                        SND_JACK_BTN_0,
+                                        &speyside_headset,
+                                        speyside_headset_pins,
+                                        ARRAY_SIZE(speyside_headset_pins));
        if (ret)
                return ret;
 
@@ -261,7 +263,7 @@ static const struct snd_kcontrol_new controls[] = {
        SOC_DAPM_PIN_SWITCH("Headphone"),
 };
 
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 
@@ -271,7 +273,7 @@ static struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_MIC("Main DMIC", NULL),
 };
 
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
        { "IN1RN", NULL, "MICB1" },
        { "IN1RP", NULL, "MICB1" },
        { "IN1RN", NULL, "MICB2" },
index 8d3149a..9287a1d 100644 (file)
@@ -130,7 +130,7 @@ static const struct snd_kcontrol_new controls[] = {
        SOC_DAPM_PIN_SWITCH("DMIC"),
 };
 
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 
@@ -140,7 +140,7 @@ static struct snd_soc_dapm_widget widgets[] = {
        SND_SOC_DAPM_SPK("Main Speaker", NULL),
 };
 
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
        { "Headphone", NULL, "HPOUTL" },
        { "Headphone", NULL, "HPOUTR" },
 
@@ -189,10 +189,10 @@ static int tobermory_late_probe(struct snd_soc_card *card)
        if (ret < 0)
                return ret;
 
-       ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET |
-                                   SND_JACK_BTN_0, &tobermory_headset,
-                                   tobermory_headset_pins,
-                                   ARRAY_SIZE(tobermory_headset_pins));
+       ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET |
+                                        SND_JACK_BTN_0, &tobermory_headset,
+                                        tobermory_headset_pins,
+                                        ARRAY_SIZE(tobermory_headset_pins));
        if (ret)
                return ret;
 
index ae46f18..97916e3 100644 (file)
@@ -47,7 +47,7 @@ config SND_SOC_RCAR
 
 config SND_SOC_RZ
        tristate "RZ/G2L series SSIF-2 support"
-       depends on ARCH_R9A07G044 || COMPILE_TEST
+       depends on ARCH_RZG2L || COMPILE_TEST
        help
          This option enables RZ/G2L SSIF-2 sound support.
 
index 6a8fe0d..eb762ab 100644 (file)
@@ -755,7 +755,7 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 
        /* set clock master for audio interface */
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
        case SND_SOC_DAIFMT_CBP_CFP:
                rdai->clk_master = 0;
                break;
@@ -1159,6 +1159,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
                struct device_node *capture)
 {
        struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+       struct device *dev = rsnd_priv_to_dev(priv);
        struct device_node *np;
        int i;
 
@@ -1169,7 +1170,11 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
        for_each_child_of_node(node, np) {
                struct rsnd_mod *mod;
 
-               i = rsnd_node_fixed_index(np, name, i);
+               i = rsnd_node_fixed_index(dev, np, name, i);
+               if (i < 0) {
+                       of_node_put(np);
+                       break;
+               }
 
                mod = mod_get(priv, i);
 
@@ -1183,7 +1188,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
        of_node_put(node);
 }
 
-int rsnd_node_fixed_index(struct device_node *node, char *name, int idx)
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx)
 {
        char node_name[16];
 
@@ -1210,6 +1215,8 @@ int rsnd_node_fixed_index(struct device_node *node, char *name, int idx)
                        return idx;
        }
 
+       dev_err(dev, "strange node numbering (%s)",
+               of_node_full_name(node));
        return -EINVAL;
 }
 
@@ -1221,10 +1228,8 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name
 
        i = 0;
        for_each_child_of_node(node, np) {
-               i = rsnd_node_fixed_index(np, name, i);
+               i = rsnd_node_fixed_index(dev, np, name, i);
                if (i < 0) {
-                       dev_err(dev, "strange node numbering (%s)",
-                               of_node_full_name(node));
                        of_node_put(np);
                        return 0;
                }
index 03e0d4e..463ab23 100644 (file)
@@ -240,12 +240,19 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
                                          struct rsnd_mod *mod, char *x)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
        struct dma_chan *chan = NULL;
        struct device_node *np;
        int i = 0;
 
        for_each_child_of_node(of_node, np) {
-               i = rsnd_node_fixed_index(np, name, i);
+               i = rsnd_node_fixed_index(dev, np, name, i);
+               if (i < 0) {
+                       chan = NULL;
+                       of_node_put(np);
+                       break;
+               }
 
                if (i == rsnd_mod_id_raw(mod) && (!chan))
                        chan = of_dma_request_slave_channel(np, x);
index 6580bab..d9cd190 100644 (file)
@@ -460,7 +460,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
                struct device_node *playback,
                struct device_node *capture);
 int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name);
-int rsnd_node_fixed_index(struct device_node *node, char *name, int idx);
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx);
 
 int rsnd_channel_normalization(int chan);
 #define rsnd_runtime_channel_original(io) \
index 42a100c..0ea84ae 100644 (file)
@@ -676,7 +676,12 @@ int rsnd_src_probe(struct rsnd_priv *priv)
                if (!of_device_is_available(np))
                        goto skip;
 
-               i = rsnd_node_fixed_index(np, SRC_NAME, i);
+               i = rsnd_node_fixed_index(dev, np, SRC_NAME, i);
+               if (i < 0) {
+                       ret = -EINVAL;
+                       of_node_put(np);
+                       goto rsnd_src_probe_done;
+               }
 
                src = rsnd_src_get(priv, i);
 
index 87e606f..43c5e27 100644 (file)
@@ -1105,6 +1105,7 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
                            struct device_node *capture)
 {
        struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+       struct device *dev = rsnd_priv_to_dev(priv);
        struct device_node *node;
        struct device_node *np;
        int i;
@@ -1117,7 +1118,11 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
        for_each_child_of_node(node, np) {
                struct rsnd_mod *mod;
 
-               i = rsnd_node_fixed_index(np, SSI_NAME, i);
+               i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+               if (i < 0) {
+                       of_node_put(np);
+                       break;
+               }
 
                mod = rsnd_ssi_mod_get(priv, i);
 
@@ -1182,7 +1187,12 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
                if (!of_device_is_available(np))
                        goto skip;
 
-               i = rsnd_node_fixed_index(np, SSI_NAME, i);
+               i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+               if (i < 0) {
+                       ret = -EINVAL;
+                       of_node_put(np);
+                       goto rsnd_ssi_probe_done;
+               }
 
                ssi = rsnd_ssi_get(priv, i);
 
index 0d8f976..4b8a63e 100644 (file)
@@ -102,6 +102,8 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
                shift  = 1;
                offset = 1;
                break;
+       default:
+               goto out;
        }
 
        for (i = 0; i < 4; i++) {
@@ -120,7 +122,7 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
                }
                rsnd_mod_write(mod, reg, val);
        }
-
+out:
        return error;
 }
 
@@ -460,6 +462,7 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
                             struct device_node *capture)
 {
        struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+       struct device *dev = rsnd_priv_to_dev(priv);
        struct device_node *node = rsnd_ssiu_of_node(priv);
        struct rsnd_dai_stream *io_p = &rdai->playback;
        struct rsnd_dai_stream *io_c = &rdai->capture;
@@ -472,7 +475,11 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
                for_each_child_of_node(node, np) {
                        struct rsnd_mod *mod;
 
-                       i = rsnd_node_fixed_index(np, SSIU_NAME, i);
+                       i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
+                       if (i < 0) {
+                               of_node_put(np);
+                               break;
+                       }
 
                        mod = rsnd_ssiu_mod_get(priv, i);
 
index e8edaed..e392de7 100644 (file)
@@ -59,9 +59,7 @@
 #define SSIFSR_RDC_MASK                0x3f
 #define SSIFSR_RDC_SHIFT       8
 
-#define SSIFSR_TDC(x)          (((x) & 0x1f) << 24)
 #define SSIFSR_TDE             BIT(16)
-#define SSIFSR_RDC(x)          (((x) & 0x1f) << 8)
 #define SSIFSR_RDF             BIT(0)
 
 #define SSIOFR_LRCONT          BIT(8)
@@ -978,22 +976,24 @@ static int rz_ssi_probe(struct platform_device *pdev)
 
        /* Error Interrupt */
        ssi->irq_int = platform_get_irq_byname(pdev, "int_req");
-       if (ssi->irq_int < 0)
-               return dev_err_probe(&pdev->dev, -ENODEV,
-                                    "Unable to get SSI int_req IRQ\n");
+       if (ssi->irq_int < 0) {
+               rz_ssi_release_dma_channels(ssi);
+               return ssi->irq_int;
+       }
 
        ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt,
                               0, dev_name(&pdev->dev), ssi);
-       if (ret < 0)
+       if (ret < 0) {
+               rz_ssi_release_dma_channels(ssi);
                return dev_err_probe(&pdev->dev, ret,
                                     "irq request error (int_req)\n");
+       }
 
        if (!rz_ssi_is_dma_enabled(ssi)) {
                /* Tx and Rx interrupts (pio only) */
                ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx");
                if (ssi->irq_tx < 0)
-                       return dev_err_probe(&pdev->dev, -ENODEV,
-                                            "Unable to get SSI dma_tx IRQ\n");
+                       return ssi->irq_tx;
 
                ret = devm_request_irq(&pdev->dev, ssi->irq_tx,
                                       &rz_ssi_interrupt, 0,
@@ -1004,8 +1004,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
 
                ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx");
                if (ssi->irq_rx < 0)
-                       return dev_err_probe(&pdev->dev, -ENODEV,
-                                            "Unable to get SSI dma_rx IRQ\n");
+                       return ssi->irq_rx;
 
                ret = devm_request_irq(&pdev->dev, ssi->irq_rx,
                                       &rz_ssi_interrupt, 0,
@@ -1016,13 +1015,16 @@ static int rz_ssi_probe(struct platform_device *pdev)
        }
 
        ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
-       if (IS_ERR(ssi->rstc))
+       if (IS_ERR(ssi->rstc)) {
+               rz_ssi_release_dma_channels(ssi);
                return PTR_ERR(ssi->rstc);
+       }
 
        reset_control_deassert(ssi->rstc);
        pm_runtime_enable(&pdev->dev);
        ret = pm_runtime_resume_and_get(&pdev->dev);
        if (ret < 0) {
+               rz_ssi_release_dma_channels(ssi);
                pm_runtime_disable(ssi->dev);
                reset_control_assert(ssi->rstc);
                return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n");
index 41c586b..4158f5a 100644 (file)
@@ -42,8 +42,42 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
 
+static int jack_new(struct snd_soc_card *card, const char *id, int type,
+                   struct snd_soc_jack *jack, bool initial_kctl)
+{
+       mutex_init(&jack->mutex);
+       jack->card = card;
+       INIT_LIST_HEAD(&jack->pins);
+       INIT_LIST_HEAD(&jack->jack_zones);
+       BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
+
+       return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false);
+}
+
 /**
- * snd_soc_card_jack_new - Create a new jack
+ * snd_soc_card_jack_new - Create a new jack without pins
+ * @card:  ASoC card
+ * @id:    an identifying string for this jack
+ * @type:  a bitmask of enum snd_jack_type values that can be detected by
+ *         this jack
+ * @jack:  structure to use for the jack
+ *
+ * Creates a new jack object without pins. If adding pins later,
+ * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins
+ * argument.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
+                         struct snd_soc_jack *jack)
+{
+       return soc_card_ret(card, jack_new(card, id, type, jack, true));
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
+
+/**
+ * snd_soc_card_jack_new_pins - Create a new jack with pins
  * @card:  ASoC card
  * @id:    an identifying string for this jack
  * @type:  a bitmask of enum snd_jack_type values that can be detected by
@@ -52,24 +86,20 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
  * @pins:  Array of jack pins to be added to the jack or NULL
  * @num_pins: Number of elements in the @pins array
  *
- * Creates a new jack object.
+ * Creates a new jack object with pins. If not adding pins,
+ * snd_soc_card_jack_new() should be used instead.
  *
  * Returns zero if successful, or a negative error code on failure.
  * On success jack will be initialised.
  */
-int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
-                         struct snd_soc_jack *jack,
-                         struct snd_soc_jack_pin *pins, unsigned int num_pins)
+int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
+                              int type, struct snd_soc_jack *jack,
+                              struct snd_soc_jack_pin *pins,
+                              unsigned int num_pins)
 {
        int ret;
 
-       mutex_init(&jack->mutex);
-       jack->card = card;
-       INIT_LIST_HEAD(&jack->pins);
-       INIT_LIST_HEAD(&jack->jack_zones);
-       BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
-
-       ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false);
+       ret = jack_new(card, id, type, jack, false);
        if (ret)
                goto end;
 
@@ -78,7 +108,7 @@ int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
 end:
        return soc_card_ret(card, ret);
 }
-EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins);
 
 int snd_soc_card_suspend_pre(struct snd_soc_card *card)
 {
index c0664f9..e12f824 100644 (file)
@@ -932,6 +932,20 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static bool snd_soc_component_is_codec_on_rtd(struct snd_soc_pcm_runtime *rtd,
+                                             struct snd_soc_component *component)
+{
+       struct snd_soc_dai *dai;
+       int i;
+
+       for_each_rtd_codec_dais(rtd, i, dai) {
+               if (dai->component == component)
+                       return true;
+       }
+
+       return false;
+}
+
 void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
                                 snd_pcm_sframes_t *cpu_delay,
                                 snd_pcm_sframes_t *codec_delay)
@@ -953,7 +967,7 @@ void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
 
                delay = component->driver->delay(component, substream);
 
-               if (snd_soc_component_is_codec(component))
+               if (snd_soc_component_is_codec_on_rtd(rtd, component))
                        *codec_delay = max(*codec_delay, delay);
                else
                        *cpu_delay = max(*cpu_delay, delay);
index 8c7da82..9574f86 100644 (file)
@@ -1230,7 +1230,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
        /*
         * Flip the polarity for the "CPU" end of a CODEC<->CODEC link
         */
-       inv_dai_fmt = snd_soc_daifmt_clock_provider_fliped(dai_fmt);
+       inv_dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt);
 
        for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
                unsigned int fmt = dai_fmt;
@@ -2497,7 +2497,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
 
        for (i = 0; i < count; i++) {
                dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
-                                          !snd_soc_component_is_codec(component));
+                                          !component->driver->non_legacy_dai_naming);
                if (dai == NULL) {
                        ret = -ENOMEM;
                        goto err;
@@ -2763,6 +2763,11 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
                        "ASoC: Property '%s' does not exist\n", propname);
                return -EINVAL;
        }
+       if (!num_widgets) {
+               dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
+                       propname);
+               return -EINVAL;
+       }
        if (num_widgets & 1) {
                dev_err(card->dev,
                        "ASoC: Property '%s' length is not even\n", propname);
@@ -2770,11 +2775,6 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
        }
 
        num_widgets /= 2;
-       if (!num_widgets) {
-               dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
-                       propname);
-               return -EINVAL;
-       }
 
        widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
                               GFP_KERNEL);
@@ -3035,7 +3035,7 @@ int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname)
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs);
 
-unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt)
+unsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt)
 {
        unsigned int inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
 
@@ -3056,7 +3056,7 @@ unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt)
 
        return inv_dai_fmt;
 }
-EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_fliped);
+EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_flipped);
 
 unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame)
 {
@@ -3395,6 +3395,86 @@ err:
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
 
+/*
+ * snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array
+ * @dai_link: DAI link
+ *
+ * Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus().
+ */
+void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link)
+{
+       struct snd_soc_dai_link_component *component;
+       int index;
+
+       for_each_link_cpus(dai_link, index, component) {
+               if (!component->of_node)
+                       break;
+               of_node_put(component->of_node);
+               component->of_node = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_cpus);
+
+/*
+ * snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs
+ * instead.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_cpus(struct device *dev,
+                                struct device_node *of_node,
+                                struct snd_soc_dai_link *dai_link)
+{
+       struct of_phandle_args args;
+       struct snd_soc_dai_link_component *component;
+       char *name;
+       int index, num_codecs, ret;
+
+       /* Count the number of CODECs */
+       name = "sound-dai";
+       num_codecs = of_count_phandle_with_args(of_node, name,
+                                               "#sound-dai-cells");
+       if (num_codecs <= 0) {
+               if (num_codecs == -ENOENT)
+                       dev_err(dev, "No 'sound-dai' property\n");
+               else
+                       dev_err(dev, "Bad phandle in 'sound-dai'\n");
+               return num_codecs;
+       }
+       component = devm_kcalloc(dev,
+                                num_codecs, sizeof(*component),
+                                GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+       dai_link->cpus = component;
+       dai_link->num_cpus = num_codecs;
+
+       /* Parse the list */
+       for_each_link_cpus(dai_link, index, component) {
+               ret = of_parse_phandle_with_args(of_node, name,
+                                                "#sound-dai-cells",
+                                                index, &args);
+               if (ret)
+                       goto err;
+               component->of_node = args.np;
+               ret = snd_soc_get_dai_name(&args, &component->dai_name);
+               if (ret < 0)
+                       goto err;
+       }
+       return 0;
+err:
+       snd_soc_of_put_dai_link_codecs(dai_link);
+       dai_link->cpus = NULL;
+       dai_link->num_cpus = 0;
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus);
+
 static int __init snd_soc_init(void)
 {
        snd_soc_debugfs_init();
index ca917a8..869c765 100644 (file)
@@ -3437,7 +3437,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                        update.val = val;
                        card->update = &update;
                }
-               change |= reg_change;
 
                ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
                                                  rconnect);
@@ -3539,7 +3538,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                        update.val = val;
                        card->update = &update;
                }
-               change |= reg_change;
 
                ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
 
index 285441d..8785846 100644 (file)
@@ -79,29 +79,19 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component,
 {
        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
-       int (*prepare_slave_config)(struct snd_pcm_substream *substream,
-                       struct snd_pcm_hw_params *params,
-                       struct dma_slave_config *slave_config);
        struct dma_slave_config slave_config;
+       int ret;
 
-       memset(&slave_config, 0, sizeof(slave_config));
-
-       if (!pcm->config)
-               prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
-       else
-               prepare_slave_config = pcm->config->prepare_slave_config;
+       if (!pcm->config->prepare_slave_config)
+               return 0;
 
-       if (prepare_slave_config) {
-               int ret = prepare_slave_config(substream, params, &slave_config);
-               if (ret)
-                       return ret;
+       memset(&slave_config, 0, sizeof(slave_config));
 
-               ret = dmaengine_slave_config(chan, &slave_config);
-               if (ret)
-                       return ret;
-       }
+       ret = pcm->config->prepare_slave_config(substream, params, &slave_config);
+       if (ret)
+               return ret;
 
-       return 0;
+       return dmaengine_slave_config(chan, &slave_config);
 }
 
 static int
@@ -121,7 +111,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
                return -EINVAL;
        }
 
-       if (pcm->config && pcm->config->pcm_hardware)
+       if (pcm->config->pcm_hardware)
                return snd_soc_set_runtime_hwparams(substream,
                                pcm->config->pcm_hardware);
 
@@ -188,7 +178,6 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
 {
        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
        struct snd_dmaengine_dai_dma_data *dma_data;
-       dma_filter_fn fn = NULL;
 
        if (rtd->num_cpus > 1) {
                dev_err(rtd->dev,
@@ -201,13 +190,11 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
                return pcm->chan[0];
 
-       if (pcm->config && pcm->config->compat_request_channel)
+       if (pcm->config->compat_request_channel)
                return pcm->config->compat_request_channel(rtd, substream);
 
-       if (pcm->config)
-               fn = pcm->config->compat_filter_fn;
-
-       return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
+       return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn,
+                                                dma_data->filter_data);
 }
 
 static bool dmaengine_pcm_can_report_residue(struct device *dev,
@@ -239,12 +226,12 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
        size_t max_buffer_size;
        unsigned int i;
 
-       if (config && config->prealloc_buffer_size)
+       if (config->prealloc_buffer_size)
                prealloc_buffer_size = config->prealloc_buffer_size;
        else
                prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
 
-       if (config && config->pcm_hardware && config->pcm_hardware->buffer_bytes_max)
+       if (config->pcm_hardware && config->pcm_hardware->buffer_bytes_max)
                max_buffer_size = config->pcm_hardware->buffer_bytes_max;
        else
                max_buffer_size = SIZE_MAX;
@@ -254,7 +241,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
                if (!substream)
                        continue;
 
-               if (!pcm->chan[i] && config && config->chan_names[i])
+               if (!pcm->chan[i] && config->chan_names[i])
                        pcm->chan[i] = dma_request_slave_channel(dev,
                                config->chan_names[i]);
 
@@ -367,10 +354,10 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
        struct dma_chan *chan;
 
        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node &&
-           !(config && config->dma_dev && config->dma_dev->of_node)))
+           !(config->dma_dev && config->dma_dev->of_node)))
                return 0;
 
-       if (config && config->dma_dev) {
+       if (config->dma_dev) {
                /*
                 * If this warning is seen, it probably means that your Linux
                 * device structure does not match your HW device structure.
@@ -387,7 +374,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
                        name = "rx-tx";
                else
                        name = dmaengine_pcm_dma_channel_names[i];
-               if (config && config->chan_names[i])
+               if (config->chan_names[i])
                        name = config->chan_names[i];
                chan = dma_request_chan(dev, name);
                if (IS_ERR(chan)) {
@@ -425,6 +412,10 @@ static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
        }
 }
 
+static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = {
+       .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
 /**
  * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
  * @dev: The parent device for the PCM device
@@ -445,6 +436,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
 #ifdef CONFIG_DEBUG_FS
        pcm->component.debugfs_prefix = "dma";
 #endif
+       if (!config)
+               config = &snd_dmaengine_pcm_default_config;
        pcm->config = config;
        pcm->flags = flags;
 
@@ -452,7 +445,7 @@ int snd_dmaengine_pcm_register(struct device *dev,
        if (ret)
                goto err_free_dma;
 
-       if (config && config->process)
+       if (config->process)
                driver = &dmaengine_pcm_component_process;
        else
                driver = &dmaengine_pcm_component;
index d798765..fcece5c 100644 (file)
@@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_get_type);
 /**
  * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
  *
- * @jack:  ASoC jack
+ * @jack:  ASoC jack created with snd_soc_card_jack_new_pins()
  * @count: Number of pins
  * @pins:  Array of pins
  *
index 11c9853..6f43db3 100644 (file)
@@ -2090,6 +2090,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                               int cmd)
 {
        struct snd_soc_pcm_runtime *be;
+       bool pause_stop_transition;
        struct snd_soc_dpcm *dpcm;
        unsigned long flags;
        int ret = 0;
@@ -2121,6 +2122,13 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (be->dpcm[stream].be_start != 1)
                                goto next;
 
+                       if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED)
+                               ret = soc_pcm_trigger(be_substream,
+                                                     SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+                       else
+                               ret = soc_pcm_trigger(be_substream,
+                                                     SNDRV_PCM_TRIGGER_START);
+
                        ret = soc_pcm_trigger(be_substream, cmd);
                        if (ret) {
                                be->dpcm[stream].be_start--;
@@ -2148,10 +2156,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                        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;
 
+                       fe->dpcm[stream].fe_pause = false;
+                       be->dpcm[stream].be_pause--;
+
                        be->dpcm[stream].be_start++;
                        if (be->dpcm[stream].be_start != 1)
                                goto next;
@@ -2175,14 +2185,33 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (be->dpcm[stream].be_start != 0)
                                goto next;
 
-                       ret = soc_pcm_trigger(be_substream, cmd);
+                       pause_stop_transition = false;
+                       if (fe->dpcm[stream].fe_pause) {
+                               pause_stop_transition = true;
+                               fe->dpcm[stream].fe_pause = false;
+                               be->dpcm[stream].be_pause--;
+                       }
+
+                       if (be->dpcm[stream].be_pause != 0)
+                               ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+                       else
+                               ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_STOP);
+
                        if (ret) {
                                if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
                                        be->dpcm[stream].be_start++;
+                               if (pause_stop_transition) {
+                                       fe->dpcm[stream].fe_pause = true;
+                                       be->dpcm[stream].be_pause++;
+                               }
                                goto next;
                        }
 
-                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+                       if (be->dpcm[stream].be_pause != 0)
+                               be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+                       else
+                               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)
@@ -2204,6 +2233,9 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
                                goto next;
 
+                       fe->dpcm[stream].fe_pause = true;
+                       be->dpcm[stream].be_pause++;
+
                        be->dpcm[stream].be_start--;
                        if (be->dpcm[stream].be_start != 0)
                                goto next;
index 3bb90a8..3f9d314 100644 (file)
@@ -40,7 +40,7 @@
  */
 #define SOC_TPLG_PASS_MANIFEST         0
 #define SOC_TPLG_PASS_VENDOR           1
-#define SOC_TPLG_PASS_MIXER            2
+#define SOC_TPLG_PASS_CONTROL          2
 #define SOC_TPLG_PASS_WIDGET           3
 #define SOC_TPLG_PASS_PCM_DAI          4
 #define SOC_TPLG_PASS_GRAPH            5
@@ -104,13 +104,13 @@ static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
        return 0;
 }
 
-static inline int soc_tplg_is_eof(struct soc_tplg *tplg)
+static inline bool soc_tplg_is_eof(struct soc_tplg *tplg)
 {
        const u8 *end = tplg->hdr_pos;
 
        if (end >= tplg->fw->data + tplg->fw->size)
-               return 1;
-       return 0;
+               return true;
+       return false;
 }
 
 static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg)
@@ -237,7 +237,7 @@ static inline void soc_control_err(struct soc_tplg *tplg,
        struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
 {
        dev_err(tplg->dev,
-               "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
+               "ASoC: no complete control IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
                name, hdr->ops.get, hdr->ops.put, hdr->ops.info,
                soc_tplg_get_offset(tplg));
 }
@@ -360,7 +360,7 @@ static void remove_mixer(struct snd_soc_component *comp,
 {
        struct snd_card *card = comp->card->snd_card;
 
-       if (pass != SOC_TPLG_PASS_MIXER)
+       if (pass != SOC_TPLG_PASS_CONTROL)
                return;
 
        if (dobj->ops && dobj->ops->control_unload)
@@ -376,7 +376,7 @@ static void remove_enum(struct snd_soc_component *comp,
 {
        struct snd_card *card = comp->card->snd_card;
 
-       if (pass != SOC_TPLG_PASS_MIXER)
+       if (pass != SOC_TPLG_PASS_CONTROL)
                return;
 
        if (dobj->ops && dobj->ops->control_unload)
@@ -392,7 +392,7 @@ static void remove_bytes(struct snd_soc_component *comp,
 {
        struct snd_card *card = comp->card->snd_card;
 
-       if (pass != SOC_TPLG_PASS_MIXER)
+       if (pass != SOC_TPLG_PASS_CONTROL)
                return;
 
        if (dobj->ops && dobj->ops->control_unload)
@@ -618,7 +618,7 @@ int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w,
 EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
 
 /* optionally pass new dynamic kcontrol to component driver. */
-static int soc_tplg_init_kcontrol(struct soc_tplg *tplg,
+static int soc_tplg_control_load(struct soc_tplg *tplg,
        struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
 {
        if (tplg->ops && tplg->ops->control_load)
@@ -676,175 +676,156 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg,
        return 0;
 }
 
-static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
-       size_t size)
+static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
 {
        struct snd_soc_tplg_bytes_control *be;
        struct soc_bytes_ext *sbe;
        struct snd_kcontrol_new kc;
-       int i;
-       int err = 0;
+       int ret = 0;
 
        if (soc_tplg_check_elem_count(tplg,
                                      sizeof(struct snd_soc_tplg_bytes_control),
-                                     count, size, "mixer bytes"))
+                                     1, size, "mixer bytes"))
                return -EINVAL;
 
-       for (i = 0; i < count; i++) {
-               be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+       be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
 
-               /* validate kcontrol */
-               if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
-                       SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
-                       return -EINVAL;
+       /* validate kcontrol */
+       if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+               SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               return -EINVAL;
 
-               sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
-               if (sbe == NULL)
-                       return -ENOMEM;
+       sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
+       if (sbe == NULL)
+               return -ENOMEM;
 
-               tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
-                             le32_to_cpu(be->priv.size));
+       tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+                     le32_to_cpu(be->priv.size));
 
-               dev_dbg(tplg->dev,
-                       "ASoC: adding bytes kcontrol %s with access 0x%x\n",
-                       be->hdr.name, be->hdr.access);
-
-               memset(&kc, 0, sizeof(kc));
-               kc.name = be->hdr.name;
-               kc.private_value = (long)sbe;
-               kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               kc.access = le32_to_cpu(be->hdr.access);
-
-               sbe->max = le32_to_cpu(be->max);
-               sbe->dobj.type = SND_SOC_DOBJ_BYTES;
-               sbe->dobj.ops = tplg->ops;
-               INIT_LIST_HEAD(&sbe->dobj.list);
-
-               /* map io handlers */
-               err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg);
-               if (err) {
-                       soc_control_err(tplg, &be->hdr, be->hdr.name);
-                       break;
-               }
+       dev_dbg(tplg->dev,
+               "ASoC: adding bytes kcontrol %s with access 0x%x\n",
+               be->hdr.name, be->hdr.access);
 
-               /* pass control to driver for optional further init */
-               err = soc_tplg_init_kcontrol(tplg, &kc,
-                       (struct snd_soc_tplg_ctl_hdr *)be);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to init %s\n",
-                               be->hdr.name);
-                       break;
-               }
+       memset(&kc, 0, sizeof(kc));
+       kc.name = be->hdr.name;
+       kc.private_value = (long)sbe;
+       kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kc.access = le32_to_cpu(be->hdr.access);
 
-               /* register control here */
-               err = soc_tplg_add_kcontrol(tplg, &kc,
-                       &sbe->dobj.control.kcontrol);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to add %s\n",
-                               be->hdr.name);
-                       break;
-               }
+       sbe->max = le32_to_cpu(be->max);
+       sbe->dobj.type = SND_SOC_DOBJ_BYTES;
+       sbe->dobj.ops = tplg->ops;
+       INIT_LIST_HEAD(&sbe->dobj.list);
 
-               list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+       /* map io handlers */
+       ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg);
+       if (ret) {
+               soc_control_err(tplg, &be->hdr, be->hdr.name);
+               goto err;
        }
-       return err;
 
+       /* pass control to driver for optional further init */
+       ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name);
+               goto err;
+       }
+
+       /* register control here */
+       ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to add %s\n", be->hdr.name);
+               goto err;
+       }
+
+       list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+
+err:
+       return ret;
 }
 
-static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
-       size_t size)
+static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
 {
        struct snd_soc_tplg_mixer_control *mc;
        struct soc_mixer_control *sm;
        struct snd_kcontrol_new kc;
-       int i;
-       int err = 0;
+       int ret = 0;
 
        if (soc_tplg_check_elem_count(tplg,
                                      sizeof(struct snd_soc_tplg_mixer_control),
-                                     count, size, "mixers"))
+                                     1, size, "mixers"))
                return -EINVAL;
 
-       for (i = 0; i < count; i++) {
-               mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+       mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
 
-               /* validate kcontrol */
-               if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
-                       SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
-                       return -EINVAL;
+       /* validate kcontrol */
+       if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+               SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               return -EINVAL;
 
-               sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
-               if (sm == NULL)
-                       return -ENOMEM;
-               tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
-                             le32_to_cpu(mc->priv.size));
+       sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
+       if (sm == NULL)
+               return -ENOMEM;
+       tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+                     le32_to_cpu(mc->priv.size));
 
-               dev_dbg(tplg->dev,
-                       "ASoC: adding mixer kcontrol %s with access 0x%x\n",
-                       mc->hdr.name, mc->hdr.access);
-
-               memset(&kc, 0, sizeof(kc));
-               kc.name = mc->hdr.name;
-               kc.private_value = (long)sm;
-               kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               kc.access = le32_to_cpu(mc->hdr.access);
-
-               /* we only support FL/FR channel mapping atm */
-               sm->reg = tplc_chan_get_reg(tplg, mc->channel,
-                       SNDRV_CHMAP_FL);
-               sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
-                       SNDRV_CHMAP_FR);
-               sm->shift = tplc_chan_get_shift(tplg, mc->channel,
-                       SNDRV_CHMAP_FL);
-               sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
-                       SNDRV_CHMAP_FR);
-
-               sm->max = le32_to_cpu(mc->max);
-               sm->min = le32_to_cpu(mc->min);
-               sm->invert = le32_to_cpu(mc->invert);
-               sm->platform_max = le32_to_cpu(mc->platform_max);
-               sm->dobj.index = tplg->index;
-               sm->dobj.ops = tplg->ops;
-               sm->dobj.type = SND_SOC_DOBJ_MIXER;
-               INIT_LIST_HEAD(&sm->dobj.list);
-
-               /* map io handlers */
-               err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg);
-               if (err) {
-                       soc_control_err(tplg, &mc->hdr, mc->hdr.name);
-                       break;
-               }
+       dev_dbg(tplg->dev,
+               "ASoC: adding mixer kcontrol %s with access 0x%x\n",
+               mc->hdr.name, mc->hdr.access);
 
-               /* create any TLV data */
-               err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
-                               mc->hdr.name);
-                       break;
-               }
+       memset(&kc, 0, sizeof(kc));
+       kc.name = mc->hdr.name;
+       kc.private_value = (long)sm;
+       kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kc.access = le32_to_cpu(mc->hdr.access);
 
-               /* pass control to driver for optional further init */
-               err = soc_tplg_init_kcontrol(tplg, &kc,
-                       (struct snd_soc_tplg_ctl_hdr *) mc);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to init %s\n",
-                               mc->hdr.name);
-                       break;
-               }
+       /* we only support FL/FR channel mapping atm */
+       sm->reg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL);
+       sm->rreg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
+       sm->shift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
+       sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
 
-               /* register control here */
-               err = soc_tplg_add_kcontrol(tplg, &kc,
-                       &sm->dobj.control.kcontrol);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to add %s\n",
-                               mc->hdr.name);
-                       break;
-               }
+       sm->max = le32_to_cpu(mc->max);
+       sm->min = le32_to_cpu(mc->min);
+       sm->invert = le32_to_cpu(mc->invert);
+       sm->platform_max = le32_to_cpu(mc->platform_max);
+       sm->dobj.index = tplg->index;
+       sm->dobj.ops = tplg->ops;
+       sm->dobj.type = SND_SOC_DOBJ_MIXER;
+       INIT_LIST_HEAD(&sm->dobj.list);
+
+       /* map io handlers */
+       ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg);
+       if (ret) {
+               soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+               goto err;
+       }
 
-               list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+       /* create any TLV data */
+       ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name);
+               goto err;
        }
 
-       return err;
+       /* pass control to driver for optional further init */
+       ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name);
+               goto err;
+       }
+
+       /* register control here */
+       ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to add %s\n", mc->hdr.name);
+               goto err;
+       }
+
+       list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+
+err:
+       return ret;
 }
 
 static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se,
@@ -911,117 +892,108 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *
        return 0;
 }
 
-static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
-       size_t size)
+static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
 {
        struct snd_soc_tplg_enum_control *ec;
        struct soc_enum *se;
        struct snd_kcontrol_new kc;
-       int i;
-       int err = 0;
+       int ret = 0;
 
        if (soc_tplg_check_elem_count(tplg,
                                      sizeof(struct snd_soc_tplg_enum_control),
-                                     count, size, "enums"))
+                                     1, size, "enums"))
                return -EINVAL;
 
-       for (i = 0; i < count; i++) {
-               ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+       ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
 
-               /* validate kcontrol */
-               if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
-                       SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
-                       return -EINVAL;
+       /* validate kcontrol */
+       if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+               SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               return -EINVAL;
 
-               se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL);
-               if (se == NULL)
-                       return -ENOMEM;
+       se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL);
+       if (se == NULL)
+               return -ENOMEM;
 
-               tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
-                             le32_to_cpu(ec->priv.size));
+       tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+                     le32_to_cpu(ec->priv.size));
 
-               dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
-                       ec->hdr.name, ec->items);
+       dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
+               ec->hdr.name, ec->items);
 
-               memset(&kc, 0, sizeof(kc));
-               kc.name = ec->hdr.name;
-               kc.private_value = (long)se;
-               kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               kc.access = le32_to_cpu(ec->hdr.access);
+       memset(&kc, 0, sizeof(kc));
+       kc.name = ec->hdr.name;
+       kc.private_value = (long)se;
+       kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kc.access = le32_to_cpu(ec->hdr.access);
 
-               se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
-               se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
-                       SNDRV_CHMAP_FL);
-               se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
-                       SNDRV_CHMAP_FL);
+       se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+       se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+               SNDRV_CHMAP_FL);
+       se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+               SNDRV_CHMAP_FL);
 
-               se->mask = le32_to_cpu(ec->mask);
-               se->dobj.index = tplg->index;
-               se->dobj.type = SND_SOC_DOBJ_ENUM;
-               se->dobj.ops = tplg->ops;
-               INIT_LIST_HEAD(&se->dobj.list);
+       se->mask = le32_to_cpu(ec->mask);
+       se->dobj.index = tplg->index;
+       se->dobj.type = SND_SOC_DOBJ_ENUM;
+       se->dobj.ops = tplg->ops;
+       INIT_LIST_HEAD(&se->dobj.list);
 
-               switch (le32_to_cpu(ec->hdr.ops.info)) {
-               case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
-               case SND_SOC_TPLG_CTL_ENUM_VALUE:
-                       err = soc_tplg_denum_create_values(tplg, se, ec);
-                       if (err < 0) {
-                               dev_err(tplg->dev,
-                                       "ASoC: could not create values for %s\n",
-                                       ec->hdr.name);
-                               goto err_denum;
-                       }
-                       fallthrough;
-               case SND_SOC_TPLG_CTL_ENUM:
-               case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
-               case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
-                       err = soc_tplg_denum_create_texts(tplg, se, ec);
-                       if (err < 0) {
-                               dev_err(tplg->dev,
-                                       "ASoC: could not create texts for %s\n",
-                                       ec->hdr.name);
-                               goto err_denum;
-                       }
-                       break;
-               default:
-                       err = -EINVAL;
+       switch (le32_to_cpu(ec->hdr.ops.info)) {
+       case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+       case SND_SOC_TPLG_CTL_ENUM_VALUE:
+               ret = soc_tplg_denum_create_values(tplg, se, ec);
+               if (ret < 0) {
                        dev_err(tplg->dev,
-                               "ASoC: invalid enum control type %d for %s\n",
-                               ec->hdr.ops.info, ec->hdr.name);
-                       goto err_denum;
-               }
-
-               /* map io handlers */
-               err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
-               if (err) {
-                       soc_control_err(tplg, &ec->hdr, ec->hdr.name);
-                       goto err_denum;
-               }
-
-               /* pass control to driver for optional further init */
-               err = soc_tplg_init_kcontrol(tplg, &kc,
-                       (struct snd_soc_tplg_ctl_hdr *) ec);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: failed to init %s\n",
+                               "ASoC: could not create values for %s\n",
                                ec->hdr.name);
-                       goto err_denum;
+                       goto err;
                }
-
-               /* register control here */
-               err = soc_tplg_add_kcontrol(tplg,
-                                           &kc, &se->dobj.control.kcontrol);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
+               fallthrough;
+       case SND_SOC_TPLG_CTL_ENUM:
+       case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+       case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+               ret = soc_tplg_denum_create_texts(tplg, se, ec);
+               if (ret < 0) {
+                       dev_err(tplg->dev,
+                               "ASoC: could not create texts for %s\n",
                                ec->hdr.name);
-                       goto err_denum;
+                       goto err;
                }
+               break;
+       default:
+               ret = -EINVAL;
+               dev_err(tplg->dev,
+                       "ASoC: invalid enum control type %d for %s\n",
+                       ec->hdr.ops.info, ec->hdr.name);
+               goto err;
+       }
 
-               list_add(&se->dobj.list, &tplg->comp->dobj_list);
+       /* map io handlers */
+       ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
+       if (ret) {
+               soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+               goto err;
        }
-       return 0;
 
-err_denum:
-       return err;
+       /* pass control to driver for optional further init */
+       ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name);
+               goto err;
+       }
+
+       /* register control here */
+       ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", ec->hdr.name);
+               goto err;
+       }
+
+       list_add(&se->dobj.list, &tplg->comp->dobj_list);
+
+err:
+       return ret;
 }
 
 static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
@@ -1049,20 +1021,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
                case SND_SOC_TPLG_CTL_RANGE:
                case SND_SOC_TPLG_DAPM_CTL_VOLSW:
                case SND_SOC_TPLG_DAPM_CTL_PIN:
-                       ret = soc_tplg_dmixer_create(tplg, 1,
-                                       le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size));
                        break;
                case SND_SOC_TPLG_CTL_ENUM:
                case SND_SOC_TPLG_CTL_ENUM_VALUE:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
-                       ret = soc_tplg_denum_create(tplg, 1,
-                                       le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size));
                        break;
                case SND_SOC_TPLG_CTL_BYTES:
-                       ret = soc_tplg_dbytes_create(tplg, 1,
-                                       le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size));
                        break;
                default:
                        soc_bind_err(tplg, control_hdr, i);
@@ -1224,8 +1193,7 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_
        }
 
        /* pass control to driver for optional further init */
-       err = soc_tplg_init_kcontrol(tplg, kc,
-                                    (struct snd_soc_tplg_ctl_hdr *)mc);
+       err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc);
        if (err < 0) {
                dev_err(tplg->dev, "ASoC: failed to init %s\n",
                        mc->hdr.name);
@@ -1309,8 +1277,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k
        }
 
        /* pass control to driver for optional further init */
-       err = soc_tplg_init_kcontrol(tplg, kc,
-                                    (struct snd_soc_tplg_ctl_hdr *)ec);
+       err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec);
        if (err < 0) {
                dev_err(tplg->dev, "ASoC: failed to init %s\n",
                        ec->hdr.name);
@@ -1362,8 +1329,7 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_
        }
 
        /* pass control to driver for optional further init */
-       err = soc_tplg_init_kcontrol(tplg, kc,
-                                    (struct snd_soc_tplg_ctl_hdr *)be);
+       err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be);
        if (err < 0) {
                dev_err(tplg->dev, "ASoC: failed to init %s\n",
                        be->hdr.name);
@@ -2498,7 +2464,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
        case SND_SOC_TPLG_TYPE_MIXER:
        case SND_SOC_TPLG_TYPE_ENUM:
        case SND_SOC_TPLG_TYPE_BYTES:
-               hdr_pass = SOC_TPLG_PASS_MIXER;
+               hdr_pass = SOC_TPLG_PASS_CONTROL;
                elem_load = soc_tplg_kcontrol_elems_load;
                break;
        case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
@@ -2550,10 +2516,8 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
 {
        int ret;
 
-       tplg->pass = SOC_TPLG_PASS_START;
-
        /* process the header types from start to end */
-       while (tplg->pass <= SOC_TPLG_PASS_END) {
+       for (tplg->pass = SOC_TPLG_PASS_START; tplg->pass <= SOC_TPLG_PASS_END; tplg->pass++) {
                struct snd_soc_tplg_hdr *hdr;
 
                tplg->hdr_pos = tplg->fw->data;
@@ -2585,8 +2549,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
                        hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
                }
 
-               /* next data type pass */
-               tplg->pass++;
        }
 
        /* signal DAPM we are complete */
@@ -2653,10 +2615,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
 {
        struct snd_card *card = comp->card->snd_card;
        struct snd_soc_dobj *dobj, *next_dobj;
-       int pass = SOC_TPLG_PASS_END;
+       int pass;
 
        /* process the header types from end to start */
-       while (pass >= SOC_TPLG_PASS_START) {
+       for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) {
 
                /* remove mixer controls */
                down_write(&card->controls_rwsem);
@@ -2699,7 +2661,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
                        }
                }
                up_write(&card->controls_rwsem);
-               pass--;
        }
 
        /* let caller know if FW can be freed when no objects are left */
diff --git a/sound/soc/soc-utils-test.c b/sound/soc/soc-utils-test.c
new file mode 100644 (file)
index 0000000..5ad8e23
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022 Cirrus Logic, Inc. and
+//                    Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/test.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <uapi/sound/asound.h>
+
+static const struct {
+       u32 rate;
+       snd_pcm_format_t fmt;
+       u8 channels;
+       u8 tdm_width;
+       u8 tdm_slots;
+       u8 slot_multiple;
+       u32 bclk;
+} tdm_params_to_bclk_cases[] = {
+       /* rate         fmt        channels tdm_width tdm_slots slot_multiple bclk */
+
+       /* From params only */
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      0,        128000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      0,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      0,        192000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      0,        384000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      0,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      0,        512000 },
+       {  44100,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      0,        705600 },
+       {  44100,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      0,       1411200 },
+       {  44100,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      0,       1058400 },
+       {  44100,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      0,       2116800 },
+       {  44100,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      0,       1411200 },
+       {  44100,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      0,       2822400 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      0,       6144000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      0,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      0,       9216000 },
+       { 384000,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      0,      18432000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      0,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      0,      24576000 },
+
+       /* I2S from params */
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      2,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      2,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      2,        384000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      2,        384000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      2,        512000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      2,        512000 },
+       {  44100,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      2,       1411200 },
+       {  44100,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      2,       1411200 },
+       {  44100,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      2,       2116800 },
+       {  44100,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      2,       2116800 },
+       {  44100,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      2,       2822400 },
+       {  44100,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      2,       2822400 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      0,      2,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      0,      2,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S24_LE, 1,  0,      0,      2,      18432000 },
+       { 384000,  SNDRV_PCM_FORMAT_S24_LE, 2,  0,      0,      2,      18432000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      0,      2,      24576000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      0,      2,      24576000 },
+
+       /* Fixed 8-slot TDM, other values from params */
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      8,      0,       1024000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      8,      0,       1024000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,  0,      8,      0,       1024000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,  0,      8,      0,       1024000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      8,      0,       2048000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      8,      0,       2048000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 3,  0,      8,      0,       2048000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 4,  0,      8,      0,       2048000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,  0,      8,      0,      49152000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,  0,      8,      0,      49152000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 3,  0,      8,      0,      49152000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 4,  0,      8,      0,      49152000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,  0,      8,      0,      98304000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,  0,      8,      0,      98304000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 3,  0,      8,      0,      98304000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 4,  0,      8,      0,      98304000 },
+
+       /* Fixed 32-bit TDM, other values from params */
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,  32,     0,      0,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,  32,     0,      0,        512000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,  32,     0,      0,        768000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,  32,     0,      0,       1024000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,  32,     0,      0,        256000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,  32,     0,      0,        512000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 3,  32,     0,      0,        768000 },
+       {   8000,  SNDRV_PCM_FORMAT_S32_LE, 4,  32,     0,      0,       1024000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,  32,     0,      0,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,  32,     0,      0,      24576000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 3,  32,     0,      0,      36864000 },
+       { 384000,  SNDRV_PCM_FORMAT_S16_LE, 4,  32,     0,      0,      49152000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,  32,     0,      0,      12288000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,  32,     0,      0,      24576000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 3,  32,     0,      0,      36864000 },
+       { 384000,  SNDRV_PCM_FORMAT_S32_LE, 4,  32,     0,      0,      49152000 },
+
+       /* Fixed 6-slot 24-bit TDM, other values from params */
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 3,  24,     6,      0,       1152000 },
+       {   8000,  SNDRV_PCM_FORMAT_S24_LE, 4,  24,     6,      0,       1152000 },
+       { 192000,  SNDRV_PCM_FORMAT_S16_LE, 1,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S16_LE, 2,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S16_LE, 3,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S16_LE, 4,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S24_LE, 1,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S24_LE, 2,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S24_LE, 3,  24,     6,      0,      27648000 },
+       { 192000,  SNDRV_PCM_FORMAT_S24_LE, 4,  24,     6,      0,      27648000 },
+};
+
+static void test_tdm_params_to_bclk_one(struct kunit *test,
+                                       unsigned int rate, snd_pcm_format_t fmt,
+                                       unsigned int channels,
+                                       unsigned int tdm_width, unsigned int tdm_slots,
+                                       unsigned int slot_multiple,
+                                       unsigned int expected_bclk)
+{
+       struct snd_pcm_hw_params params;
+       int got_bclk;
+
+       _snd_pcm_hw_params_any(&params);
+       snd_mask_none(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT));
+       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->min = rate;
+       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->max = rate;
+       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels;
+       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels;
+       params_set_format(&params, fmt);
+
+       got_bclk = snd_soc_tdm_params_to_bclk(&params, tdm_width, tdm_slots, slot_multiple);
+       pr_debug("%s: r=%u sb=%u ch=%u tw=%u ts=%u sm=%u expected=%u got=%d\n",
+                __func__,
+                rate, params_width(&params), channels, tdm_width, tdm_slots, slot_multiple,
+                expected_bclk, got_bclk);
+       KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk);
+}
+
+static void test_tdm_params_to_bclk(struct kunit *test)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) {
+               test_tdm_params_to_bclk_one(test,
+                                           tdm_params_to_bclk_cases[i].rate,
+                                           tdm_params_to_bclk_cases[i].fmt,
+                                           tdm_params_to_bclk_cases[i].channels,
+                                           tdm_params_to_bclk_cases[i].tdm_width,
+                                           tdm_params_to_bclk_cases[i].tdm_slots,
+                                           tdm_params_to_bclk_cases[i].slot_multiple,
+                                           tdm_params_to_bclk_cases[i].bclk);
+
+               if (tdm_params_to_bclk_cases[i].slot_multiple > 0)
+                       continue;
+
+               /* Slot multiple 1 should have the same effect as multiple 0 */
+               test_tdm_params_to_bclk_one(test,
+                                           tdm_params_to_bclk_cases[i].rate,
+                                           tdm_params_to_bclk_cases[i].fmt,
+                                           tdm_params_to_bclk_cases[i].channels,
+                                           tdm_params_to_bclk_cases[i].tdm_width,
+                                           tdm_params_to_bclk_cases[i].tdm_slots,
+                                           1,
+                                           tdm_params_to_bclk_cases[i].bclk);
+       }
+}
+
+static struct kunit_case soc_utils_test_cases[] = {
+       KUNIT_CASE(test_tdm_params_to_bclk),
+       {}
+};
+
+static struct kunit_suite soc_utils_test_suite = {
+       .name = "soc-utils",
+       .test_cases = soc_utils_test_cases,
+};
+
+kunit_test_suites(&soc_utils_test_suite);
+
+MODULE_DESCRIPTION("ASoC soc-utils kunit test");
+MODULE_LICENSE("GPL");
index a4efe7e..594cb31 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/platform_device.h>
 #include <linux/export.h>
+#include <linux/math.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -52,6 +53,50 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
 }
 EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
 
+/**
+ * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info.
+ *
+ * Calculate the bclk from the params sample rate and the tdm slot count and
+ * tdm slot width. Either or both of tdm_width and tdm_slots can be 0.
+ *
+ * If tdm_width == 0 and tdm_slots > 0:        the params_width will be used.
+ * If tdm_width > 0 and tdm_slots == 0:        the params_channels will be used
+ *                                     as the slot count.
+ * Both tdm_width and tdm_slots are 0: this is equivalent to calling
+ *                                     snd_soc_params_to_bclk().
+ *
+ * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0)
+ * will be rounded up to a multiple of this value. This is mainly useful for
+ * I2S mode, which has a left and right phase so the number of slots is always
+ * a multiple of 2.
+ *
+ * @params:        Pointer to struct_pcm_hw_params.
+ * @tdm_width:     Width in bits of the tdm slots.
+ * @tdm_slots:     Number of tdm slots per frame.
+ * @slot_multiple: If >1 roundup slot count to a multiple of this value.
+ *
+ * Return: bclk frequency in Hz, else a negative error code if params format
+ *        is invalid.
+ */
+int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params,
+                              int tdm_width, int tdm_slots, int slot_multiple)
+{
+       if (!tdm_slots)
+               tdm_slots = params_channels(params);
+
+       if (slot_multiple > 1)
+               tdm_slots = roundup(tdm_slots, slot_multiple);
+
+       if (!tdm_width) {
+               tdm_width = snd_pcm_format_width(params_format(params));
+               if (tdm_width < 0)
+                       return tdm_width;
+       }
+
+       return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk);
+
 static const struct snd_pcm_hardware dummy_dma_hardware = {
        /* Random values to keep userspace happy when checking constraints */
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
index 18acbc0..92b5e83 100644 (file)
@@ -2,7 +2,9 @@
 
 snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
                control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
-               ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o
+               ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o\
+               ipc3-dtrace.o\
+               ipc4.o ipc4-loader.o
 ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
 snd-sof-objs += sof-client.o
 endif
index 903b6cc..c9482b2 100644 (file)
@@ -34,7 +34,7 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev)
 }
 EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON);
 
-int acp_sof_trace_init(struct snd_sof_dev *sdev,
+int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
                       struct sof_ipc_dma_trace_params_ext *dtrace_params)
 {
        struct acp_dsp_stream *stream;
@@ -46,7 +46,7 @@ int acp_sof_trace_init(struct snd_sof_dev *sdev,
        if (!stream)
                return -ENODEV;
 
-       stream->dmab = &sdev->dmatb;
+       stream->dmab = dmab;
        stream->num_pages = NUM_PAGES;
 
        ret = acp_dsp_stream_config(sdev, stream);
index 71d71c1..0c27257 100644 (file)
@@ -138,23 +138,75 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
        return ret;
 }
 
-static int psp_fw_validate(struct acp_dev_data *adata)
+/*
+ * psp_mbox_ready- function to poll ready bit of psp mbox
+ * @adata: acp device data
+ * @ack: bool variable to check ready bit status or psp ack
+ */
+
+static int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
 {
        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);
+               smn_read(adata->smn_dev, MP0_C2PMSG_114_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;
+       dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK);
+
+       if (ack)
+               return -ETIMEDOUT;
+
+       return -EBUSY;
+}
+
+/*
+ * psp_send_cmd - function to send psp command over mbox
+ * @adata: acp device data
+ * @cmd: non zero integer value for command type
+ */
+
+static int psp_send_cmd(struct acp_dev_data *adata, int cmd)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       int ret, timeout;
+       u32 data;
+
+       if (!cmd)
+               return -EINVAL;
+
+       /* Get a non-zero Doorbell value from PSP */
+       for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
+               msleep(MBOX_DELAY);
+               smn_read(adata->smn_dev, MP0_C2PMSG_73_REG, &data);
+               if (data)
+                       break;
+       }
+
+       if (!timeout) {
+               dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);
+               return -EINVAL;
+       }
+
+       /* Check if PSP is ready for new command */
+       ret = psp_mbox_ready(adata, 0);
+       if (ret)
+               return ret;
+
+       smn_write(adata->smn_dev, MP0_C2PMSG_114_REG, cmd);
+
+       /* Ring the Doorbell for PSP */
+       smn_write(adata->smn_dev, MP0_C2PMSG_73_REG, data);
+
+       /* Check MBOX ready as PSP ack */
+       ret = psp_mbox_ready(adata, 1);
+
+       return ret;
 }
 
 int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
@@ -196,7 +248,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
                return ret;
        }
 
-       ret = psp_fw_validate(adata);
+       ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
        if (ret)
                return ret;
 
index 35e46fe..291b44c 100644 (file)
 #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 MP0_C2PMSG_114_REG                     0x3810AC8
+#define MP0_C2PMSG_73_REG                      0x3810A24
+#define MBOX_ACP_SHA_DMA_COMMAND               0x70000
+#define MBOX_DELAY                             1000
 #define MBOX_READY_MASK                                0x80000000
 #define MBOX_STATUS_MASK                       0xFFFF
 
@@ -204,13 +206,13 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr
                      struct snd_pcm_hw_params *params,
                      struct snd_sof_platform_stream_params *platform_params);
 
-extern const struct snd_sof_dsp_ops sof_renoir_ops;
+extern 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,
+int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
                       struct sof_ipc_dma_trace_params_ext *dtrace_params);
 int acp_sof_trace_release(struct snd_sof_dev *sdev);
 
index 392ffbd..d5d9bcc 100644 (file)
@@ -54,9 +54,17 @@ static const struct sof_dev_desc renoir_desc = {
        .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",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "amd/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "amd/sof-tplg",
+       },
+       .default_fw_filename    = {
+               [SOF_IPC] = "sof-rn.ri",
+       },
        .nocodec_tplg_filename  = "sof-acp.tplg",
        .ops                    = &sof_renoir_ops,
 };
@@ -93,6 +101,7 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci
        res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
        if (!res) {
                sof_pci_remove(pci);
+               platform_device_unregister(dmic_dev);
                return -ENOMEM;
        }
 
index 409fd57..7019036 100644 (file)
@@ -123,7 +123,7 @@ static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev
 }
 
 /* AMD Renoir DSP ops */
-const struct snd_sof_dsp_ops sof_renoir_ops = {
+struct snd_sof_dsp_ops sof_renoir_ops = {
        /* probe and remove */
        .probe                  = amd_sof_acp_probe,
        .remove                 = amd_sof_acp_remove,
@@ -136,9 +136,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
        .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,
@@ -152,7 +149,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
        .ipc_msg_data           = acp_sof_ipc_msg_data,
        .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,
index a8e908e..47639b6 100644 (file)
@@ -147,8 +147,7 @@ static int sof_compr_free(struct snd_soc_component *component,
        stream.comp_id = spcm->stream[cstream->direction].comp_id;
 
        if (spcm->prepared[cstream->direction]) {
-               ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
-                                        &stream, sizeof(stream),
+               ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
                                         &reply, sizeof(reply));
                if (!ret)
                        spcm->prepared[cstream->direction] = false;
@@ -209,7 +208,7 @@ static int sof_compr_set_params(struct snd_soc_component *component,
                snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3;
        pcm.params.host_period_bytes = params->buffer.fragment_size;
 
-       ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+       ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
                                 &ipc_params_reply, sizeof(ipc_params_reply));
        if (ret < 0) {
                dev_err(component->dev, "error ipc failed\n");
@@ -268,8 +267,7 @@ static int sof_compr_trigger(struct snd_soc_component *component,
                break;
        }
 
-       return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
-                                 &stream, sizeof(stream),
+       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
                                  &reply, sizeof(reply));
 }
 
index de1778c..e0e9efd 100644 (file)
 #include "sof-priv.h"
 #include "sof-audio.h"
 
-static void update_mute_led(struct snd_sof_control *scontrol,
-                           struct snd_kcontrol *kcontrol,
-                           struct snd_ctl_elem_value *ucontrol)
-{
-       int temp = 0;
-       int mask;
-       int i;
-
-       mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-       for (i = 0; i < scontrol->num_channels; i++) {
-               if (ucontrol->value.integer.value[i]) {
-                       temp |= mask;
-                       break;
-               }
-       }
-
-       if (temp == scontrol->led_ctl.led_value)
-               return;
-
-       scontrol->led_ctl.led_value = temp;
-
-#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
-       if (!scontrol->led_ctl.direction)
-               ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON);
-       else
-               ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON);
-#endif
-}
-
 int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_value *ucontrol)
 {
@@ -121,9 +91,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
 
-       if (scontrol->led_ctl.use_led)
-               update_mute_led(scontrol, kcontrol, ucontrol);
-
        if (tplg_ops->control->switch_put)
                return tplg_ops->control->switch_put(scontrol, ucontrol);
 
@@ -220,10 +187,9 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
        const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
        int ret, err;
 
-       ret = pm_runtime_get_sync(scomp->dev);
+       ret = pm_runtime_resume_and_get(scomp->dev);
        if (ret < 0 && ret != -EACCES) {
                dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret);
-               pm_runtime_put_noidle(scomp->dev);
                return ret;
        }
 
index e916316..53719c0 100644 (file)
@@ -250,14 +250,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
        }
 
        if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
-               sdev->dtrace_is_supported = true;
+               sdev->fw_trace_is_supported = true;
 
-               /* init DMA trace */
-               ret = snd_sof_init_trace(sdev);
+               /* init firmware tracing */
+               ret = sof_fw_trace_init(sdev);
                if (ret < 0) {
                        /* non fatal */
-                       dev_warn(sdev->dev,
-                                "warning: failed to initialize trace %d\n",
+                       dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n",
                                 ret);
                }
        } else {
@@ -308,7 +307,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
 sof_machine_err:
        snd_sof_machine_unregister(sdev, plat_data);
 fw_trace_err:
-       snd_sof_free_trace(sdev);
+       sof_fw_trace_free(sdev);
 fw_run_err:
        snd_sof_fw_unload(sdev);
 fw_load_err:
@@ -342,6 +341,7 @@ static void sof_probe_work(struct work_struct *work)
 int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
 {
        struct snd_sof_dev *sdev;
+       int ret;
 
        sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
        if (!sdev)
@@ -357,11 +357,23 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
        sdev->first_boot = true;
        dev_set_drvdata(dev, sdev);
 
+       /* check IPC support */
+       if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
+               dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
+                       plat_data->ipc_type, plat_data->desc->ipc_supported_mask);
+               return -EINVAL;
+       }
+
+       /* init ops, if necessary */
+       ret = sof_ops_init(sdev);
+       if (ret < 0)
+               return ret;
+
        /* check all mandatory ops */
        if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
            !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
            !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
-           !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->fw_ready) {
+           !sof_ops(sdev)->ipc_msg_data) {
                dev_err(dev, "error: missing mandatory ops\n");
                return -EINVAL;
        }
@@ -434,7 +446,7 @@ int snd_sof_device_remove(struct device *dev)
        snd_sof_machine_unregister(sdev, pdata);
 
        if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
-               snd_sof_free_trace(sdev);
+               sof_fw_trace_free(sdev);
                ret = snd_sof_dsp_power_down_notify(sdev);
                if (ret < 0)
                        dev_warn(dev, "error: %d failed to prepare DSP for device removal",
index 7b11399..cf1271e 100644 (file)
@@ -229,14 +229,13 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
        if (!reply)
                return -ENOMEM;
 
-       ret = pm_runtime_get_sync(sdev->dev);
+       ret = pm_runtime_resume_and_get(sdev->dev);
        if (ret < 0 && ret != -EACCES) {
-               pm_runtime_put_noidle(sdev->dev);
                dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
                goto error;
        }
 
-       ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
+       ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
        pm_runtime_mark_last_busy(sdev->dev);
        pm_runtime_put_autosuspend(sdev->dev);
        if (ret < 0 || reply->rhdr.error < 0) {
@@ -331,7 +330,7 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init);
 
 int snd_sof_dbg_init(struct snd_sof_dev *sdev)
 {
-       const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
+       struct snd_sof_dsp_ops *ops = sof_ops(sdev);
        const struct snd_sof_debugfs_map *map;
        int i;
        int err;
@@ -444,6 +443,6 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev)
        snd_sof_ipc_dump(sdev);
        snd_sof_dsp_dbg_dump(sdev, "Firmware exception",
                             SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
-       snd_sof_trace_notify_for_error(sdev);
+       sof_fw_trace_fw_crashed(sdev);
 }
 EXPORT_SYMBOL(snd_sof_handle_fw_exception);
index 825bd2b..2844d9a 100644 (file)
@@ -487,7 +487,7 @@ static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
 }
 
 /* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8_ops = {
+static struct snd_sof_dsp_ops sof_imx8_ops = {
        /* probe and remove */
        .probe          = imx8_probe,
        .remove         = imx8_remove,
@@ -504,16 +504,14 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = {
 
        /* ipc */
        .send_msg       = imx8_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset     = imx8_get_mailbox_offset,
        .get_window_offset      = imx8_get_window_offset,
 
        .ipc_msg_data   = sof_ipc_msg_data,
        .set_stream_data_offset = sof_set_stream_data_offset,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
        .get_bar_index  = imx8_get_bar_index,
+
        /* firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -550,7 +548,7 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = {
 };
 
 /* i.MX8X ops */
-static const struct snd_sof_dsp_ops sof_imx8x_ops = {
+static struct snd_sof_dsp_ops sof_imx8x_ops = {
        /* probe and remove */
        .probe          = imx8_probe,
        .remove         = imx8_remove,
@@ -567,16 +565,14 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = {
 
        /* ipc */
        .send_msg       = imx8_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset     = imx8_get_mailbox_offset,
        .get_window_offset      = imx8_get_window_offset,
 
        .ipc_msg_data   = sof_ipc_msg_data,
        .set_stream_data_offset = sof_set_stream_data_offset,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
        .get_bar_index  = imx8_get_bar_index,
+
        /* firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -613,17 +609,33 @@ static const struct snd_sof_dsp_ops 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",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "imx/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "imx/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "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",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "imx/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "imx/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-imx8.ri",
+       },
        .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
        .ops = &sof_imx8_ops,
 };
index 803d6be..1243f8a 100644 (file)
@@ -412,7 +412,7 @@ static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state
 }
 
 /* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8m_ops = {
+static struct snd_sof_dsp_ops sof_imx8m_ops = {
        /* probe and remove */
        .probe          = imx8m_probe,
        .remove         = imx8m_remove,
@@ -430,16 +430,14 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = {
 
        /* ipc */
        .send_msg       = imx8m_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset     = imx8m_get_mailbox_offset,
        .get_window_offset      = imx8m_get_window_offset,
 
        .ipc_msg_data   = sof_ipc_msg_data,
        .set_stream_data_offset = sof_set_stream_data_offset,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
        .get_bar_index  = imx8m_get_bar_index,
+
        /* firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -473,9 +471,17 @@ static const struct snd_sof_dsp_ops 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",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "imx/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "imx/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-imx8m.ri",
+       },
        .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
        .ops = &sof_imx8m_ops,
 };
index 1724193..0def2aa 100644 (file)
@@ -260,7 +260,7 @@ config SND_SOC_SOF_HDA
          'select' statements at a higher level.
 
 config SND_SOC_SOF_HDA_PROBES
-       bool
+       tristate
        select SND_SOC_SOF_DEBUG_PROBES
        help
          The option enables the data probing for Intel(R) Skylake and newer
index 1f473d4..b9d51dc 100644 (file)
@@ -6,7 +6,7 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
 snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
                                 hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
                                 hda-dai.o hda-bus.o \
-                                apl.o cnl.o tgl.o icl.o
+                                apl.o cnl.o tgl.o icl.o hda-common-ops.o
 snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
 
 snd-sof-intel-hda-objs := hda-codec.o
index 6721c8f..0cea280 100644 (file)
@@ -15,6 +15,8 @@
  * Hardware interface for audio DSP on Apollolake and GeminiLake
  */
 
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
 #include "../sof-priv.h"
 #include "hda.h"
 #include "../sof-audio.h"
@@ -26,108 +28,62 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = {
 };
 
 /* apollolake ops */
-const struct snd_sof_dsp_ops sof_apl_ops = {
+struct snd_sof_dsp_ops sof_apl_ops;
+EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_apl_ops_init(struct snd_sof_dev *sdev)
+{
+       /* common defaults */
+       memcpy(&sof_apl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
        /* probe/remove/shutdown */
-       .probe          = hda_dsp_probe,
-       .remove         = hda_dsp_remove,
-       .shutdown       = hda_dsp_shutdown,
-
-       /* Register IO */
-       .write          = sof_io_write,
-       .read           = sof_io_read,
-       .write64        = sof_io_write64,
-       .read64         = sof_io_read64,
-
-       /* Block IO */
-       .block_read     = sof_block_read,
-       .block_write    = sof_block_write,
-
-       /* Mailbox IO */
-       .mailbox_read   = sof_mailbox_read,
-       .mailbox_write  = sof_mailbox_write,
-
-       /* doorbell */
-       .irq_thread     = hda_dsp_ipc_irq_thread,
-
-       /* ipc */
-       .send_msg       = hda_dsp_ipc_send_msg,
-       .fw_ready       = sof_fw_ready,
-       .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
-       .get_window_offset = hda_dsp_ipc_get_window_offset,
-
-       .ipc_msg_data   = hda_ipc_msg_data,
-       .set_stream_data_offset = hda_set_stream_data_offset,
-
-       /* machine driver */
-       .machine_select = hda_machine_select,
-       .machine_register = sof_machine_register,
-       .machine_unregister = sof_machine_unregister,
-       .set_mach_params = hda_set_mach_params,
+       sof_apl_ops.shutdown    = hda_dsp_shutdown;
+
+       if (sdev->pdata->ipc_type == SOF_IPC) {
+               /* doorbell */
+               sof_apl_ops.irq_thread  = hda_dsp_ipc_irq_thread;
+
+               /* ipc */
+               sof_apl_ops.send_msg    = hda_dsp_ipc_send_msg;
+       }
+
+       if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_fw_data *ipc4_data;
+
+               sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+               if (!sdev->private)
+                       return -ENOMEM;
+
+               ipc4_data = sdev->private;
+               ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+               /* doorbell */
+               sof_apl_ops.irq_thread  = hda_dsp_ipc4_irq_thread;
+
+               /* ipc */
+               sof_apl_ops.send_msg    = hda_dsp_ipc4_send_msg;
+       }
+
+       /* set DAI driver ops */
+       hda_set_dai_drv_ops(sdev, &sof_apl_ops);
 
        /* debug */
-       .debug_map      = apl_dsp_debugfs,
-       .debug_map_count        = ARRAY_SIZE(apl_dsp_debugfs),
-       .dbg_dump       = hda_dsp_dump,
-       .ipc_dump       = hda_ipc_dump,
-       .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
-       /* stream callbacks */
-       .pcm_open       = hda_dsp_pcm_open,
-       .pcm_close      = hda_dsp_pcm_close,
-       .pcm_hw_params  = hda_dsp_pcm_hw_params,
-       .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,
-
-       /* firmware loading */
-       .load_firmware = snd_sof_load_firmware_raw,
+       sof_apl_ops.debug_map   = apl_dsp_debugfs;
+       sof_apl_ops.debug_map_count     = ARRAY_SIZE(apl_dsp_debugfs);
+       sof_apl_ops.ipc_dump    = hda_ipc_dump;
 
        /* firmware run */
-       .run = hda_dsp_cl_boot_firmware,
+       sof_apl_ops.run = hda_dsp_cl_boot_firmware;
 
        /* pre/post fw run */
-       .pre_fw_run = hda_dsp_pre_fw_run,
-       .post_fw_run = hda_dsp_post_fw_run,
-
-       /* parse platform specific extended manifest */
-       .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+       sof_apl_ops.post_fw_run = hda_dsp_post_fw_run;
 
        /* dsp core get/put */
-       .core_get = hda_dsp_core_get,
-
-       /* trace callback */
-       .trace_init = hda_dsp_trace_init,
-       .trace_release = hda_dsp_trace_release,
-       .trace_trigger = hda_dsp_trace_trigger,
-
-       /* client ops */
-       .register_ipc_clients = hda_register_clients,
-       .unregister_ipc_clients = hda_unregister_clients,
-
-       /* DAI drivers */
-       .drv            = skl_dai,
-       .num_drv        = SOF_SKL_NUM_DAIS,
-
-       /* PM */
-       .suspend                = hda_dsp_suspend,
-       .resume                 = hda_dsp_resume,
-       .runtime_suspend        = hda_dsp_runtime_suspend,
-       .runtime_resume         = hda_dsp_runtime_resume,
-       .runtime_idle           = hda_dsp_runtime_idle,
-       .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
-       .set_power_state        = hda_dsp_set_power_state,
-
-       /* ALSA HW info flags */
-       .hw_info =      SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
-       .dsp_arch_ops = &sof_xtensa_arch_ops,
+       sof_apl_ops.core_get = hda_dsp_core_get;
+
+       return 0;
 };
-EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_apl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
 
 const struct sof_intel_dsp_desc apl_chip_info = {
        /* Apollolake */
@@ -139,9 +95,12 @@ const struct sof_intel_dsp_desc apl_chip_info = {
        .ipc_ack = HDA_DSP_REG_HIPCIE,
        .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE,
        .ipc_ctl = HDA_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 150,
        .ssp_count = APL_SSP_COUNT,
        .ssp_base_offset = APL_SSP_BASE_OFFSET,
        .quirks = SOF_INTEL_PROCEN_FMT_QUIRK,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_1_5_PLUS,
 };
 EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
index fb9682b..26df780 100644 (file)
@@ -567,7 +567,7 @@ static struct snd_soc_dai_driver bdw_dai[] = {
 };
 
 /* broadwell ops */
-static const struct snd_sof_dsp_ops sof_bdw_ops = {
+static struct snd_sof_dsp_ops sof_bdw_ops = {
        /*Device init */
        .probe          = bdw_probe,
 
@@ -591,7 +591,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
 
        /* ipc */
        .send_msg       = bdw_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset = bdw_get_mailbox_offset,
        .get_window_offset = bdw_get_window_offset,
 
@@ -614,9 +613,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
        .pcm_open       = sof_stream_pcm_open,
        .pcm_close      = sof_stream_pcm_close,
 
-       /* Module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
-
        /*Firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -637,6 +633,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
 static const struct sof_intel_dsp_desc bdw_chip_info = {
        .cores_num = 1,
        .host_managed_cores_mask = 1,
+       .hw_ip_version = SOF_INTEL_BROADWELL,
 };
 
 static const struct sof_dev_desc sof_acpi_broadwell_desc = {
@@ -646,9 +643,17 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = {
        .resindex_imr_base = -1,
        .irqindex_host_ipc = 0,
        .chip_info = &bdw_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-bdw.ri",
+       .ipc_supported_mask = BIT(SOF_IPC),
+       .ipc_default = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-bdw.ri",
+       },
        .nocodec_tplg_filename = "sof-bdw-nocodec.tplg",
        .ops = &sof_bdw_ops,
 };
index bb84a4a..4ed8381 100644 (file)
@@ -216,7 +216,7 @@ irq:
 }
 
 /* baytrail ops */
-static const struct snd_sof_dsp_ops sof_byt_ops = {
+static struct snd_sof_dsp_ops sof_byt_ops = {
        /* device init */
        .probe          = byt_acpi_probe,
        .remove         = byt_remove,
@@ -245,7 +245,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
 
        /* ipc */
        .send_msg       = atom_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset = atom_get_mailbox_offset,
        .get_window_offset = atom_get_window_offset,
 
@@ -268,9 +267,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
        .pcm_open       = sof_stream_pcm_open,
        .pcm_close      = sof_stream_pcm_close,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
-
        /*Firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -295,10 +291,11 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
 static const struct sof_intel_dsp_desc byt_chip_info = {
        .cores_num = 1,
        .host_managed_cores_mask = 1,
+       .hw_ip_version = SOF_INTEL_BAYTRAIL,
 };
 
 /* cherrytrail and braswell ops */
-static const struct snd_sof_dsp_ops sof_cht_ops = {
+static struct snd_sof_dsp_ops sof_cht_ops = {
        /* device init */
        .probe          = byt_acpi_probe,
        .remove         = byt_remove,
@@ -327,7 +324,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
 
        /* ipc */
        .send_msg       = atom_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset = atom_get_mailbox_offset,
        .get_window_offset = atom_get_window_offset,
 
@@ -350,9 +346,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
        .pcm_open       = sof_stream_pcm_open,
        .pcm_close      = sof_stream_pcm_close,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
-
        /*Firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -378,6 +371,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
 static const struct sof_intel_dsp_desc cht_chip_info = {
        .cores_num = 1,
        .host_managed_cores_mask = 1,
+       .hw_ip_version = SOF_INTEL_BAYTRAIL,
 };
 
 /* BYTCR uses different IRQ index */
@@ -388,9 +382,17 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = {
        .resindex_imr_base = 2,
        .irqindex_host_ipc = 0,
        .chip_info = &byt_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-byt.ri",
+       .ipc_supported_mask = BIT(SOF_IPC),
+       .ipc_default = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-byt.ri",
+       },
        .nocodec_tplg_filename = "sof-byt-nocodec.tplg",
        .ops = &sof_byt_ops,
 };
@@ -402,9 +404,17 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = {
        .resindex_imr_base = 2,
        .irqindex_host_ipc = 5,
        .chip_info = &byt_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-byt.ri",
+       .ipc_supported_mask = BIT(SOF_IPC),
+       .ipc_default = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-byt.ri",
+       },
        .nocodec_tplg_filename = "sof-byt-nocodec.tplg",
        .ops = &sof_byt_ops,
 };
@@ -416,9 +426,17 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
        .resindex_imr_base = 2,
        .irqindex_host_ipc = 5,
        .chip_info = &cht_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-cht.ri",
+       .ipc_supported_mask = BIT(SOF_IPC),
+       .ipc_default = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-cht.ri",
+       },
        .nocodec_tplg_filename = "sof-cht-nocodec.tplg",
        .ops = &sof_cht_ops,
 };
index 6a96470..cd6e5f8 100644 (file)
@@ -15,6 +15,9 @@
  * Hardware interface for audio DSP on Cannonlake.
  */
 
+#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
+#include "../ipc4-priv.h"
 #include "../ops.h"
 #include "hda.h"
 #include "hda-ipc.h"
@@ -29,6 +32,68 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = {
 static void cnl_ipc_host_done(struct snd_sof_dev *sdev);
 static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev);
 
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
+{
+       struct sof_ipc4_msg notification_data = {{ 0 }};
+       struct snd_sof_dev *sdev = context;
+       bool ipc_irq = false;
+       u32 hipcida, hipctdr;
+
+       hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
+       if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
+               /* DSP received the message */
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+                                       CNL_DSP_REG_HIPCCTL,
+                                       CNL_DSP_REG_HIPCCTL_DONE, 0);
+               cnl_ipc_dsp_done(sdev);
+
+               ipc_irq = true;
+       }
+
+       hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
+       if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
+               /* Message from DSP (reply or notification) */
+               u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+                                              CNL_DSP_REG_HIPCTDD);
+               u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
+               u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
+
+               if (primary & SOF_IPC4_MSG_DIR_MASK) {
+                       /* Reply received */
+                       struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+                       data->primary = primary;
+                       data->extension = extension;
+
+                       spin_lock_irq(&sdev->ipc_lock);
+
+                       snd_sof_ipc_get_reply(sdev);
+                       snd_sof_ipc_reply(sdev, data->primary);
+
+                       spin_unlock_irq(&sdev->ipc_lock);
+               } else {
+                       /* Notification received */
+                       notification_data.primary = primary;
+                       notification_data.extension = extension;
+
+                       sdev->ipc->msg.rx_data = &notification_data;
+                       snd_sof_ipc_msgs_rx(sdev);
+                       sdev->ipc->msg.rx_data = NULL;
+               }
+
+               /* Let DSP know that we have finished processing the message */
+               cnl_ipc_host_done(sdev);
+
+               ipc_irq = true;
+       }
+
+       if (!ipc_irq)
+               /* This interrupt is not shared so no need to return IRQ_NONE. */
+               dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+       return IRQ_HANDLED;
+}
+
 irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
 {
        struct snd_sof_dev *sdev = context;
@@ -176,6 +241,22 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
        return false;
 }
 
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+       struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+       /* send the message via mailbox */
+       if (msg_data->data_size)
+               sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+                                 msg_data->data_size);
+
+       snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, msg_data->extension);
+       snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
+                         msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
+
+       return 0;
+}
+
 int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
 {
        struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
@@ -244,108 +325,63 @@ void cnl_ipc_dump(struct snd_sof_dev *sdev)
 }
 
 /* cannonlake ops */
-const struct snd_sof_dsp_ops sof_cnl_ops = {
+struct snd_sof_dsp_ops sof_cnl_ops;
+EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_cnl_ops_init(struct snd_sof_dev *sdev)
+{
+       /* common defaults */
+       memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
        /* probe/remove/shutdown */
-       .probe          = hda_dsp_probe,
-       .remove         = hda_dsp_remove,
-       .shutdown       = hda_dsp_shutdown,
+       sof_cnl_ops.shutdown    = hda_dsp_shutdown;
+
+       /* ipc */
+       if (sdev->pdata->ipc_type == SOF_IPC) {
+               /* doorbell */
+               sof_cnl_ops.irq_thread  = cnl_ipc_irq_thread;
 
-       /* Register IO */
-       .write          = sof_io_write,
-       .read           = sof_io_read,
-       .write64        = sof_io_write64,
-       .read64         = sof_io_read64,
+               /* ipc */
+               sof_cnl_ops.send_msg    = cnl_ipc_send_msg;
+       }
 
-       /* Block IO */
-       .block_read     = sof_block_read,
-       .block_write    = sof_block_write,
+       if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_fw_data *ipc4_data;
 
-       /* Mailbox IO */
-       .mailbox_read   = sof_mailbox_read,
-       .mailbox_write  = sof_mailbox_write,
+               sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+               if (!sdev->private)
+                       return -ENOMEM;
 
-       /* doorbell */
-       .irq_thread     = cnl_ipc_irq_thread,
+               ipc4_data = sdev->private;
+               ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
 
-       /* ipc */
-       .send_msg       = cnl_ipc_send_msg,
-       .fw_ready       = sof_fw_ready,
-       .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
-       .get_window_offset = hda_dsp_ipc_get_window_offset,
+               /* doorbell */
+               sof_cnl_ops.irq_thread  = cnl_ipc4_irq_thread;
 
-       .ipc_msg_data   = hda_ipc_msg_data,
-       .set_stream_data_offset = hda_set_stream_data_offset,
+               /* ipc */
+               sof_cnl_ops.send_msg    = cnl_ipc4_send_msg;
+       }
 
-       /* machine driver */
-       .machine_select = hda_machine_select,
-       .machine_register = sof_machine_register,
-       .machine_unregister = sof_machine_unregister,
-       .set_mach_params = hda_set_mach_params,
+       /* set DAI driver ops */
+       hda_set_dai_drv_ops(sdev, &sof_cnl_ops);
 
        /* debug */
-       .debug_map      = cnl_dsp_debugfs,
-       .debug_map_count        = ARRAY_SIZE(cnl_dsp_debugfs),
-       .dbg_dump       = hda_dsp_dump,
-       .ipc_dump       = cnl_ipc_dump,
-       .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
-       /* stream callbacks */
-       .pcm_open       = hda_dsp_pcm_open,
-       .pcm_close      = hda_dsp_pcm_close,
-       .pcm_hw_params  = hda_dsp_pcm_hw_params,
-       .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,
-
-       /* firmware loading */
-       .load_firmware = snd_sof_load_firmware_raw,
+       sof_cnl_ops.debug_map   = cnl_dsp_debugfs;
+       sof_cnl_ops.debug_map_count     = ARRAY_SIZE(cnl_dsp_debugfs);
+       sof_cnl_ops.ipc_dump    = cnl_ipc_dump;
 
        /* pre/post fw run */
-       .pre_fw_run = hda_dsp_pre_fw_run,
-       .post_fw_run = hda_dsp_post_fw_run,
+       sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run;
 
-       /* parse platform specific extended manifest */
-       .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+       /* firmware run */
+       sof_cnl_ops.run = hda_dsp_cl_boot_firmware;
 
        /* dsp core get/put */
-       .core_get = hda_dsp_core_get,
+       sof_cnl_ops.core_get = hda_dsp_core_get;
 
-       /* firmware run */
-       .run = hda_dsp_cl_boot_firmware,
-
-       /* trace callback */
-       .trace_init = hda_dsp_trace_init,
-       .trace_release = hda_dsp_trace_release,
-       .trace_trigger = hda_dsp_trace_trigger,
-
-       /* client ops */
-       .register_ipc_clients = hda_register_clients,
-       .unregister_ipc_clients = hda_unregister_clients,
-
-       /* DAI drivers */
-       .drv            = skl_dai,
-       .num_drv        = SOF_SKL_NUM_DAIS,
-
-       /* PM */
-       .suspend                = hda_dsp_suspend,
-       .resume                 = hda_dsp_resume,
-       .runtime_suspend        = hda_dsp_runtime_suspend,
-       .runtime_resume         = hda_dsp_runtime_resume,
-       .runtime_idle           = hda_dsp_runtime_idle,
-       .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
-       .set_power_state        = hda_dsp_set_power_state,
-
-       /* ALSA HW info flags */
-       .hw_info =      SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
-       .dsp_arch_ops = &sof_xtensa_arch_ops,
+       return 0;
 };
-EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_cnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
 
 const struct sof_intel_dsp_desc cnl_chip_info = {
        /* Cannonlake */
@@ -357,12 +393,15 @@ const struct sof_intel_dsp_desc cnl_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = CNL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_1_8,
 };
 EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
 
@@ -383,11 +422,14 @@ const struct sof_intel_dsp_desc jsl_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_0,
 };
 EXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
new file mode 100644 (file)
index 0000000..b232639
--- /dev/null
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license.  When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+
+/*
+ * common ops for SKL+ HDAudio platforms
+ */
+
+#include "../sof-priv.h"
+#include "hda.h"
+#include "../sof-audio.h"
+
+struct snd_sof_dsp_ops sof_hda_common_ops = {
+       /* probe/remove/shutdown */
+       .probe          = hda_dsp_probe,
+       .remove         = hda_dsp_remove,
+
+       /* Register IO */
+       .write          = sof_io_write,
+       .read           = sof_io_read,
+       .write64        = sof_io_write64,
+       .read64         = sof_io_read64,
+
+       /* Block IO */
+       .block_read     = sof_block_read,
+       .block_write    = sof_block_write,
+
+       /* Mailbox IO */
+       .mailbox_read   = sof_mailbox_read,
+       .mailbox_write  = sof_mailbox_write,
+
+       /* ipc */
+       .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
+       .get_window_offset = hda_dsp_ipc_get_window_offset,
+
+       .ipc_msg_data   = hda_ipc_msg_data,
+       .set_stream_data_offset = hda_set_stream_data_offset,
+
+       /* machine driver */
+       .machine_select = hda_machine_select,
+       .machine_register = sof_machine_register,
+       .machine_unregister = sof_machine_unregister,
+       .set_mach_params = hda_set_mach_params,
+
+       /* debug */
+       .dbg_dump       = hda_dsp_dump,
+       .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+       /* stream callbacks */
+       .pcm_open       = hda_dsp_pcm_open,
+       .pcm_close      = hda_dsp_pcm_close,
+       .pcm_hw_params  = hda_dsp_pcm_hw_params,
+       .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,
+
+       /* firmware loading */
+       .load_firmware = snd_sof_load_firmware_raw,
+
+       /* pre/post fw run */
+       .pre_fw_run = hda_dsp_pre_fw_run,
+
+       /* firmware run */
+       .run = hda_dsp_cl_boot_firmware,
+
+       /* parse platform specific extended manifest */
+       .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+
+       /* dsp core get/put */
+
+       /* trace callback */
+       .trace_init = hda_dsp_trace_init,
+       .trace_release = hda_dsp_trace_release,
+       .trace_trigger = hda_dsp_trace_trigger,
+
+       /* client ops */
+       .register_ipc_clients = hda_register_clients,
+       .unregister_ipc_clients = hda_unregister_clients,
+
+       /* DAI drivers */
+       .drv            = skl_dai,
+       .num_drv        = SOF_SKL_NUM_DAIS,
+
+       /* PM */
+       .suspend                = hda_dsp_suspend,
+       .resume                 = hda_dsp_resume,
+       .runtime_suspend        = hda_dsp_runtime_suspend,
+       .runtime_resume         = hda_dsp_runtime_resume,
+       .runtime_idle           = hda_dsp_runtime_idle,
+       .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
+       .set_power_state        = hda_dsp_set_power_state,
+
+       /* ALSA HW info flags */
+       .hw_info =      SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_PAUSE |
+                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+
+       .dsp_arch_ops = &sof_xtensa_arch_ops,
+};
index f9cb9f1..9823230 100644 (file)
@@ -50,8 +50,8 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
 }
 
 static struct hdac_ext_stream *
-       hda_link_stream_assign(struct hdac_bus *bus,
-                              struct snd_pcm_substream *substream)
+hda_link_stream_assign(struct hdac_bus *bus,
+                      struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct sof_intel_hda_stream *hda_stream;
@@ -128,6 +128,40 @@ static struct hdac_ext_stream *
        return res;
 }
 
+static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
+                               struct hdac_stream *hstream,
+                               struct snd_soc_dai *cpu_dai,
+                               struct snd_soc_dai *codec_dai,
+                               bool trigger_suspend_stop)
+{
+       struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+       struct hdac_bus *bus = hstream->bus;
+       struct sof_intel_hda_stream *hda_stream;
+       struct hdac_ext_link *link;
+       int stream_tag;
+
+       link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
+       if (!link)
+               return -EINVAL;
+
+       if (trigger_suspend_stop)
+               snd_hdac_ext_link_stream_clear(hext_stream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               stream_tag = hdac_stream(hext_stream)->stream_tag;
+               snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+       }
+       snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+       snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+       hext_stream->link_prepared = 0;
+
+       /* free the host DMA channel reserved by hostless streams */
+       hda_stream = hstream_to_sof_hda_stream(hext_stream);
+       hda_stream->host_reserved = 0;
+
+       return 0;
+}
+
 static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
                               struct hda_pipe_params *params)
 {
@@ -162,61 +196,28 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
        return 0;
 }
 
-static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
-                                     struct snd_soc_dapm_widget *w,
-                                     int channel, bool widget_setup)
-{
-       struct snd_sof_dai_config_data data;
-
-       data.dai_data = channel;
-
-       /* set up/free DAI widget and send DAI_CONFIG IPC */
-       if (widget_setup)
-               return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
-
-       return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
-}
-
-static int hda_link_hw_params(struct snd_pcm_substream *substream,
-                             struct snd_pcm_hw_params *params,
-                             struct snd_soc_dai *dai)
+static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params)
 {
        struct hdac_stream *hstream = substream->runtime->private_data;
-       struct hdac_bus *bus = hstream->bus;
        struct hdac_ext_stream *hext_stream;
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
-       struct sof_intel_hda_stream *hda_stream;
        struct hda_pipe_params p_params = {0};
-       struct snd_soc_dapm_widget *w;
+       struct hdac_bus *bus = hstream->bus;
        struct hdac_ext_link *link;
-       int stream_tag;
-       int ret;
 
        /* get stored dma data if resuming from system suspend */
-       hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+       hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
        if (!hext_stream) {
                hext_stream = hda_link_stream_assign(bus, substream);
                if (!hext_stream)
                        return -EBUSY;
 
-               snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream);
+               snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
        }
 
-       stream_tag = hdac_stream(hext_stream)->stream_tag;
-
-       hda_stream = hstream_to_sof_hda_stream(hext_stream);
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               w = dai->playback_widget;
-       else
-               w = dai->capture_widget;
-
-       /* set up the DAI widget and send the DAI_CONFIG with the new tag */
-       ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
-       if (ret < 0)
-               return ret;
-
        link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
        if (!link)
                return -EINVAL;
@@ -239,26 +240,118 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
        return hda_link_dma_params(hext_stream, &p_params);
 }
 
-static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
+static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
 {
-       struct hdac_ext_stream *hext_stream =
-                               snd_soc_dai_get_dma_data(dai, substream);
-       struct snd_sof_dev *sdev =
-                               snd_soc_component_get_drvdata(dai->component);
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        int stream = substream->stream;
 
-       if (hext_stream->link_prepared)
+       return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
+}
+
+static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct hdac_stream *hstream = substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+       struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+       int ret;
+
+       dev_dbg(cpu_dai->dev, "%s: cmd=%d\n", __func__, cmd);
+       if (!hext_stream)
+               return 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               snd_hdac_ext_link_stream_start(hext_stream);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, true);
+               if (ret < 0)
+                       return ret;
+
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               snd_hdac_ext_link_stream_clear(hext_stream);
+
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *hstream = substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+       struct hdac_ext_stream *hext_stream;
+
+       hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+       if (!hext_stream)
+               return 0;
+
+       return hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, false);
+}
+
+static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
+                                int channel, bool widget_setup)
+{
+       struct snd_sof_dai_config_data data;
+
+       data.dai_data = channel;
+
+       /* set up/free DAI widget and send DAI_CONFIG IPC */
+       if (widget_setup)
+               return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
+
+       return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+}
+
+static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *params,
+                                   struct snd_soc_dai *dai)
+{
+       struct hdac_ext_stream *hext_stream;
+       struct snd_soc_dapm_widget *w;
+       int stream_tag;
+
+       hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+       if (!hext_stream)
+               return -EINVAL;
+
+       stream_tag = hdac_stream(hext_stream)->stream_tag;
+
+       w = snd_soc_dai_get_widget(dai, substream->stream);
+
+       /* set up the DAI widget and send the DAI_CONFIG with the new tag */
+       return hda_dai_widget_update(w, stream_tag - 1, true);
+}
+
+static int hda_dai_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct hdac_ext_stream *hext_stream =
+                               snd_soc_dai_get_dma_data(dai, substream);
+       int ret;
+
+       if (hext_stream && hext_stream->link_prepared)
                return 0;
 
-       dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
+       ret = hda_link_dma_hw_params(substream, params);
+       if (ret < 0)
+               return ret;
 
-       return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
-                                 dai);
+       return hda_dai_hw_params_update(substream, params, dai);
 }
 
-static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
+
+static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
 {
        struct snd_sof_widget *swidget = w->dobj.private;
        struct snd_soc_component *component = swidget->scomp;
@@ -276,132 +369,134 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
        return ret;
 }
 
-static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
-                               int cmd, struct snd_soc_dai *dai)
+static int ipc3_hda_dai_prepare(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
 {
        struct hdac_ext_stream *hext_stream =
                                snd_soc_dai_get_dma_data(dai, substream);
-       struct sof_intel_hda_stream *hda_stream;
-       struct snd_soc_pcm_runtime *rtd;
-       struct snd_soc_dapm_widget *w;
-       struct hdac_ext_link *link;
-       struct hdac_stream *hstream;
-       struct hdac_bus *bus;
-       int stream_tag;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       int stream = substream->stream;
        int ret;
 
-       hstream = substream->runtime->private_data;
-       bus = hstream->bus;
-       rtd = asoc_substream_to_rtd(substream);
+       if (hext_stream && hext_stream->link_prepared)
+               return 0;
 
-       link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
-       if (!link)
-               return -EINVAL;
+       dev_dbg(sdev->dev, "%s: prepare stream dir %d\n", __func__, substream->stream);
 
-       hda_stream = hstream_to_sof_hda_stream(hext_stream);
+       ret = hda_link_dma_prepare(substream);
+       if (ret < 0)
+               return ret;
+
+       return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
+}
 
-       dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
+static int hda_dai_hw_free_ipc(int stream, /* direction */
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_dapm_widget *w;
+
+       w = snd_soc_dai_get_widget(dai, stream);
+
+       /* free the link DMA channel in the FW and the DAI widget */
+       return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
+}
+
+static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
+{
+       struct snd_soc_dapm_widget *w;
+       int ret;
+
+       ret = hda_link_dma_trigger(substream, cmd);
+       if (ret < 0)
+               return ret;
 
        w = snd_soc_dai_get_widget(dai, substream->stream);
 
+       dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd);
        switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               snd_hdac_ext_link_stream_start(hext_stream);
-               break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
-               snd_hdac_ext_link_stream_clear(hext_stream);
-
                /*
                 * free DAI widget during stop/suspend to keep widget use_count's balanced.
                 */
-               ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
+               ret = hda_dai_hw_free_ipc(substream->stream, dai);
                if (ret < 0)
                        return ret;
 
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       stream_tag = hdac_stream(hext_stream)->stream_tag;
-                       snd_hdac_ext_link_clear_stream_id(link, stream_tag);
-               }
-
-               hext_stream->link_prepared = 0;
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               snd_hdac_ext_link_stream_clear(hext_stream);
-
-               ret = hda_link_dai_config_pause_push_ipc(w);
+               ret = hda_dai_config_pause_push_ipc(w);
                if (ret < 0)
                        return ret;
                break;
+
        default:
-               return -EINVAL;
+               break;
        }
        return 0;
 }
 
-static int hda_link_hw_free(struct snd_pcm_substream *substream,
-                           struct snd_soc_dai *dai)
+static int hda_dai_hw_free(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
-       unsigned int stream_tag;
-       struct sof_intel_hda_stream *hda_stream;
-       struct hdac_bus *bus;
-       struct hdac_ext_link *link;
-       struct hdac_stream *hstream;
-       struct snd_soc_pcm_runtime *rtd;
-       struct hdac_ext_stream *hext_stream;
-       struct snd_soc_dapm_widget *w;
        int ret;
 
-       hstream = substream->runtime->private_data;
-       bus = hstream->bus;
-       rtd = asoc_substream_to_rtd(substream);
-       hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+       ret = hda_link_dma_hw_free(substream);
+       if (ret < 0)
+               return ret;
 
-       if (!hext_stream) {
-               dev_dbg(dai->dev,
-                       "%s: hext_stream is not assigned\n", __func__);
-               return -EINVAL;
-       }
+       return hda_dai_hw_free_ipc(substream->stream, dai);
+}
 
-       hda_stream = hstream_to_sof_hda_stream(hext_stream);
+static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
+       .hw_params = hda_dai_hw_params,
+       .hw_free = hda_dai_hw_free,
+       .trigger = ipc3_hda_dai_trigger,
+       .prepare = ipc3_hda_dai_prepare,
+};
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               w = dai->playback_widget;
-       else
-               w = dai->capture_widget;
+static int hda_dai_suspend(struct hdac_bus *bus)
+{
+       struct snd_soc_pcm_runtime *rtd;
+       struct hdac_ext_stream *hext_stream;
+       struct hdac_stream *s;
+       int ret;
 
-       /* free the link DMA channel in the FW and the DAI widget */
-       ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
-       if (ret < 0)
-               return ret;
+       /* set internal flag for BE */
+       list_for_each_entry(s, &bus->stream_list, list) {
 
-       link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
-       if (!link)
-               return -EINVAL;
+               hext_stream = stream_to_hdac_ext_stream(s);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               stream_tag = hdac_stream(hext_stream)->stream_tag;
-               snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+               /*
+                * clear stream. This should already be taken care for running
+                * streams when the SUSPEND trigger is called. But paused
+                * streams do not get suspended, so this needs to be done
+                * explicitly during suspend.
+                */
+               if (hext_stream->link_substream) {
+                       struct snd_soc_dai *cpu_dai;
+                       struct snd_soc_dai *codec_dai;
+
+                       rtd = asoc_substream_to_rtd(hext_stream->link_substream);
+                       cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+                       codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+                       ret = hda_link_dma_cleanup(hext_stream->link_substream, s,
+                                                  cpu_dai, codec_dai, false);
+                       if (ret < 0)
+                               return ret;
+
+                       /* for consistency with TRIGGER_SUSPEND we free DAI resources */
+                       ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
-       snd_soc_dai_set_dma_data(dai, substream, NULL);
-       snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
-       hext_stream->link_prepared = 0;
-
-       /* free the host DMA channel reserved by hostless streams */
-       hda_stream->host_reserved = 0;
-
        return 0;
 }
-
-static const struct snd_soc_dai_ops hda_link_dai_ops = {
-       .hw_params = hda_link_hw_params,
-       .hw_free = hda_link_hw_free,
-       .trigger = hda_link_pcm_trigger,
-       .prepare = hda_link_pcm_prepare,
-};
-
 #endif
 
 /* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
@@ -414,10 +509,7 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
 {
        struct snd_soc_dapm_widget *w;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               w = dai->playback_widget;
-       else
-               w = dai->capture_widget;
+       w = snd_soc_dai_get_widget(dai, substream->stream);
 
        if (setup)
                return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
@@ -478,8 +570,8 @@ static int ssp_dai_prepare(struct snd_pcm_substream *substream,
        return ssp_dai_setup(substream, dai, true);
 }
 
-static int ssp_dai_trigger(struct snd_pcm_substream *substream,
-                          int cmd, struct snd_soc_dai *dai)
+static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
 {
        if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
                return 0;
@@ -507,15 +599,39 @@ static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
        kfree(dma_data);
 }
 
-static const struct snd_soc_dai_ops ssp_dai_ops = {
+static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
        .startup = ssp_dai_startup,
        .hw_params = ssp_dai_hw_params,
        .prepare = ssp_dai_prepare,
-       .trigger = ssp_dai_trigger,
+       .trigger = ipc3_ssp_dai_trigger,
        .hw_free = ssp_dai_hw_free,
        .shutdown = ssp_dai_shutdown,
 };
 
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
+{
+       int i;
+
+       switch (sdev->pdata->ipc_type) {
+       case SOF_IPC:
+               for (i = 0; i < ops->num_drv; i++) {
+                       if (strstr(ops->drv[i].name, "SSP")) {
+                               ops->drv[i].ops = &ipc3_ssp_dai_ops;
+                               continue;
+                       }
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+                       if (strstr(ops->drv[i].name, "iDisp") ||
+                           strstr(ops->drv[i].name, "Analog") ||
+                           strstr(ops->drv[i].name, "Digital"))
+                               ops->drv[i].ops = &ipc3_hda_dai_ops;
+#endif
+               }
+               break;
+       default:
+               break;
+       }
+}
+
 /*
  * common dai driver for skl+ platforms.
  * some products who use this DAI array only physically have a subset of
@@ -524,7 +640,6 @@ static const struct snd_soc_dai_ops ssp_dai_ops = {
 struct snd_soc_dai_driver skl_dai[] = {
 {
        .name = "SSP0 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -536,7 +651,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "SSP1 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -548,7 +662,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "SSP2 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -560,7 +673,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "SSP3 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -572,7 +684,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "SSP4 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -584,7 +695,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "SSP5 Pin",
-       .ops = &ssp_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -611,7 +721,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
 {
        .name = "iDisp1 Pin",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -619,7 +728,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "iDisp2 Pin",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -627,7 +735,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "iDisp3 Pin",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -635,7 +742,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "iDisp4 Pin",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 8,
@@ -643,7 +749,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "Analog CPU DAI",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 16,
@@ -655,7 +760,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "Digital CPU DAI",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 16,
@@ -667,7 +771,6 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 {
        .name = "Alt Analog CPU DAI",
-       .ops = &hda_link_dai_ops,
        .playback = {
                .channels_min = 1,
                .channels_max = 16,
@@ -679,3 +782,22 @@ struct snd_soc_dai_driver skl_dai[] = {
 },
 #endif
 };
+
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
+{
+       /*
+        * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
+        * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
+        * Since the component suspend is called last, we can trap this corner case
+        * and force the DAIs to release their resources.
+        */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+       int ret;
+
+       ret = hda_dai_suspend(sof_to_bus(sdev));
+       if (ret < 0)
+               return ret;
+#endif
+
+       return 0;
+}
index 8ddde60..000ea90 100644 (file)
@@ -363,9 +363,8 @@ static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
        pm_gate.flags = flags;
 
        /* send pm_gate ipc to dsp */
-       return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd,
-                                       &pm_gate, sizeof(pm_gate), &reply,
-                                       sizeof(reply));
+       return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
+                                       &reply, sizeof(reply));
 }
 
 static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
@@ -433,7 +432,7 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
                 * when the DSP enters D0I3 while the system is in S0
                 * for debug purpose.
                 */
-               if (!sdev->dtrace_is_supported ||
+               if (!sdev->fw_trace_is_supported ||
                    !hda_enable_trace_D0I3_S0 ||
                    sdev->system_suspend_target != SOF_SUSPEND_NONE)
                        flags = HDA_PM_NO_DMA_TRACE;
@@ -895,44 +894,14 @@ int hda_dsp_shutdown(struct snd_sof_dev *sdev)
 
 int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
 {
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-       struct hdac_bus *bus = sof_to_bus(sdev);
-       struct snd_soc_pcm_runtime *rtd;
-       struct hdac_ext_stream *hext_stream;
-       struct hdac_ext_link *link;
-       struct hdac_stream *s;
-       const char *name;
-       int stream_tag;
-
-       /* set internal flag for BE */
-       list_for_each_entry(s, &bus->stream_list, list) {
-               hext_stream = stream_to_hdac_ext_stream(s);
-
-               /*
-                * clear stream. This should already be taken care for running
-                * streams when the SUSPEND trigger is called. But paused
-                * streams do not get suspended, so this needs to be done
-                * explicitly during suspend.
-                */
-               if (hext_stream->link_substream) {
-                       rtd = asoc_substream_to_rtd(hext_stream->link_substream);
-                       name = asoc_rtd_to_codec(rtd, 0)->component->name;
-                       link = snd_hdac_ext_bus_get_link(bus, name);
-                       if (!link)
-                               return -EINVAL;
-
-                       hext_stream->link_prepared = 0;
+       int ret;
 
-                       if (hdac_stream(hext_stream)->direction ==
-                               SNDRV_PCM_STREAM_CAPTURE)
-                               continue;
+       /* make sure all DAI resources are freed */
+       ret = hda_dsp_dais_suspend(sdev);
+       if (ret < 0)
+               dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__);
 
-                       stream_tag = hdac_stream(hext_stream)->stream_tag;
-                       snd_hdac_ext_link_clear_stream_id(link, stream_tag);
-               }
-       }
-#endif
-       return 0;
+       return ret;
 }
 
 void hda_dsp_d0i3_work(struct work_struct *work)
@@ -985,8 +954,7 @@ int hda_dsp_core_get(struct snd_sof_dev *sdev, int 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),
+       ret = sof_ipc_tx_message(sdev->ipc, &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",
index 0395638..f080112 100644 (file)
@@ -15,6 +15,7 @@
  * Hardware interface for generic Intel audio DSP HDA IP
  */
 
+#include <sound/sof/ipc4/header.h>
 #include "../ops.h"
 #include "hda.h"
 
@@ -65,6 +66,22 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
        return 0;
 }
 
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+       struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+       /* send the message via mailbox */
+       if (msg_data->data_size)
+               sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+                                 msg_data->data_size);
+
+       snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension);
+       snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
+                         msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
+
+       return 0;
+}
+
 void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
 {
        struct snd_sof_ipc_msg *msg = sdev->msg;
@@ -100,6 +117,71 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
        }
 }
 
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)
+{
+       struct sof_ipc4_msg notification_data = {{ 0 }};
+       struct snd_sof_dev *sdev = context;
+       bool ipc_irq = false;
+       u32 hipcie, hipct;
+
+       hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
+       if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {
+               /* DSP received the message */
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+                                       HDA_DSP_REG_HIPCCTL_DONE, 0);
+               hda_dsp_ipc_dsp_done(sdev);
+
+               ipc_irq = true;
+       }
+
+       hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
+       if (hipct & HDA_DSP_REG_HIPCT_BUSY) {
+               /* Message from DSP (reply or notification) */
+               u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+                                             HDA_DSP_REG_HIPCTE);
+               u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;
+               u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;
+
+               /* mask BUSY interrupt */
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+                                       HDA_DSP_REG_HIPCCTL_BUSY, 0);
+
+               if (primary & SOF_IPC4_MSG_DIR_MASK) {
+                       /* Reply received */
+                       struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+                       data->primary = primary;
+                       data->extension = extension;
+
+                       spin_lock_irq(&sdev->ipc_lock);
+
+                       snd_sof_ipc_get_reply(sdev);
+                       snd_sof_ipc_reply(sdev, data->primary);
+
+                       spin_unlock_irq(&sdev->ipc_lock);
+               } else {
+                       /* Notification received */
+
+                       notification_data.primary = primary;
+                       notification_data.extension = extension;
+                       sdev->ipc->msg.rx_data = &notification_data;
+                       snd_sof_ipc_msgs_rx(sdev);
+                       sdev->ipc->msg.rx_data = NULL;
+               }
+
+               /* Let DSP know that we have finished processing the message */
+               hda_dsp_ipc_host_done(sdev);
+
+               ipc_irq = true;
+       }
+
+       if (!ipc_irq)
+               /* This interrupt is not shared so no need to return IRQ_NONE. */
+               dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+       return IRQ_HANDLED;
+}
+
 /* IPC handler thread */
 irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
 {
index 2ac5d9d..6429012 100644 (file)
@@ -24,8 +24,6 @@
 #include "../sof-priv.h"
 #include "hda.h"
 
-#define HDA_CL_STREAM_FORMAT 0x40
-
 static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
 {
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -43,9 +41,9 @@ static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
        }
 }
 
-static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
-                                                unsigned int size, struct snd_dma_buffer *dmab,
-                                                int direction)
+struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
+                                             unsigned int size, struct snd_dma_buffer *dmab,
+                                             int direction)
 {
        struct hdac_ext_stream *hext_stream;
        struct hdac_stream *hstream;
@@ -101,14 +99,14 @@ out_put:
  * status on core 1, so power up core 1 also momentarily, keep it in
  * reset/stall and then turn it off
  */
-static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
+static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
 {
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
        const struct sof_intel_dsp_desc *chip = hda->desc;
-       unsigned int status;
+       unsigned int status, target_status;
+       u32 flags, ipc_hdr, j;
        unsigned long mask;
        char *dump_msg;
-       u32 flags, j;
        int ret;
 
        /* step 1: power up corex */
@@ -121,10 +119,12 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
 
        hda_ssp_set_cbp_cfp(sdev);
 
-       /* step 2: purge FW request */
-       snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req,
-                         chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW |
-                         ((stream_tag - 1) << 9)));
+       /* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */
+       ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL;
+       if (!imr_boot)
+               ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9);
+
+       snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr);
 
        /* step 3: unset core 0 reset state & unstall/run core 0 */
        ret = hda_dsp_core_run(sdev, BIT(0));
@@ -171,11 +171,20 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
        /* step 6: enable IPC interrupts */
        hda_dsp_ipc_int_enable(sdev);
 
-       /* step 7: wait for ROM init */
+       /*
+        * step 7:
+        * - Cold/Full boot: wait for ROM init to proceed to download the firmware
+        * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
+        */
+       if (imr_boot)
+               target_status = HDA_DSP_ROM_FW_ENTERED;
+       else
+               target_status = HDA_DSP_ROM_INIT;
+
        ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
-                                       HDA_DSP_SRAM_REG_ROM_STATUS, status,
+                                       chip->rom_status_reg, status,
                                        ((status & HDA_DSP_ROM_STS_MASK)
-                                               == HDA_DSP_ROM_INIT),
+                                               == target_status),
                                        HDA_DSP_REG_POLL_INTERVAL_US,
                                        chip->rom_init_timeout *
                                        USEC_PER_MSEC);
@@ -190,8 +199,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
 
        if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
                dev_err(sdev->dev,
-                       "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
-                       __func__);
+                       "%s: timeout with rom_status_reg (%#x) read\n",
+                       __func__, chip->rom_status_reg);
 
 err:
        flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
@@ -236,8 +245,8 @@ static int cl_trigger(struct snd_sof_dev *sdev,
        }
 }
 
-static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
-                     struct hdac_ext_stream *hext_stream)
+int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+                  struct hdac_ext_stream *hext_stream)
 {
        struct hdac_stream *hstream = &hext_stream->hstream;
        int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
@@ -268,8 +277,10 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
        return ret;
 }
 
-static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
 {
+       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+       const struct sof_intel_dsp_desc *chip = hda->desc;
        unsigned int reg;
        int ret, status;
 
@@ -280,7 +291,7 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str
        }
 
        status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
-                                       HDA_DSP_SRAM_REG_ROM_STATUS, reg,
+                                       chip->rom_status_reg, reg,
                                        ((reg & HDA_DSP_ROM_STS_MASK)
                                                == HDA_DSP_ROM_FW_ENTERED),
                                        HDA_DSP_REG_POLL_INTERVAL_US,
@@ -293,8 +304,8 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str
 
        if (status < 0) {
                dev_err(sdev->dev,
-                       "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
-                       __func__);
+                       "%s: timeout with rom_status_reg (%#x) read\n",
+                       __func__, chip->rom_status_reg);
        }
 
        ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
@@ -313,6 +324,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
        struct hdac_ext_stream *iccmax_stream;
        struct hdac_bus *bus = sof_to_bus(sdev);
        struct firmware stripped_firmware;
+       struct snd_dma_buffer dmab_bdl;
        int ret, ret1;
        u8 original_gb;
 
@@ -327,8 +339,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
        stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset;
 
        /* prepare capture stream for ICCMAX */
-       iccmax_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
-                                         &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE);
+       iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
+                                             &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE);
        if (IS_ERR(iccmax_stream)) {
                dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
                return PTR_ERR(iccmax_stream);
@@ -340,7 +352,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
         * Perform iccmax stream cleanup. This should be done even if firmware loading fails.
         * If the cleanup also fails, we return the initial error
         */
-       ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream);
+       ret1 = hda_cl_cleanup(sdev, &dmab_bdl, iccmax_stream);
        if (ret1 < 0) {
                dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
 
@@ -357,32 +369,11 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
 
 static int hda_dsp_boot_imr(struct snd_sof_dev *sdev)
 {
-       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
-       const struct sof_intel_dsp_desc *chip = hda->desc;
-       unsigned long mask;
-       u32 j;
        int ret;
 
-       /* power up & unstall/run the cores to run the firmware */
-       ret = hda_dsp_enable_core(sdev, chip->init_core_mask);
-       if (ret < 0) {
-               dev_err(sdev->dev, "dsp core start failed %d\n", ret);
-               return -EIO;
-       }
-
-       /* 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]++;
-
-       hda_ssp_set_cbp_cfp(sdev);
-
-       /* enable IPC interrupts */
-       hda_dsp_ipc_int_enable(sdev);
-
-       /* process wakes */
-       hda_sdw_process_wakeen(sdev);
+       ret = cl_dsp_init(sdev, 0, true);
+       if (!ret)
+               hda_sdw_process_wakeen(sdev);
 
        return ret;
 }
@@ -395,13 +386,17 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
        const struct sof_intel_dsp_desc *chip_info;
        struct hdac_ext_stream *hext_stream;
        struct firmware stripped_firmware;
+       struct snd_dma_buffer dmab;
        int ret, ret1, i;
 
-       if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) &&
-           !(sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) &&
-           !sdev->first_boot) {
+       if (hda->imrboot_supported && !sdev->first_boot) {
                dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n");
-               return hda_dsp_boot_imr(sdev);
+               hda->boot_iteration = 0;
+               ret = hda_dsp_boot_imr(sdev);
+               if (!ret)
+                       return 0;
+
+               dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n");
        }
 
        chip_info = desc->chip_info;
@@ -418,14 +413,15 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
        init_waitqueue_head(&sdev->boot_wait);
 
        /* prepare DMA for code loader stream */
-       hext_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
-                                       &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK);
+       hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT,
+                                           stripped_firmware.size,
+                                           &dmab, SNDRV_PCM_STREAM_PLAYBACK);
        if (IS_ERR(hext_stream)) {
                dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
                return PTR_ERR(hext_stream);
        }
 
-       memcpy(sdev->dmab.area, stripped_firmware.data,
+       memcpy(dmab.area, stripped_firmware.data,
               stripped_firmware.size);
 
        /* try ROM init a few times before giving up */
@@ -434,7 +430,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
                        "Attempting iteration %d of Core En/ROM load...\n", i);
 
                hda->boot_iteration = i + 1;
-               ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag);
+               ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag, false);
 
                /* don't retry anymore if successful */
                if (!ret)
@@ -473,7 +469,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
         * Continue with code loading and firmware boot
         */
        hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
-       ret = cl_copy_fw(sdev, hext_stream);
+       ret = hda_cl_copy_fw(sdev, hext_stream);
        if (!ret)
                dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
        else
@@ -486,7 +482,7 @@ cleanup:
         * This should be done even if firmware loading fails.
         * If the cleanup also fails, we return the initial error
         */
-       ret1 = cl_cleanup(sdev, &sdev->dmab, hext_stream);
+       ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream);
        if (ret1 < 0) {
                dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
 
@@ -522,12 +518,19 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
        int ret;
 
        if (sdev->first_boot) {
+               struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
                ret = hda_sdw_startup(sdev);
                if (ret < 0) {
                        dev_err(sdev->dev,
                                "error: could not startup SoundWire links\n");
                        return ret;
                }
+
+               /* Check if IMR boot is usable */
+               if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+                   sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT)
+                       hdev->imrboot_supported = true;
        }
 
        hda_sdw_int_enable(sdev, true);
index 755ef1d..cbb9bd7 100644 (file)
@@ -36,7 +36,7 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer
        return ret;
 }
 
-int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
                       struct sof_ipc_dma_trace_params_ext *dtrace_params)
 {
        struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -57,7 +57,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev,
         * initialize capture stream, set BDL address and return corresponding
         * stream tag which will be sent to the firmware by IPC message.
         */
-       ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb);
+       ret = hda_dsp_trace_prepare(sdev, dmab);
        if (ret < 0) {
                dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret);
                hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE,
index 9c97c80..bc07df1 100644 (file)
@@ -406,11 +406,13 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = {
 
 static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level)
 {
+       const struct sof_intel_dsp_desc *chip;
        u32 status;
        int i;
 
+       chip = get_chip_info(sdev->pdata);
        status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
-                                 HDA_DSP_SRAM_REG_ROM_STATUS);
+                                 chip->rom_status_reg);
 
        for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) {
                if (status == hda_dsp_rom_msg[i].code) {
@@ -456,13 +458,15 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
 static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level,
                                        u32 flags)
 {
+       const struct sof_intel_dsp_desc *chip;
        char msg[128];
        int len = 0;
        u32 value;
        int i;
 
+       chip = get_chip_info(sdev->pdata);
        for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) {
-               value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_STATUS + i * 0x4);
+               value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + i * 0x4);
                len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
        }
 
@@ -493,6 +497,17 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
        }
 }
 
+static bool hda_check_ipc_irq(struct snd_sof_dev *sdev)
+{
+       const struct sof_intel_dsp_desc *chip;
+
+       chip = get_chip_info(sdev->pdata);
+       if (chip && chip->check_ipc_irq)
+               return chip->check_ipc_irq(sdev);
+
+       return false;
+}
+
 void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
 {
        struct hdac_bus *bus = sof_to_bus(sdev);
@@ -584,14 +599,13 @@ static int hda_init(struct snd_sof_dev *sdev)
 
 static int check_dmic_num(struct snd_sof_dev *sdev)
 {
+       struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
        struct nhlt_acpi_table *nhlt;
        int dmic_num = 0;
 
-       nhlt = intel_nhlt_init(sdev->dev);
-       if (nhlt) {
+       nhlt = hdev->nhlt;
+       if (nhlt)
                dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt);
-               intel_nhlt_free(nhlt);
-       }
 
        /* allow for module parameter override */
        if (dmic_num_override != -1) {
@@ -611,10 +625,11 @@ static int check_dmic_num(struct snd_sof_dev *sdev)
 
 static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev)
 {
+       struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
        struct nhlt_acpi_table *nhlt;
        int ssp_mask = 0;
 
-       nhlt = intel_nhlt_init(sdev->dev);
+       nhlt = hdev->nhlt;
        if (!nhlt)
                return ssp_mask;
 
@@ -623,7 +638,6 @@ static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev)
                if (ssp_mask)
                        dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask);
        }
-       intel_nhlt_free(nhlt);
 
        return ssp_mask;
 }
@@ -816,7 +830,7 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
        if (hda_dsp_check_stream_irq(sdev))
                hda_dsp_stream_threaded_handler(irq, sdev);
 
-       if (hda_dsp_check_ipc_irq(sdev))
+       if (hda_check_ipc_irq(sdev))
                sof_ops(sdev)->irq_thread(irq, sdev);
 
        if (hda_dsp_check_sdw_irq(sdev))
@@ -984,6 +998,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
 
        INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
 
+       hdev->nhlt = intel_nhlt_init(sdev->dev);
+
        return 0;
 
 free_ipc_irq:
@@ -1009,6 +1025,10 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
        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);
+       struct nhlt_acpi_table *nhlt = hda->nhlt;
+
+       if (nhlt)
+               intel_nhlt_free(nhlt);
 
        /* cancel any attempt for DSP D0I3 */
        cancel_delayed_work_sync(&hda->d0i3_work);
@@ -1274,7 +1294,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
                        mach->mach_params.links = mach->links;
                        mach->mach_params.link_mask = mach->link_mask;
                        mach->mach_params.platform = dev_name(sdev->dev);
-                       pdata->fw_filename = pdata->desc->default_fw_filename;
+                       pdata->fw_filename = pdata->desc->default_fw_filename[pdata->ipc_type];
                        pdata->tplg_filename = mach->sof_tplg_filename;
 
                        /*
index 05e5e15..3e0f7b0 100644 (file)
 #define HDA_DSP_ROM_USER_EXCEPTION             0xBEEF0000
 #define HDA_DSP_ROM_UNEXPECTED_RESET           0xDECAF000
 #define HDA_DSP_ROM_NULL_FW_ENTRY              0x4c4c4e55
-#define HDA_DSP_IPC_PURGE_FW                   0x01004000
+
+#define HDA_DSP_ROM_IPC_CONTROL                        0x01000000
+#define HDA_DSP_ROM_IPC_PURGE_FW               0x00004000
 
 /* various timeout values */
 #define HDA_DSP_PU_TIMEOUT             50
 #define HDA_DSP_REG_POLL_INTERVAL_US           500     /* 0.5 msec */
 #define HDA_DSP_REG_POLL_RETRY_COUNT           50
 
-#define HDA_DSP_ADSPIC_IPC                     1
-#define HDA_DSP_ADSPIS_IPC                     1
+#define HDA_DSP_ADSPIC_IPC                     BIT(0)
+#define HDA_DSP_ADSPIS_IPC                     BIT(0)
 
 /* Intel HD Audio General DSP Registers */
 #define HDA_DSP_GEN_BASE               0x0
 /* HIPCTE */
 #define HDA_DSP_REG_HIPCTE_MSG_MASK    0x3FFFFFFF
 
-#define HDA_DSP_ADSPIC_CL_DMA          0x2
-#define HDA_DSP_ADSPIS_CL_DMA          0x2
+#define HDA_DSP_ADSPIC_CL_DMA          BIT(1)
+#define HDA_DSP_ADSPIS_CL_DMA          BIT(1)
 
 /* Delay before scheduling D0i3 entry */
 #define BXT_D0I3_DELAY 5000
@@ -416,6 +418,8 @@ enum sof_hda_D0_substate {
 
 /* represents DSP HDA controller frontend - i.e. host facing control */
 struct sof_intel_hda_dev {
+       bool imrboot_supported;
+
        int boot_iteration;
 
        struct hda_bus hbus;
@@ -449,6 +453,9 @@ struct sof_intel_hda_dev {
 
        /* FW clock config, 0:HPRO, 1:LPRO */
        bool clk_config_lpro;
+
+       /* Intel NHLT information */
+       struct nhlt_acpi_table *nhlt;
 };
 
 static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
@@ -588,6 +595,13 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir);
  */
 int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev);
 int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev);
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream);
+struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
+                                             unsigned int size, struct snd_dma_buffer *dmab,
+                                             int direction);
+int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+                  struct hdac_ext_stream *hext_stream);
+#define HDA_CL_STREAM_FORMAT 0x40
 
 /* pre and post fw run ops */
 int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev);
@@ -644,7 +658,7 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
 /*
  * Trace Control.
  */
-int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
                       struct sof_ipc_dma_trace_params_ext *dtrace_params);
 int hda_dsp_trace_release(struct snd_sof_dev *sdev);
 int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
@@ -683,18 +697,24 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
 
 /* common dai driver */
 extern struct snd_soc_dai_driver skl_dai[];
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
 
 /*
  * Platform Specific HW abstraction Ops.
  */
-extern const struct snd_sof_dsp_ops sof_apl_ops;
-extern const struct snd_sof_dsp_ops sof_cnl_ops;
-extern const struct snd_sof_dsp_ops sof_tgl_ops;
-extern const struct snd_sof_dsp_ops sof_icl_ops;
+extern struct snd_sof_dsp_ops sof_hda_common_ops;
+
+extern struct snd_sof_dsp_ops sof_apl_ops;
+int sof_apl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_cnl_ops;
+int sof_cnl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_tgl_ops;
+int sof_tgl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_icl_ops;
+int sof_icl_ops_init(struct snd_sof_dev *sdev);
 
 extern const struct sof_intel_dsp_desc apl_chip_info;
 extern const struct sof_intel_dsp_desc cnl_chip_info;
-extern const struct sof_intel_dsp_desc skl_chip_info;
 extern const struct sof_intel_dsp_desc icl_chip_info;
 extern const struct sof_intel_dsp_desc tgl_chip_info;
 extern const struct sof_intel_dsp_desc tglh_chip_info;
@@ -742,4 +762,12 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f
 
 extern int sof_hda_position_quirk;
 
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops);
+
+/* IPC4 */
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+
 #endif
index b44a649..f19517d 100644 (file)
@@ -56,11 +56,18 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
        int ret;
 
        if (sdev->first_boot) {
+               struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
                ret = hda_sdw_startup(sdev);
                if (ret < 0) {
                        dev_err(sdev->dev, "error: could not startup SoundWire links\n");
                        return ret;
                }
+
+               /* Check if IMR boot is usable */
+               if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+                   sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT)
+                       hdev->imrboot_supported = true;
        }
 
        hda_sdw_int_enable(sdev, true);
@@ -88,109 +95,44 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
 }
 
 /* Icelake ops */
-const struct snd_sof_dsp_ops sof_icl_ops = {
-       /* probe/remove/shutdown */
-       .probe          = hda_dsp_probe,
-       .remove         = hda_dsp_remove,
-       .shutdown       = hda_dsp_shutdown,
-
-       /* Register IO */
-       .write          = sof_io_write,
-       .read           = sof_io_read,
-       .write64        = sof_io_write64,
-       .read64         = sof_io_read64,
+struct snd_sof_dsp_ops sof_icl_ops;
+EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
 
-       /* Block IO */
-       .block_read     = sof_block_read,
-       .block_write    = sof_block_write,
+int sof_icl_ops_init(struct snd_sof_dev *sdev)
+{
+       /* common defaults */
+       memcpy(&sof_icl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
 
-       /* Mailbox IO */
-       .mailbox_read   = sof_mailbox_read,
-       .mailbox_write  = sof_mailbox_write,
+       /* probe/remove/shutdown */
+       sof_icl_ops.shutdown    = hda_dsp_shutdown;
 
        /* doorbell */
-       .irq_thread     = cnl_ipc_irq_thread,
+       sof_icl_ops.irq_thread  = cnl_ipc_irq_thread;
 
        /* ipc */
-       .send_msg       = cnl_ipc_send_msg,
-       .fw_ready       = sof_fw_ready,
-       .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
-       .get_window_offset = hda_dsp_ipc_get_window_offset,
-
-       .ipc_msg_data   = hda_ipc_msg_data,
-       .set_stream_data_offset = hda_set_stream_data_offset,
-
-       /* machine driver */
-       .machine_select = hda_machine_select,
-       .machine_register = sof_machine_register,
-       .machine_unregister = sof_machine_unregister,
-       .set_mach_params = hda_set_mach_params,
+       sof_icl_ops.send_msg    = cnl_ipc_send_msg;
 
        /* debug */
-       .debug_map      = icl_dsp_debugfs,
-       .debug_map_count        = ARRAY_SIZE(icl_dsp_debugfs),
-       .dbg_dump       = hda_dsp_dump,
-       .ipc_dump       = cnl_ipc_dump,
-       .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
-       /* stream callbacks */
-       .pcm_open       = hda_dsp_pcm_open,
-       .pcm_close      = hda_dsp_pcm_close,
-       .pcm_hw_params  = hda_dsp_pcm_hw_params,
-       .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,
-
-       /* firmware loading */
-       .load_firmware = snd_sof_load_firmware_raw,
+       sof_icl_ops.debug_map   = icl_dsp_debugfs;
+       sof_icl_ops.debug_map_count     = ARRAY_SIZE(icl_dsp_debugfs);
+       sof_icl_ops.ipc_dump    = cnl_ipc_dump;
 
        /* pre/post fw run */
-       .pre_fw_run = hda_dsp_pre_fw_run,
-       .post_fw_run = icl_dsp_post_fw_run,
+       sof_icl_ops.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,
+       /* firmware run */
+       sof_icl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
+       sof_icl_ops.stall = icl_dsp_core_stall;
 
        /* dsp core get/put */
-       .core_get = hda_dsp_core_get,
+       sof_icl_ops.core_get = hda_dsp_core_get;
 
-       /* firmware run */
-       .run = hda_dsp_cl_boot_firmware_iccmax,
-       .stall = icl_dsp_core_stall,
-
-       /* trace callback */
-       .trace_init = hda_dsp_trace_init,
-       .trace_release = hda_dsp_trace_release,
-       .trace_trigger = hda_dsp_trace_trigger,
-
-       /* client ops */
-       .register_ipc_clients = hda_register_clients,
-       .unregister_ipc_clients = hda_unregister_clients,
-
-       /* DAI drivers */
-       .drv            = skl_dai,
-       .num_drv        = SOF_SKL_NUM_DAIS,
-
-       /* PM */
-       .suspend                = hda_dsp_suspend,
-       .resume                 = hda_dsp_resume,
-       .runtime_suspend        = hda_dsp_runtime_suspend,
-       .runtime_resume         = hda_dsp_runtime_resume,
-       .runtime_idle           = hda_dsp_runtime_idle,
-       .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
-       .set_power_state        = hda_dsp_set_power_state,
-
-       /* ALSA HW info flags */
-       .hw_info =      SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
-       .dsp_arch_ops = &sof_xtensa_arch_ops,
+       /* set DAI driver ops */
+       hda_set_dai_drv_ops(sdev, &sof_icl_ops);
+
+       return 0;
 };
-EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_icl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
 
 const struct sof_intel_dsp_desc icl_chip_info = {
        /* Icelake */
@@ -202,11 +144,14 @@ const struct sof_intel_dsp_desc icl_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_0,
 };
 EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
index a023b3c..2de3658 100644 (file)
@@ -27,11 +27,23 @@ static const struct sof_dev_desc bxt_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &apl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-apl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/apl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-apl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-apl-nocodec.tplg",
        .ops = &sof_apl_ops,
+       .ops_init = sof_apl_ops_init,
 };
 
 static const struct sof_dev_desc glk_desc = {
@@ -42,11 +54,23 @@ static const struct sof_dev_desc glk_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &apl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-glk.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/glk",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-glk.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-glk-nocodec.tplg",
        .ops = &sof_apl_ops,
+       .ops_init = sof_apl_ops_init,
 };
 
 /* PCI IDs */
index 40cf1cd..87e587a 100644 (file)
@@ -28,11 +28,23 @@ static const struct sof_dev_desc cnl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &cnl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-cnl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/cnl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-cnl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
        .ops = &sof_cnl_ops,
+       .ops_init = sof_cnl_ops_init,
 };
 
 static const struct sof_dev_desc cfl_desc = {
@@ -44,11 +56,23 @@ static const struct sof_dev_desc cfl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &cnl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-cfl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/cnl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-cfl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
        .ops = &sof_cnl_ops,
+       .ops_init = sof_cnl_ops_init,
 };
 
 static const struct sof_dev_desc cml_desc = {
@@ -60,11 +84,23 @@ static const struct sof_dev_desc cml_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &cnl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-cml.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/cnl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-cml.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
        .ops = &sof_cnl_ops,
+       .ops_init = sof_cnl_ops_init,
 };
 
 /* PCI IDs */
index 39c8412..1c7f16c 100644 (file)
@@ -28,11 +28,23 @@ static const struct sof_dev_desc icl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &icl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-icl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/icl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-icl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-icl-nocodec.tplg",
        .ops = &sof_icl_ops,
+       .ops_init = sof_icl_ops_init,
 };
 
 static const struct sof_dev_desc jsl_desc = {
@@ -43,11 +55,23 @@ static const struct sof_dev_desc jsl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &jsl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-jsl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/jsl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-jsl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-jsl-nocodec.tplg",
        .ops = &sof_cnl_ops,
+       .ops_init = sof_cnl_ops_init,
 };
 
 /* PCI IDs */
index feaec25..58a9bd9 100644 (file)
@@ -28,11 +28,23 @@ static const struct sof_dev_desc tgl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &tgl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-tgl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/tgl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-tgl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
        .ops = &sof_tgl_ops,
+       .ops_init = sof_tgl_ops_init,
 };
 
 static const struct sof_dev_desc tglh_desc = {
@@ -44,11 +56,23 @@ static const struct sof_dev_desc tglh_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &tglh_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-tgl-h.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/tgl-h",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-tgl-h.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
        .ops = &sof_tgl_ops,
+       .ops_init = sof_tgl_ops_init,
 };
 
 static const struct sof_dev_desc ehl_desc = {
@@ -59,11 +83,23 @@ static const struct sof_dev_desc ehl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &ehl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-ehl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/ehl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-ehl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-ehl-nocodec.tplg",
        .ops = &sof_tgl_ops,
+       .ops_init = sof_tgl_ops_init,
 };
 
 static const struct sof_dev_desc adls_desc = {
@@ -75,11 +111,23 @@ static const struct sof_dev_desc adls_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &adls_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-adl-s.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/adl-s",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-adl-s.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-adl-nocodec.tplg",
        .ops = &sof_tgl_ops,
+       .ops_init = sof_tgl_ops_init,
 };
 
 static const struct sof_dev_desc adl_desc = {
@@ -91,11 +139,23 @@ static const struct sof_dev_desc adl_desc = {
        .resindex_imr_base      = -1,
        .irqindex_host_ipc      = -1,
        .chip_info = &tgl_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-adl.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+               [SOF_INTEL_IPC4] = "intel/avs/adl",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+               [SOF_INTEL_IPC4] = "intel/avs-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-adl.ri",
+               [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+       },
        .nocodec_tplg_filename = "sof-adl-nocodec.tplg",
        .ops = &sof_tgl_ops,
+       .ops_init = sof_tgl_ops_init,
 };
 
 /* PCI IDs */
@@ -116,6 +176,12 @@ static const struct pci_device_id sof_pci_ids[] = {
                .driver_data = (unsigned long)&adl_desc},
        { PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */
                .driver_data = (unsigned long)&adl_desc},
+       { PCI_DEVICE(0x8086, 0x51c9), /* ADL-PS */
+               .driver_data = (unsigned long)&adl_desc},
+       { PCI_DEVICE(0x8086, 0x51ca), /* RPL-P */
+               .driver_data = (unsigned long)&adl_desc},
+       { PCI_DEVICE(0x8086, 0x51cb), /* RPL-P */
+               .driver_data = (unsigned long)&adl_desc},
        { PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */
                .driver_data = (unsigned long)&adl_desc},
        { PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */
@@ -140,4 +206,3 @@ module_pci_driver(snd_sof_pci_intel_tgl_driver);
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON);
 MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
-
index 6efef22..f0f6d9b 100644 (file)
@@ -75,7 +75,11 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev)
 
        /* LPE base */
        base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
-       size = PCI_BAR_SIZE;
+       size = pci_resource_len(pci, desc->resindex_lpe_base);
+       if (size < PCI_BAR_SIZE) {
+               dev_err(sdev->dev, "error: I/O region is too small.\n");
+               return -ENODEV;
+       }
 
        dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
        sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
@@ -132,7 +136,7 @@ irq:
        return ret;
 }
 
-const struct snd_sof_dsp_ops sof_tng_ops = {
+struct snd_sof_dsp_ops sof_tng_ops = {
        /* device init */
        .probe          = tangier_pci_probe,
 
@@ -160,7 +164,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
 
        /* ipc */
        .send_msg       = atom_send_msg,
-       .fw_ready       = sof_fw_ready,
        .get_mailbox_offset = atom_get_mailbox_offset,
        .get_window_offset = atom_get_window_offset,
 
@@ -183,9 +186,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
        .pcm_open       = sof_stream_pcm_open,
        .pcm_close      = sof_stream_pcm_close,
 
-       /* module loading */
-       .load_module    = snd_sof_parse_module_memcpy,
-
        /*Firmware loading */
        .load_firmware  = snd_sof_load_firmware_memcpy,
 
@@ -206,6 +206,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
 const struct sof_intel_dsp_desc tng_chip_info = {
        .cores_num = 1,
        .host_managed_cores_mask = 1,
+       .hw_ip_version = SOF_INTEL_TANGIER,
 };
 
 static const struct sof_dev_desc tng_desc = {
@@ -215,9 +216,17 @@ static const struct sof_dev_desc tng_desc = {
        .resindex_imr_base      = 0,
        .irqindex_host_ipc      = -1,
        .chip_info = &tng_chip_info,
-       .default_fw_path = "intel/sof",
-       .default_tplg_path = "intel/sof-tplg",
-       .default_fw_filename = "sof-byt.ri",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "intel/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "intel/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-byt.ri",
+       },
        .nocodec_tplg_filename = "sof-byt.tplg",
        .ops = &sof_tng_ops,
 };
index f36cd9d..1fd7b48 100644 (file)
 #ifndef __SOF_INTEL_SHIM_H
 #define __SOF_INTEL_SHIM_H
 
+enum sof_intel_hw_ip_version {
+       SOF_INTEL_TANGIER,
+       SOF_INTEL_BAYTRAIL,
+       SOF_INTEL_BROADWELL,
+       SOF_INTEL_CAVS_1_5,     /* SkyLake, KabyLake, AmberLake */
+       SOF_INTEL_CAVS_1_5_PLUS,/* ApolloLake, GeminiLake */
+       SOF_INTEL_CAVS_1_8,     /* CannonLake, CometLake, CoffeeLake */
+       SOF_INTEL_CAVS_2_0,     /* IceLake, JasperLake */
+       SOF_INTEL_CAVS_2_5,     /* TigerLake, AlderLake */
+};
+
 /*
  * SHIM registers for BYT, BSW, CHT, BDW
  */
@@ -164,16 +175,19 @@ struct sof_intel_dsp_desc {
        int ipc_ack;
        int ipc_ack_mask;
        int ipc_ctl;
+       int rom_status_reg;
        int rom_init_timeout;
        int ssp_count;                  /* ssp count of the platform */
        int ssp_base_offset;            /* base address of the SSPs */
        u32 sdw_shim_base;
        u32 sdw_alh_base;
        u32 quirks;
+       enum sof_intel_hw_ip_version hw_ip_version;
        bool (*check_sdw_irq)(struct snd_sof_dev *sdev);
+       bool (*check_ipc_irq)(struct snd_sof_dev *sdev);
 };
 
-extern const struct snd_sof_dsp_ops sof_tng_ops;
+extern struct snd_sof_dsp_ops sof_tng_ops;
 
 extern const struct sof_intel_dsp_desc tng_chip_info;
 
index cb1c319..1ddc492 100644 (file)
@@ -9,6 +9,8 @@
  * Hardware interface for audio DSP on Tigerlake.
  */
 
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
 #include "../ops.h"
 #include "hda.h"
 #include "hda-ipc.h"
@@ -35,8 +37,7 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int 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),
+       return sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config),
                                 &pm_core_config, sizeof(pm_core_config));
 }
 
@@ -55,115 +56,68 @@ static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int 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),
+       return sof_ipc_tx_message(sdev->ipc, &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 = {
+struct snd_sof_dsp_ops sof_tgl_ops;
+EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_tgl_ops_init(struct snd_sof_dev *sdev)
+{
+       /* common defaults */
+       memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
        /* probe/remove/shutdown */
-       .probe          = hda_dsp_probe,
-       .remove         = hda_dsp_remove,
-       .shutdown       = hda_dsp_shutdown,
-
-       /* Register IO */
-       .write          = sof_io_write,
-       .read           = sof_io_read,
-       .write64        = sof_io_write64,
-       .read64         = sof_io_read64,
-
-       /* Block IO */
-       .block_read     = sof_block_read,
-       .block_write    = sof_block_write,
-
-       /* Mailbox IO */
-       .mailbox_read   = sof_mailbox_read,
-       .mailbox_write  = sof_mailbox_write,
-
-       /* doorbell */
-       .irq_thread     = cnl_ipc_irq_thread,
-
-       /* ipc */
-       .send_msg       = cnl_ipc_send_msg,
-       .fw_ready       = sof_fw_ready,
-       .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
-       .get_window_offset = hda_dsp_ipc_get_window_offset,
-
-       .ipc_msg_data   = hda_ipc_msg_data,
-       .set_stream_data_offset = hda_set_stream_data_offset,
-
-       /* machine driver */
-       .machine_select = hda_machine_select,
-       .machine_register = sof_machine_register,
-       .machine_unregister = sof_machine_unregister,
-       .set_mach_params = hda_set_mach_params,
+       sof_tgl_ops.shutdown    = hda_dsp_shutdown;
+
+       if (sdev->pdata->ipc_type == SOF_IPC) {
+               /* doorbell */
+               sof_tgl_ops.irq_thread  = cnl_ipc_irq_thread;
+
+               /* ipc */
+               sof_tgl_ops.send_msg    = cnl_ipc_send_msg;
+       }
+
+       if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_fw_data *ipc4_data;
+
+               sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+               if (!sdev->private)
+                       return -ENOMEM;
+
+               ipc4_data = sdev->private;
+               ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+               /* doorbell */
+               sof_tgl_ops.irq_thread  = cnl_ipc4_irq_thread;
+
+               /* ipc */
+               sof_tgl_ops.send_msg    = cnl_ipc4_send_msg;
+       }
+
+       /* set DAI driver ops */
+       hda_set_dai_drv_ops(sdev, &sof_tgl_ops);
 
        /* debug */
-       .debug_map      = tgl_dsp_debugfs,
-       .debug_map_count        = ARRAY_SIZE(tgl_dsp_debugfs),
-       .dbg_dump       = hda_dsp_dump,
-       .ipc_dump       = cnl_ipc_dump,
-       .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
-       /* stream callbacks */
-       .pcm_open       = hda_dsp_pcm_open,
-       .pcm_close      = hda_dsp_pcm_close,
-       .pcm_hw_params  = hda_dsp_pcm_hw_params,
-       .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,
-
-       /* firmware loading */
-       .load_firmware = snd_sof_load_firmware_raw,
+       sof_tgl_ops.debug_map   = tgl_dsp_debugfs;
+       sof_tgl_ops.debug_map_count     = ARRAY_SIZE(tgl_dsp_debugfs);
+       sof_tgl_ops.ipc_dump    = cnl_ipc_dump;
 
        /* pre/post fw run */
-       .pre_fw_run = hda_dsp_pre_fw_run,
-       .post_fw_run = hda_dsp_post_fw_run,
+       sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run;
 
-       /* parse platform specific extended manifest */
-       .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+       /* firmware run */
+       sof_tgl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
 
        /* dsp core get/put */
-       .core_get = tgl_dsp_core_get,
-       .core_put = tgl_dsp_core_put,
+       sof_tgl_ops.core_get = tgl_dsp_core_get;
+       sof_tgl_ops.core_put = tgl_dsp_core_put;
 
-       /* firmware run */
-       .run = hda_dsp_cl_boot_firmware_iccmax,
-
-       /* trace callback */
-       .trace_init = hda_dsp_trace_init,
-       .trace_release = hda_dsp_trace_release,
-       .trace_trigger = hda_dsp_trace_trigger,
-
-       /* client ops */
-       .register_ipc_clients = hda_register_clients,
-       .unregister_ipc_clients = hda_unregister_clients,
-
-       /* DAI drivers */
-       .drv            = skl_dai,
-       .num_drv        = SOF_SKL_NUM_DAIS,
-
-       /* PM */
-       .suspend                = hda_dsp_suspend,
-       .resume                 = hda_dsp_resume,
-       .runtime_suspend        = hda_dsp_runtime_suspend,
-       .runtime_resume         = hda_dsp_runtime_resume,
-       .runtime_idle           = hda_dsp_runtime_idle,
-       .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
-       .set_power_state        = hda_dsp_set_power_state,
-
-       /* ALSA HW info flags */
-       .hw_info =      SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
-       .dsp_arch_ops = &sof_xtensa_arch_ops,
+       return 0;
 };
-EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_tgl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
 
 const struct sof_intel_dsp_desc tgl_chip_info = {
        /* Tigerlake , Alderlake */
@@ -175,12 +129,15 @@ const struct sof_intel_dsp_desc tgl_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_5,
 };
 EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
 
@@ -194,12 +151,15 @@ const struct sof_intel_dsp_desc tglh_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_5,
 };
 EXPORT_SYMBOL_NS(tglh_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
 
@@ -213,12 +173,15 @@ const struct sof_intel_dsp_desc ehl_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_5,
 };
 EXPORT_SYMBOL_NS(ehl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
 
@@ -232,11 +195,14 @@ const struct sof_intel_dsp_desc adls_chip_info = {
        .ipc_ack = CNL_DSP_REG_HIPCIDA,
        .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
        .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+       .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
        .rom_init_timeout       = 300,
        .ssp_count = ICL_SSP_COUNT,
        .ssp_base_offset = CNL_SSP_BASE_OFFSET,
        .sdw_shim_base = SDW_SHIM_BASE,
        .sdw_alh_base = SDW_ALH_BASE,
        .check_sdw_irq  = hda_common_check_sdw_irq,
+       .check_ipc_irq  = hda_dsp_check_ipc_irq,
+       .hw_ip_version = SOF_INTEL_CAVS_2_5,
 };
 EXPORT_SYMBOL_NS(adls_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
index 5f57536..c5aef5f 100644 (file)
 #include "sof-priv.h"
 #include "sof-audio.h"
 #include "ops.h"
-#include "ipc3-ops.h"
 
-typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
-
-static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf);
-static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf);
-
-/*
- * IPC message Tx/Rx message handling.
+/**
+ * sof_ipc_send_msg - generic function to prepare and send one IPC message
+ * @sdev:              pointer to SOF core device struct
+ * @msg_data:          pointer to a message to send
+ * @msg_bytes:         number of bytes in the message
+ * @reply_bytes:       number of bytes available for the reply.
+ *                     The buffer for the reply data is not passed to this
+ *                     function, the available size is an information for the
+ *                     reply handling functions.
+ *
+ * On success the function returns 0, otherwise negative error number.
+ *
+ * Note: higher level sdev->ipc->tx_mutex must be held to make sure that
+ *      transfers are synchronized.
  */
-
-struct sof_ipc_ctrl_data_params {
-       size_t msg_bytes;
-       size_t hdr_bytes;
-       size_t pl_size;
-       size_t elems;
-       u32 num_msg;
-       u8 *src;
-       u8 *dst;
-};
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
-static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
-{
-       u8 *str;
-       u8 *str2 = NULL;
-       u32 glb;
-       u32 type;
-       bool vdbg = false;
-
-       glb = cmd & SOF_GLB_TYPE_MASK;
-       type = cmd & SOF_CMD_TYPE_MASK;
-
-       switch (glb) {
-       case SOF_IPC_GLB_REPLY:
-               str = "GLB_REPLY"; break;
-       case SOF_IPC_GLB_COMPOUND:
-               str = "GLB_COMPOUND"; break;
-       case SOF_IPC_GLB_TPLG_MSG:
-               str = "GLB_TPLG_MSG";
-               switch (type) {
-               case SOF_IPC_TPLG_COMP_NEW:
-                       str2 = "COMP_NEW"; break;
-               case SOF_IPC_TPLG_COMP_FREE:
-                       str2 = "COMP_FREE"; break;
-               case SOF_IPC_TPLG_COMP_CONNECT:
-                       str2 = "COMP_CONNECT"; break;
-               case SOF_IPC_TPLG_PIPE_NEW:
-                       str2 = "PIPE_NEW"; break;
-               case SOF_IPC_TPLG_PIPE_FREE:
-                       str2 = "PIPE_FREE"; break;
-               case SOF_IPC_TPLG_PIPE_CONNECT:
-                       str2 = "PIPE_CONNECT"; break;
-               case SOF_IPC_TPLG_PIPE_COMPLETE:
-                       str2 = "PIPE_COMPLETE"; break;
-               case SOF_IPC_TPLG_BUFFER_NEW:
-                       str2 = "BUFFER_NEW"; break;
-               case SOF_IPC_TPLG_BUFFER_FREE:
-                       str2 = "BUFFER_FREE"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_PM_MSG:
-               str = "GLB_PM_MSG";
-               switch (type) {
-               case SOF_IPC_PM_CTX_SAVE:
-                       str2 = "CTX_SAVE"; break;
-               case SOF_IPC_PM_CTX_RESTORE:
-                       str2 = "CTX_RESTORE"; break;
-               case SOF_IPC_PM_CTX_SIZE:
-                       str2 = "CTX_SIZE"; break;
-               case SOF_IPC_PM_CLK_SET:
-                       str2 = "CLK_SET"; break;
-               case SOF_IPC_PM_CLK_GET:
-                       str2 = "CLK_GET"; break;
-               case SOF_IPC_PM_CLK_REQ:
-                       str2 = "CLK_REQ"; break;
-               case SOF_IPC_PM_CORE_ENABLE:
-                       str2 = "CORE_ENABLE"; break;
-               case SOF_IPC_PM_GATE:
-                       str2 = "GATE"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_COMP_MSG:
-               str = "GLB_COMP_MSG";
-               switch (type) {
-               case SOF_IPC_COMP_SET_VALUE:
-                       str2 = "SET_VALUE"; break;
-               case SOF_IPC_COMP_GET_VALUE:
-                       str2 = "GET_VALUE"; break;
-               case SOF_IPC_COMP_SET_DATA:
-                       str2 = "SET_DATA"; break;
-               case SOF_IPC_COMP_GET_DATA:
-                       str2 = "GET_DATA"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_STREAM_MSG:
-               str = "GLB_STREAM_MSG";
-               switch (type) {
-               case SOF_IPC_STREAM_PCM_PARAMS:
-                       str2 = "PCM_PARAMS"; break;
-               case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
-                       str2 = "PCM_REPLY"; break;
-               case SOF_IPC_STREAM_PCM_FREE:
-                       str2 = "PCM_FREE"; break;
-               case SOF_IPC_STREAM_TRIG_START:
-                       str2 = "TRIG_START"; break;
-               case SOF_IPC_STREAM_TRIG_STOP:
-                       str2 = "TRIG_STOP"; break;
-               case SOF_IPC_STREAM_TRIG_PAUSE:
-                       str2 = "TRIG_PAUSE"; break;
-               case SOF_IPC_STREAM_TRIG_RELEASE:
-                       str2 = "TRIG_RELEASE"; break;
-               case SOF_IPC_STREAM_TRIG_DRAIN:
-                       str2 = "TRIG_DRAIN"; break;
-               case SOF_IPC_STREAM_TRIG_XRUN:
-                       str2 = "TRIG_XRUN"; break;
-               case SOF_IPC_STREAM_POSITION:
-                       vdbg = true;
-                       str2 = "POSITION"; break;
-               case SOF_IPC_STREAM_VORBIS_PARAMS:
-                       str2 = "VORBIS_PARAMS"; break;
-               case SOF_IPC_STREAM_VORBIS_FREE:
-                       str2 = "VORBIS_FREE"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_FW_READY:
-               str = "FW_READY"; break;
-       case SOF_IPC_GLB_DAI_MSG:
-               str = "GLB_DAI_MSG";
-               switch (type) {
-               case SOF_IPC_DAI_CONFIG:
-                       str2 = "CONFIG"; break;
-               case SOF_IPC_DAI_LOOPBACK:
-                       str2 = "LOOPBACK"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_TRACE_MSG:
-               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) {
-               case SOF_IPC_TEST_IPC_FLOOD:
-                       str2 = "IPC_FLOOD"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_DEBUG:
-               str = "GLB_DEBUG";
-               switch (type) {
-               case SOF_IPC_DEBUG_MEM_USAGE:
-                       str2 = "MEM_USAGE"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       case SOF_IPC_GLB_PROBE:
-               str = "GLB_PROBE";
-               switch (type) {
-               case SOF_IPC_PROBE_INIT:
-                       str2 = "INIT"; break;
-               case SOF_IPC_PROBE_DEINIT:
-                       str2 = "DEINIT"; break;
-               case SOF_IPC_PROBE_DMA_ADD:
-                       str2 = "DMA_ADD"; break;
-               case SOF_IPC_PROBE_DMA_INFO:
-                       str2 = "DMA_INFO"; break;
-               case SOF_IPC_PROBE_DMA_REMOVE:
-                       str2 = "DMA_REMOVE"; break;
-               case SOF_IPC_PROBE_POINT_ADD:
-                       str2 = "POINT_ADD"; break;
-               case SOF_IPC_PROBE_POINT_INFO:
-                       str2 = "POINT_INFO"; break;
-               case SOF_IPC_PROBE_POINT_REMOVE:
-                       str2 = "POINT_REMOVE"; break;
-               default:
-                       str2 = "unknown type"; break;
-               }
-               break;
-       default:
-               str = "unknown GLB command"; break;
-       }
-
-       if (str2) {
-               if (vdbg)
-                       dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
-               else
-                       dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
-       } else {
-               dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
-       }
-}
-#else
-static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
-{
-       if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
-               dev_dbg(dev, "%s: 0x%x\n", text, cmd);
-}
-#endif
-
-/* wait for IPC message reply */
-static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
-                       void *reply_data)
-{
-       struct snd_sof_dev *sdev = ipc->sdev;
-       struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
-       int ret;
-
-       /* wait for DSP IPC completion */
-       ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
-                                msecs_to_jiffies(sdev->ipc_timeout));
-
-       if (ret == 0) {
-               dev_err(sdev->dev,
-                       "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n",
-                       hdr->cmd, hdr->size, msg->reply_size);
-               snd_sof_handle_fw_exception(ipc->sdev);
-               ret = -ETIMEDOUT;
-       } else {
-               ret = msg->reply_error;
-               if (ret < 0) {
-                       dev_err(sdev->dev,
-                               "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n",
-                               hdr->cmd, hdr->size, msg->reply_size, ret);
-               } else {
-                       ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
-                       if (msg->reply_size)
-                               /* copy the data returned from DSP */
-                               memcpy(reply_data, msg->reply_data,
-                                      msg->reply_size);
-               }
-
-               /* re-enable dumps after successful IPC tx */
-               if (sdev->ipc_dump_printed) {
-                       sdev->dbg_dump_printed = false;
-                       sdev->ipc_dump_printed = false;
-               }
-       }
-
-       return ret;
-}
-
-/* send IPC message from host to DSP */
-static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc,
-                                      void *msg_data, size_t msg_bytes,
-                                      void *reply_data, size_t reply_bytes)
+int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+                    size_t reply_bytes)
 {
-       struct sof_ipc_cmd_hdr *hdr = msg_data;
-       struct snd_sof_dev *sdev = ipc->sdev;
+       struct snd_sof_ipc *ipc = sdev->ipc;
        struct snd_sof_ipc_msg *msg;
        int ret;
 
-       if (!msg_data || msg_bytes < sizeof(*hdr)) {
-               dev_err_ratelimited(sdev->dev, "No IPC message to send\n");
-               return -EINVAL;
-       }
-
        if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
                return -ENODEV;
 
        /*
-        * The spin-lock is also still needed to protect message objects against
-        * other atomic contexts.
+        * The spin-lock is needed to protect message objects against other
+        * atomic contexts.
         */
        spin_lock_irq(&sdev->ipc_lock);
 
@@ -327,38 +68,19 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc,
 
        spin_unlock_irq(&sdev->ipc_lock);
 
-       if (ret) {
-               dev_err_ratelimited(sdev->dev,
-                                   "error: ipc tx failed with error %d\n",
-                                   ret);
-               return ret;
-       }
-
-       ipc_log_header(sdev->dev, "ipc tx", hdr->cmd);
-
-       /* now wait for completion */
-       return tx_wait_done(ipc, msg, reply_data);
+       return ret;
 }
 
 /* send IPC message from host to DSP */
-int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
-                      void *msg_data, size_t msg_bytes, void *reply_data,
-                      size_t reply_bytes)
+int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
+                      void *reply_data, size_t reply_bytes)
 {
-       const struct sof_dsp_power_state target_state = {
-               .state = SOF_DSP_PM_D0,
-       };
-       int ret;
-
-       /* ensure the DSP is in D0 before sending a new IPC */
-       ret = snd_sof_dsp_set_power_state(ipc->sdev, &target_state);
-       if (ret < 0) {
-               dev_err(ipc->sdev->dev, "error: resuming DSP %d\n", ret);
-               return ret;
-       }
+       if (msg_bytes > ipc->max_payload_size ||
+           reply_bytes > ipc->max_payload_size)
+               return -ENOBUFS;
 
-       return sof_ipc_tx_message_no_pm(ipc, header, msg_data, msg_bytes,
-                                       reply_data, reply_bytes);
+       return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
+                               reply_bytes, false);
 }
 EXPORT_SYMBOL(sof_ipc_tx_message);
 
@@ -367,86 +89,32 @@ EXPORT_SYMBOL(sof_ipc_tx_message);
  * This will be used for IPC's that can be handled by the DSP
  * even in a low-power D0 substate.
  */
-int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
-                            void *msg_data, size_t msg_bytes,
+int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
                             void *reply_data, size_t reply_bytes)
 {
-       int ret;
-
-       if (msg_bytes > SOF_IPC_MSG_MAX_SIZE ||
-           reply_bytes > SOF_IPC_MSG_MAX_SIZE)
+       if (msg_bytes > ipc->max_payload_size ||
+           reply_bytes > ipc->max_payload_size)
                return -ENOBUFS;
 
-       /* Serialise IPC TX */
-       mutex_lock(&ipc->tx_mutex);
-
-       ret = sof_ipc_tx_message_unlocked(ipc, msg_data, msg_bytes,
-                                         reply_data, reply_bytes);
-
-       mutex_unlock(&ipc->tx_mutex);
-
-       return ret;
+       return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
+                               reply_bytes, true);
 }
 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) {
+       if (!sdev->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;
+       sdev->msg->reply_error = sdev->ipc->ops->get_reply(sdev);
 }
 EXPORT_SYMBOL(snd_sof_ipc_get_reply);
 
@@ -468,550 +136,11 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
 }
 EXPORT_SYMBOL(snd_sof_ipc_reply);
 
-static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
-{
-       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
-       struct sof_ipc_cmd_hdr *hdr = msg_buf;
-       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
-
-       switch (msg_type) {
-       case SOF_IPC_COMP_GET_VALUE:
-       case SOF_IPC_COMP_GET_DATA:
-               break;
-       default:
-               dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type);
-               return;
-       }
-
-       if (tplg_ops->control->update)
-               tplg_ops->control->update(sdev, msg_buf);
-}
-
-/* DSP firmware has sent host a message  */
-void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
-{
-       ipc_rx_callback rx_callback = NULL;
-       struct sof_ipc_cmd_hdr hdr;
-       void *msg_buf;
-       u32 cmd;
-       int err;
-
-       /* read back header */
-       err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
-       if (err < 0) {
-               dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
-               return;
-       }
-
-       if (hdr.size < sizeof(hdr)) {
-               dev_err(sdev->dev, "The received message size is invalid\n");
-               return;
-       }
-
-       ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
-
-       cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
-
-       /* check message type */
-       switch (cmd) {
-       case SOF_IPC_GLB_REPLY:
-               dev_err(sdev->dev, "error: ipc reply unknown\n");
-               break;
-       case SOF_IPC_FW_READY:
-               /* check for FW boot completion */
-               if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
-                       err = sof_ops(sdev)->fw_ready(sdev, cmd);
-                       if (err < 0)
-                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
-                       else
-                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
-
-                       /* wake up firmware loader */
-                       wake_up(&sdev->boot_wait);
-               }
-               break;
-       case SOF_IPC_GLB_COMPOUND:
-       case SOF_IPC_GLB_TPLG_MSG:
-       case SOF_IPC_GLB_PM_MSG:
-               break;
-       case SOF_IPC_GLB_COMP_MSG:
-               rx_callback = ipc_comp_notification;
-               break;
-       case SOF_IPC_GLB_STREAM_MSG:
-               rx_callback = ipc_stream_message;
-               break;
-       case SOF_IPC_GLB_TRACE_MSG:
-               rx_callback = ipc_trace_message;
-               break;
-       default:
-               dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
-               break;
-       }
-
-       /* read the full message */
-       msg_buf = kmalloc(hdr.size, GFP_KERNEL);
-       if (!msg_buf)
-               return;
-
-       err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
-       if (err < 0) {
-               dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
-       } else {
-               /* Call local handler for the message */
-               if (rx_callback)
-                       rx_callback(sdev, msg_buf);
-
-               /* Notify registered clients */
-               sof_client_ipc_rx_dispatcher(sdev, msg_buf);
-       }
-
-       kfree(msg_buf);
-
-       ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
-}
-EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
-
-/*
- * IPC trace mechanism.
- */
-
-static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
-{
-       struct sof_ipc_cmd_hdr *hdr = msg_buf;
-       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
-
-       switch (msg_type) {
-       case SOF_IPC_TRACE_DMA_POSITION:
-               snd_sof_trace_update_pos(sdev, msg_buf);
-               break;
-       default:
-               dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type);
-               break;
-       }
-}
-
-/*
- * IPC stream position.
- */
-
-static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
-{
-       struct snd_soc_component *scomp = sdev->component;
-       struct snd_sof_pcm_stream *stream;
-       struct sof_ipc_stream_posn posn;
-       struct snd_sof_pcm *spcm;
-       int direction, ret;
-
-       spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
-       if (!spcm) {
-               dev_err(sdev->dev,
-                       "error: period elapsed for unknown stream, msg_id %d\n",
-                       msg_id);
-               return;
-       }
-
-       stream = &spcm->stream[direction];
-       ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
-       if (ret < 0) {
-               dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
-               return;
-       }
-
-       dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
-                posn.host_posn, posn.dai_posn, posn.wallclock);
-
-       memcpy(&stream->posn, &posn, sizeof(posn));
-
-       if (spcm->pcm.compress)
-               snd_sof_compr_fragment_elapsed(stream->cstream);
-       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);
-}
-
-/* DSP notifies host of an XRUN within FW */
-static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
-{
-       struct snd_soc_component *scomp = sdev->component;
-       struct snd_sof_pcm_stream *stream;
-       struct sof_ipc_stream_posn posn;
-       struct snd_sof_pcm *spcm;
-       int direction, ret;
-
-       spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
-       if (!spcm) {
-               dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n",
-                       msg_id);
-               return;
-       }
-
-       stream = &spcm->stream[direction];
-       ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
-       if (ret < 0) {
-               dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
-               return;
-       }
-
-       dev_dbg(sdev->dev,  "posn XRUN: host %llx comp %d size %d\n",
-               posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
-
-#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
-       /* stop PCM on XRUN - used for pipeline debug */
-       memcpy(&stream->posn, &posn, sizeof(posn));
-       snd_pcm_stop_xrun(stream->substream);
-#endif
-}
-
-/* stream notifications from DSP FW */
-static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
-{
-       struct sof_ipc_cmd_hdr *hdr = msg_buf;
-       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
-       u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
-
-       switch (msg_type) {
-       case SOF_IPC_STREAM_POSITION:
-               ipc_period_elapsed(sdev, msg_id);
-               break;
-       case SOF_IPC_STREAM_TRIG_XRUN:
-               ipc_xrun(sdev, msg_id);
-               break;
-       default:
-               dev_err(sdev->dev, "error: unhandled stream message %#x\n",
-                       msg_id);
-               break;
-       }
-}
-
-/* get stream position IPC - use faster MMIO method if available on platform */
-int snd_sof_ipc_stream_posn(struct snd_soc_component *scomp,
-                           struct snd_sof_pcm *spcm, int direction,
-                           struct sof_ipc_stream_posn *posn)
-{
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-       struct sof_ipc_stream stream;
-       int err;
-
-       /* read position via slower IPC */
-       stream.hdr.size = sizeof(stream);
-       stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION;
-       stream.comp_id = spcm->stream[direction].comp_id;
-
-       /* send IPC to the DSP */
-       err = sof_ipc_tx_message(sdev->ipc,
-                                stream.hdr.cmd, &stream, sizeof(stream), posn,
-                                sizeof(*posn));
-       if (err < 0) {
-               dev_err(sdev->dev, "error: failed to get stream %d position\n",
-                       stream.comp_id);
-               return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(snd_sof_ipc_stream_posn);
-
-static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
-                                   struct sof_ipc_ctrl_data *src,
-                                   struct sof_ipc_ctrl_data *dst,
-                                   struct sof_ipc_ctrl_data_params *sparams)
-{
-       switch (ctrl_type) {
-       case SOF_CTRL_TYPE_VALUE_CHAN_GET:
-       case SOF_CTRL_TYPE_VALUE_CHAN_SET:
-               sparams->src = (u8 *)src->chanv;
-               sparams->dst = (u8 *)dst->chanv;
-               break;
-       case SOF_CTRL_TYPE_DATA_GET:
-       case SOF_CTRL_TYPE_DATA_SET:
-               sparams->src = (u8 *)src->data->data;
-               sparams->dst = (u8 *)dst->data->data;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       /* calculate payload size and number of messages */
-       sparams->pl_size = SOF_IPC_MSG_MAX_SIZE - sparams->hdr_bytes;
-       sparams->num_msg = DIV_ROUND_UP(sparams->msg_bytes, sparams->pl_size);
-
-       return 0;
-}
-
-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 set)
-{
-       struct sof_ipc_ctrl_data *partdata;
-       size_t send_bytes;
-       size_t offset = 0;
-       size_t msg_bytes;
-       size_t pl_size;
-       int err;
-       int i;
-
-       /* allocate max ipc size because we have at least one */
-       partdata = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
-       if (!partdata)
-               return -ENOMEM;
-
-       if (set)
-               err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata,
-                                              sparams);
-       else
-               err = sof_get_ctrl_copy_params(cdata->type, partdata, cdata,
-                                              sparams);
-       if (err < 0) {
-               kfree(partdata);
-               return err;
-       }
-
-       msg_bytes = sparams->msg_bytes;
-       pl_size = sparams->pl_size;
-
-       /* copy the header data */
-       memcpy(partdata, cdata, sparams->hdr_bytes);
-
-       /* Serialise IPC TX */
-       mutex_lock(&sdev->ipc->tx_mutex);
-
-       /* copy the payload data in a loop */
-       for (i = 0; i < sparams->num_msg; i++) {
-               send_bytes = min(msg_bytes, pl_size);
-               partdata->num_elems = send_bytes;
-               partdata->rhdr.hdr.size = sparams->hdr_bytes + send_bytes;
-               partdata->msg_index = i;
-               msg_bytes -= send_bytes;
-               partdata->elems_remaining = msg_bytes;
-
-               if (set)
-                       memcpy(sparams->dst, sparams->src + offset, send_bytes);
-
-               err = sof_ipc_tx_message_unlocked(sdev->ipc,
-                                                 partdata,
-                                                 partdata->rhdr.hdr.size,
-                                                 partdata,
-                                                 partdata->rhdr.hdr.size);
-               if (err < 0)
-                       break;
-
-               if (!set)
-                       memcpy(sparams->dst + offset, sparams->src, send_bytes);
-
-               offset += pl_size;
-       }
-
-       mutex_unlock(&sdev->ipc->tx_mutex);
-
-       kfree(partdata);
-       return err;
-}
-
-/*
- * IPC get()/set() for kcontrols.
- */
-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->ipc_control_data;
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-       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) {
-               if (swidget->comp_id == scontrol->comp_id) {
-                       widget_found = true;
-                       break;
-               }
-       }
-
-       if (!widget_found) {
-               dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
-               return -EINVAL;
-       }
-
-       /*
-        * Volatile controls should always be part of static pipelines and the widget use_count
-        * would always be > 0 in this case. For the others, just return the cached value if the
-        * widget is not set up.
-        */
-       if (!swidget->use_count)
-               return 0;
-
-       /* read or write firmware volume */
-       if (scontrol->readback_offset != 0) {
-               /* write/read value header via mmaped region */
-               send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
-               cdata->num_elems;
-               if (set)
-                       err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM,
-                                                     scontrol->readback_offset,
-                                                     cdata->chanv, send_bytes);
-
-               else
-                       err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM,
-                                                    scontrol->readback_offset,
-                                                    cdata->chanv, send_bytes);
-
-               if (err)
-                       dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n",
-                                    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->type = ctrl_type;
-       cdata->comp_id = scontrol->comp_id;
-       cdata->msg_index = 0;
-
-       /* calculate header and data size */
-       switch (cdata->type) {
-       case SOF_CTRL_TYPE_VALUE_CHAN_GET:
-       case SOF_CTRL_TYPE_VALUE_CHAN_SET:
-               sparams.msg_bytes = scontrol->num_channels *
-                       sizeof(struct sof_ipc_ctrl_value_chan);
-               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;
-               sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data) +
-                       sizeof(struct sof_abi_hdr);
-               sparams.elems = cdata->data->size;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       cdata->rhdr.hdr.size = sparams.msg_bytes + sparams.hdr_bytes;
-       cdata->num_elems = sparams.elems;
-       cdata->elems_remaining = 0;
-
-       /* send normal size ipc in one part */
-       if (cdata->rhdr.hdr.size <= SOF_IPC_MSG_MAX_SIZE) {
-               err = sof_ipc_tx_message(sdev->ipc, cdata->rhdr.hdr.cmd, cdata,
-                                        cdata->rhdr.hdr.size, cdata,
-                                        cdata->rhdr.hdr.size);
-
-               if (err < 0)
-                       dev_err(sdev->dev, "error: set/get ctrl ipc comp %d\n",
-                               cdata->comp_id);
-
-               return err;
-       }
-
-       /* data is bigger than max ipc size, chop into smaller pieces */
-       dev_dbg(sdev->dev, "large ipc size %u, control size %u\n",
-               cdata->rhdr.hdr.size, scontrol->size);
-
-       /* large messages is only supported from ABI 3.3.0 onwards */
-       if (v->abi_version < SOF_ABI_VER(3, 3, 0)) {
-               dev_err(sdev->dev, "error: incompatible FW ABI version\n");
-               return -EINVAL;
-       }
-
-       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",
-                       cdata->comp_id);
-
-       return err;
-}
-EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data);
-
-int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
-{
-       struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
-       struct sof_ipc_fw_version *v = &ready->version;
-
-       dev_info(sdev->dev,
-                "Firmware info: version %d:%d:%d-%s\n",  v->major, v->minor,
-                v->micro, v->tag);
-       dev_info(sdev->dev,
-                "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
-                SOF_ABI_VERSION_MAJOR(v->abi_version),
-                SOF_ABI_VERSION_MINOR(v->abi_version),
-                SOF_ABI_VERSION_PATCH(v->abi_version),
-                SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
-
-       if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
-               dev_err(sdev->dev, "error: incompatible FW ABI version\n");
-               return -EINVAL;
-       }
-
-       if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
-               if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
-                       dev_warn(sdev->dev, "warn: FW ABI is more recent than kernel\n");
-               } else {
-                       dev_err(sdev->dev, "error: FW ABI is more recent than kernel\n");
-                       return -EINVAL;
-               }
-       }
-
-       if (ready->flags & SOF_IPC_INFO_BUILD) {
-               dev_info(sdev->dev,
-                        "Firmware debug build %d on %s-%s - options:\n"
-                        " GDB: %s\n"
-                        " lock debug: %s\n"
-                        " lock vdebug: %s\n",
-                        v->build, v->date, v->time,
-                        (ready->flags & SOF_IPC_INFO_GDB) ?
-                               "enabled" : "disabled",
-                        (ready->flags & SOF_IPC_INFO_LOCKS) ?
-                               "enabled" : "disabled",
-                        (ready->flags & SOF_IPC_INFO_LOCKSV) ?
-                               "enabled" : "disabled");
-       }
-
-       /* copy the fw_version into debugfs at first boot */
-       memcpy(&sdev->fw_version, v, sizeof(*v));
-
-       return 0;
-}
-EXPORT_SYMBOL(snd_sof_ipc_valid);
-
-int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev)
-{
-       struct snd_sof_ipc_msg *msg;
-
-       msg = &sdev->ipc->msg;
-
-       msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
-       if (!msg->reply_data)
-               return -ENOMEM;
-
-       return 0;
-}
-
 struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
 {
        struct snd_sof_ipc *ipc;
        struct snd_sof_ipc_msg *msg;
+       const struct sof_ipc_ops *ops;
 
        ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
        if (!ipc)
@@ -1031,11 +160,33 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
         * versions, this will need to be modified to use the selected version at runtime.
         */
        ipc->ops = &ipc3_ops;
+       ops = ipc->ops;
 
        /* check for mandatory ops */
-       if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget ||
-           !ipc->ops->tplg->control) {
-               dev_err(sdev->dev, "Invalid IPC ops\n");
+       if (!ops->tx_msg || !ops->rx_msg || !ops->set_get_data || !ops->get_reply) {
+               dev_err(sdev->dev, "Missing IPC message handling ops\n");
+               return NULL;
+       }
+
+       if (!ops->fw_loader || !ops->fw_loader->validate ||
+           !ops->fw_loader->parse_ext_manifest) {
+               dev_err(sdev->dev, "Missing IPC firmware loading ops\n");
+               return NULL;
+       }
+
+       if (!ops->pcm) {
+               dev_err(sdev->dev, "Missing IPC PCM ops\n");
+               return NULL;
+       }
+
+       if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) {
+               dev_err(sdev->dev, "Missing IPC topology ops\n");
+               return NULL;
+       }
+
+       if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend ||
+                               !ops->fw_tracing->resume)) {
+               dev_err(sdev->dev, "Missing firmware tracing ops\n");
                return NULL;
        }
 
index cdd5ad8..3fdc0d8 100644 (file)
@@ -9,26 +9,85 @@
 
 #include "sof-priv.h"
 #include "sof-audio.h"
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
 
-static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
+/* IPC set()/get() for kcontrols. */
+static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
 {
-       if (value >= size)
-               return volume_map[size - 1];
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
+       struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+       const struct sof_ipc_ops *iops = sdev->ipc->ops;
+       enum sof_ipc_ctrl_type ctrl_type;
+       struct snd_sof_widget *swidget;
+       bool widget_found = false;
+       u32 ipc_cmd, msg_bytes;
 
-       return volume_map[value];
-}
+       list_for_each_entry(swidget, &sdev->widget_list, list) {
+               if (swidget->comp_id == scontrol->comp_id) {
+                       widget_found = true;
+                       break;
+               }
+       }
 
-static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
-{
-       int i;
+       if (!widget_found) {
+               dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__,
+                       scontrol->comp_id);
+               return -EINVAL;
+       }
+
+       /*
+        * Volatile controls should always be part of static pipelines and the widget use_count
+        * would always be > 0 in this case. For the others, just return the cached value if the
+        * widget is not set up.
+        */
+       if (!swidget->use_count)
+               return 0;
+
+       /*
+        * 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->type = ctrl_type;
+       cdata->comp_id = scontrol->comp_id;
+       cdata->msg_index = 0;
+
+       /* calculate header and data size */
+       switch (cdata->type) {
+       case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+       case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+               cdata->num_elems = scontrol->num_channels;
 
-       for (i = 0; i < size; i++) {
-               if (volume_map[i] >= value)
-                       return i;
+               msg_bytes = scontrol->num_channels *
+                           sizeof(struct sof_ipc_ctrl_value_chan);
+               msg_bytes += sizeof(struct sof_ipc_ctrl_data);
+               break;
+       case SOF_CTRL_TYPE_DATA_GET:
+       case SOF_CTRL_TYPE_DATA_SET:
+               cdata->num_elems = cdata->data->size;
+
+               msg_bytes = cdata->data->size;
+               msg_bytes += sizeof(struct sof_ipc_ctrl_data) +
+                            sizeof(struct sof_abi_hdr);
+               break;
+       default:
+               return -EINVAL;
        }
 
-       return i - 1;
+       cdata->rhdr.hdr.size = msg_bytes;
+       cdata->elems_remaining = 0;
+
+       return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
 }
 
 static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
@@ -49,7 +108,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
 
        /* refresh the component data from DSP */
        scontrol->comp_data_dirty = false;
-       ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+       ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
        if (ret < 0) {
                dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
 
@@ -97,7 +156,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
 
        /* notify DSP of mixer updates */
        if (pm_runtime_active(scomp->dev)) {
-               int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+               int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
 
                if (ret < 0) {
                        dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -145,7 +204,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
 
        /* notify DSP of mixer updates */
        if (pm_runtime_active(scomp->dev)) {
-               int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+               int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
 
                if (ret < 0) {
                        dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -193,7 +252,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
 
        /* notify DSP of enum updates */
        if (pm_runtime_active(scomp->dev)) {
-               int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+               int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
 
                if (ret < 0) {
                        dev_err(scomp->dev, "Failed to set enum updates for %s\n",
@@ -265,7 +324,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
 
        /* notify DSP of byte control updates */
        if (pm_runtime_active(scomp->dev))
-               return snd_sof_ipc_set_get_comp_data(scontrol, true);
+               return sof_ipc3_set_get_kcontrol_data(scontrol, true);
 
        return 0;
 }
@@ -379,7 +438,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
 
        /* notify DSP of byte control updates */
        if (pm_runtime_active(scomp->dev))
-               return snd_sof_ipc_set_get_comp_data(scontrol, true);
+               return sof_ipc3_set_get_kcontrol_data(scontrol, true);
 
        return 0;
 }
@@ -409,7 +468,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
        cdata->data->abi = SOF_ABI_VERSION;
 
        /* get all the component data from DSP */
-       ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+       ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
        if (ret < 0)
                return ret;
 
@@ -578,6 +637,60 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_
        snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
 }
 
+static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
+                                         struct snd_sof_widget *swidget)
+{
+       struct snd_sof_control *scontrol;
+       int ret;
+
+       /* set up all controls for the widget */
+       list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+               if (scontrol->comp_id == swidget->comp_id) {
+                       /* set kcontrol data in DSP */
+                       ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+                       if (ret < 0) {
+                               dev_err(sdev->dev,
+                                       "kcontrol %d set up failed for widget %s\n",
+                                       scontrol->comp_id, 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 = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+                       if (ret < 0)
+                               dev_warn(sdev->dev,
+                                        "kcontrol %d read failed for widget %s\n",
+                                        scontrol->comp_id, swidget->widget->name);
+               }
+
+       return 0;
+}
+
+static int
+sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
+{
+       int i;
+
+       /* init the volume table */
+       scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
+       if (!scontrol->volume_table)
+               return -ENOMEM;
+
+       /* populate the volume table */
+       for (i = 0; i < size ; i++)
+               scontrol->volume_table[i] = vol_compute_gain(i, tlv);
+
+       return 0;
+}
+
 const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
        .volume_put = sof_ipc3_volume_put,
        .volume_get = sof_ipc3_volume_get,
@@ -591,4 +704,6 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
        .bytes_ext_get = sof_ipc3_bytes_ext_get,
        .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,
        .update = sof_ipc3_control_update,
+       .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup,
+       .set_up_volume_table = sof_ipc3_set_up_volume_table,
 };
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
new file mode 100644 (file)
index 0000000..b4e1343
--- /dev/null
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+
+#include <linux/debugfs.h>
+#include <linux/sched/signal.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ops.h"
+#include "sof-utils.h"
+#include "ipc3-priv.h"
+
+#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
+#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
+
+enum sof_dtrace_state {
+       SOF_DTRACE_DISABLED,
+       SOF_DTRACE_STOPPED,
+       SOF_DTRACE_ENABLED,
+};
+
+struct sof_dtrace_priv {
+       struct snd_dma_buffer dmatb;
+       struct snd_dma_buffer dmatp;
+       int dma_trace_pages;
+       wait_queue_head_t trace_sleep;
+       u32 host_offset;
+       bool dtrace_error;
+       bool dtrace_draining;
+       enum sof_dtrace_state dtrace_state;
+};
+
+static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value,
+                                   struct sof_ipc_trace_filter_elem *elem_list,
+                                   int capacity, int *counter)
+{
+       if (*counter >= capacity)
+               return -ENOMEM;
+
+       elem_list[*counter].key = key;
+       elem_list[*counter].value = value;
+       ++*counter;
+
+       return 0;
+}
+
+static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
+                                   struct sof_ipc_trace_filter_elem *elem,
+                                   int capacity, int *counter)
+{
+       int log_level, pipe_id, comp_id, read, ret;
+       int len = strlen(line);
+       int cnt = *counter;
+       u32 uuid_id;
+
+       /* ignore empty content */
+       ret = sscanf(line, " %n", &read);
+       if (!ret && read == len)
+               return len;
+
+       ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
+       if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
+               dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line);
+               return -EINVAL;
+       }
+
+       if (uuid_id > 0) {
+               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
+                                              uuid_id, elem, capacity, &cnt);
+               if (ret)
+                       return ret;
+       }
+       if (pipe_id >= 0) {
+               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
+                                              pipe_id, elem, capacity, &cnt);
+               if (ret)
+                       return ret;
+       }
+       if (comp_id >= 0) {
+               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
+                                              comp_id, elem, capacity, &cnt);
+               if (ret)
+                       return ret;
+       }
+
+       ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
+                                      SOF_IPC_TRACE_FILTER_ELEM_FIN,
+                                      log_level, elem, capacity, &cnt);
+       if (ret)
+               return ret;
+
+       /* update counter only when parsing whole entry passed */
+       *counter = cnt;
+
+       return len;
+}
+
+static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
+                             int *out_elem_cnt,
+                             struct sof_ipc_trace_filter_elem **out)
+{
+       static const char entry_delimiter[] = ";";
+       char *entry = string;
+       int capacity = 0;
+       int entry_len;
+       int cnt = 0;
+
+       /*
+        * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
+        * IPC elements, depending on content. Calculate IPC elements capacity
+        * for the input string where each element is set.
+        */
+       while (entry) {
+               capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
+               entry = strchr(entry + 1, entry_delimiter[0]);
+       }
+       *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
+       if (!*out)
+               return -ENOMEM;
+
+       /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
+       while ((entry = strsep(&string, entry_delimiter))) {
+               entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
+               if (entry_len < 0) {
+                       dev_err(sdev->dev,
+                               "Parsing filter entry '%s' failed with %d\n",
+                               entry, entry_len);
+                       return -EINVAL;
+               }
+       }
+
+       *out_elem_cnt = cnt;
+
+       return 0;
+}
+
+static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
+                                   struct sof_ipc_trace_filter_elem *elems)
+{
+       struct sof_ipc_trace_filter *msg;
+       struct sof_ipc_reply reply;
+       size_t size;
+       int ret;
+
+       size = struct_size(msg, elems, num_elems);
+       if (size > SOF_IPC_MSG_MAX_SIZE)
+               return -EINVAL;
+
+       msg = kmalloc(size, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->hdr.size = size;
+       msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
+       msg->elem_cnt = num_elems;
+       memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
+
+       ret = pm_runtime_get_sync(sdev->dev);
+       if (ret < 0 && ret != -EACCES) {
+               pm_runtime_put_noidle(sdev->dev);
+               dev_err(sdev->dev, "enabling device failed: %d\n", ret);
+               goto error;
+       }
+       ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
+       pm_runtime_mark_last_busy(sdev->dev);
+       pm_runtime_put_autosuspend(sdev->dev);
+
+error:
+       kfree(msg);
+       return ret ? ret : reply.error;
+}
+
+static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
+                                          size_t count, loff_t *ppos)
+{
+       struct snd_sof_dfsentry *dfse = file->private_data;
+       struct sof_ipc_trace_filter_elem *elems = NULL;
+       struct snd_sof_dev *sdev = dfse->sdev;
+       loff_t pos = 0;
+       int num_elems;
+       char *string;
+       int ret;
+
+       if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
+               dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
+                       TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
+               return -EINVAL;
+       }
+
+       string = kmalloc(count + 1, GFP_KERNEL);
+       if (!string)
+               return -ENOMEM;
+
+       /* assert null termination */
+       string[count] = 0;
+       ret = simple_write_to_buffer(string, count, &pos, from, count);
+       if (ret < 0)
+               goto error;
+
+       ret = trace_filter_parse(sdev, string, &num_elems, &elems);
+       if (ret < 0)
+               goto error;
+
+       if (num_elems) {
+               ret = ipc3_trace_update_filter(sdev, num_elems, elems);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "Filter update failed: %d\n", ret);
+                       goto error;
+               }
+       }
+       ret = count;
+error:
+       kfree(string);
+       kfree(elems);
+       return ret;
+}
+
+static const struct file_operations sof_dfs_trace_filter_fops = {
+       .open = simple_open,
+       .write = dfsentry_trace_filter_write,
+       .llseek = default_llseek,
+};
+
+static int debugfs_create_trace_filter(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_dfsentry *dfse;
+
+       dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+       if (!dfse)
+               return -ENOMEM;
+
+       dfse->sdev = sdev;
+       dfse->type = SOF_DFSENTRY_TYPE_BUF;
+
+       debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
+                           &sof_dfs_trace_filter_fops);
+       /* add to dfsentry list */
+       list_add(&dfse->list, &sdev->dfsentry_list);
+
+       return 0;
+}
+
+static size_t sof_dtrace_avail(struct snd_sof_dev *sdev,
+                              loff_t pos, size_t buffer_size)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+       loff_t host_offset = READ_ONCE(priv->host_offset);
+
+       /*
+        * If host offset is less than local pos, it means write pointer of
+        * host DMA buffer has been wrapped. We should output the trace data
+        * at the end of host DMA buffer at first.
+        */
+       if (host_offset < pos)
+               return buffer_size - pos;
+
+       /* If there is available trace data now, it is unnecessary to wait. */
+       if (host_offset > pos)
+               return host_offset - pos;
+
+       return 0;
+}
+
+static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos,
+                                   size_t buffer_size)
+{
+       size_t ret = sof_dtrace_avail(sdev, pos, buffer_size);
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+       wait_queue_entry_t wait;
+
+       /* data immediately available */
+       if (ret)
+               return ret;
+
+       if (priv->dtrace_state != SOF_DTRACE_ENABLED && priv->dtrace_draining) {
+               /*
+                * tracing has ended and all traces have been
+                * read by client, return EOF
+                */
+               priv->dtrace_draining = false;
+               return 0;
+       }
+
+       /* wait for available trace data from FW */
+       init_waitqueue_entry(&wait, current);
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&priv->trace_sleep, &wait);
+
+       if (!signal_pending(current)) {
+               /* set timeout to max value, no error code */
+               schedule_timeout(MAX_SCHEDULE_TIMEOUT);
+       }
+       remove_wait_queue(&priv->trace_sleep, &wait);
+
+       return sof_dtrace_avail(sdev, pos, buffer_size);
+}
+
+static ssize_t dfsentry_dtrace_read(struct file *file, 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_dtrace_priv *priv = sdev->fw_trace_data;
+       unsigned long rem;
+       loff_t lpos = *ppos;
+       size_t avail, buffer_size = dfse->size;
+       u64 lpos_64;
+
+       /* make sure we know about any failures on the DSP side */
+       priv->dtrace_error = false;
+
+       /* check pos and count */
+       if (lpos < 0)
+               return -EINVAL;
+       if (!count)
+               return 0;
+
+       /* check for buffer wrap and count overflow */
+       lpos_64 = lpos;
+       lpos = do_div(lpos_64, buffer_size);
+
+       /* get available count based on current host offset */
+       avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size);
+       if (priv->dtrace_error) {
+               dev_err(sdev->dev, "trace IO error\n");
+               return -EIO;
+       }
+
+       /* make sure count is <= avail */
+       if (count > avail)
+               count = avail;
+
+       /*
+        * make sure that all trace data is available for the CPU as the trace
+        * data buffer might be allocated from non consistent memory.
+        * Note: snd_dma_buffer_sync() is called for normal audio playback and
+        *       capture streams also.
+        */
+       snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU);
+       /* copy available trace data to debugfs */
+       rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
+       if (rem)
+               return -EFAULT;
+
+       *ppos += count;
+
+       /* move debugfs reading position */
+       return count;
+}
+
+static int dfsentry_dtrace_release(struct inode *inode, struct file *file)
+{
+       struct snd_sof_dfsentry *dfse = inode->i_private;
+       struct snd_sof_dev *sdev = dfse->sdev;
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+       /* avoid duplicate traces at next open */
+       if (priv->dtrace_state != SOF_DTRACE_ENABLED)
+               priv->host_offset = 0;
+
+       return 0;
+}
+
+static const struct file_operations sof_dfs_dtrace_fops = {
+       .open = simple_open,
+       .read = dfsentry_dtrace_read,
+       .llseek = default_llseek,
+       .release = dfsentry_dtrace_release,
+};
+
+static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
+{
+       struct sof_dtrace_priv *priv;
+       struct snd_sof_dfsentry *dfse;
+       int ret;
+
+       if (!sdev)
+               return -EINVAL;
+
+       priv = sdev->fw_trace_data;
+
+       ret = debugfs_create_trace_filter(sdev);
+       if (ret < 0)
+               dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret);
+
+       dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+       if (!dfse)
+               return -ENOMEM;
+
+       dfse->type = SOF_DFSENTRY_TYPE_BUF;
+       dfse->buf = priv->dmatb.area;
+       dfse->size = priv->dmatb.bytes;
+       dfse->sdev = sdev;
+
+       debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
+                           &sof_dfs_dtrace_fops);
+
+       return 0;
+}
+
+static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+       struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+       struct sof_ipc_fw_version *v = &ready->version;
+       struct sof_ipc_dma_trace_params_ext params;
+       struct sof_ipc_reply ipc_reply;
+       int ret;
+
+       if (!sdev->fw_trace_is_supported)
+               return 0;
+
+       if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages)
+               return -EINVAL;
+
+       if (priv->dtrace_state == SOF_DTRACE_STOPPED)
+               goto start;
+
+       /* set IPC parameters */
+       params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
+       /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
+       if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
+               params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
+               params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
+               params.timestamp_ns = ktime_get(); /* in nanosecond */
+       } else {
+               params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
+               params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
+       }
+       params.buffer.phy_addr = priv->dmatp.addr;
+       params.buffer.size = priv->dmatb.bytes;
+       params.buffer.pages = priv->dma_trace_pages;
+       params.stream_tag = 0;
+
+       priv->host_offset = 0;
+       priv->dtrace_draining = false;
+
+       ret = sof_dtrace_host_init(sdev, &priv->dmatb, &params);
+       if (ret < 0) {
+               dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret);
+               return ret;
+       }
+       dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
+
+       /* send IPC to the DSP */
+       ret = sof_ipc_tx_message(sdev->ipc, &params, sizeof(params), &ipc_reply, sizeof(ipc_reply));
+       if (ret < 0) {
+               dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
+               goto trace_release;
+       }
+
+start:
+       ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START);
+       if (ret < 0) {
+               dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret);
+               goto trace_release;
+       }
+
+       priv->dtrace_state = SOF_DTRACE_ENABLED;
+
+       return 0;
+
+trace_release:
+       sof_dtrace_host_release(sdev);
+       return ret;
+}
+
+static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
+{
+       struct sof_dtrace_priv *priv;
+       int ret;
+
+       /* dtrace is only supported with SOF_IPC */
+       if (sdev->pdata->ipc_type != SOF_IPC)
+               return -EOPNOTSUPP;
+
+       if (sdev->fw_trace_data) {
+               dev_err(sdev->dev, "fw_trace_data has been already allocated\n");
+               return -EBUSY;
+       }
+
+       priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       sdev->fw_trace_data = priv;
+
+       /* set false before start initialization */
+       priv->dtrace_state = SOF_DTRACE_DISABLED;
+
+       /* allocate trace page table buffer */
+       ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
+                                 PAGE_SIZE, &priv->dmatp);
+       if (ret < 0) {
+               dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret);
+               return ret;
+       }
+
+       /* allocate trace data buffer */
+       ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
+                                     DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
+                                     &priv->dmatb);
+       if (ret < 0) {
+               dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret);
+               goto page_err;
+       }
+
+       /* create compressed page table for audio firmware */
+       ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb,
+                                       priv->dmatp.area, priv->dmatb.bytes);
+       if (ret < 0)
+               goto table_err;
+
+       priv->dma_trace_pages = ret;
+       dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__,
+               priv->dma_trace_pages);
+
+       if (sdev->first_boot) {
+               ret = debugfs_create_dtrace(sdev);
+               if (ret < 0)
+                       goto table_err;
+       }
+
+       init_waitqueue_head(&priv->trace_sleep);
+
+       ret = ipc3_dtrace_enable(sdev);
+       if (ret < 0)
+               goto table_err;
+
+       return 0;
+table_err:
+       priv->dma_trace_pages = 0;
+       snd_dma_free_pages(&priv->dmatb);
+page_err:
+       snd_dma_free_pages(&priv->dmatp);
+       return ret;
+}
+
+int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
+                           struct sof_ipc_dma_trace_posn *posn)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+       if (!sdev->fw_trace_is_supported)
+               return 0;
+
+       if (priv->dtrace_state == SOF_DTRACE_ENABLED &&
+           priv->host_offset != posn->host_offset) {
+               priv->host_offset = posn->host_offset;
+               wake_up(&priv->trace_sleep);
+       }
+
+       if (posn->overflow != 0)
+               dev_err(sdev->dev,
+                       "DSP trace buffer overflow %u bytes. Total messages %d\n",
+                       posn->overflow, posn->messages);
+
+       return 0;
+}
+
+/* an error has occurred within the DSP that prevents further trace */
+static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+       if (priv->dtrace_state == SOF_DTRACE_ENABLED) {
+               priv->dtrace_error = true;
+               wake_up(&priv->trace_sleep);
+       }
+}
+
+static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+       struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+       struct sof_ipc_fw_version *v = &ready->version;
+       struct sof_ipc_cmd_hdr hdr;
+       struct sof_ipc_reply ipc_reply;
+       int ret;
+
+       if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
+               return;
+
+       ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
+       if (ret < 0)
+               dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret);
+       priv->dtrace_state = SOF_DTRACE_STOPPED;
+
+       /*
+        * 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, hdr.size,
+                                        &ipc_reply, sizeof(ipc_reply));
+               if (ret < 0)
+                       dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
+       }
+
+       if (only_stop)
+               goto out;
+
+       ret = sof_dtrace_host_release(sdev);
+       if (ret < 0)
+               dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
+
+       priv->dtrace_state = SOF_DTRACE_DISABLED;
+
+out:
+       priv->dtrace_draining = true;
+       wake_up(&priv->trace_sleep);
+}
+
+static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
+{
+       ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0);
+}
+
+static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
+{
+       return ipc3_dtrace_enable(sdev);
+}
+
+static void ipc3_dtrace_free(struct snd_sof_dev *sdev)
+{
+       struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+       /* release trace */
+       ipc3_dtrace_release(sdev, false);
+
+       if (priv->dma_trace_pages) {
+               snd_dma_free_pages(&priv->dmatb);
+               snd_dma_free_pages(&priv->dmatp);
+               priv->dma_trace_pages = 0;
+       }
+}
+
+const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = {
+       .init = ipc3_dtrace_init,
+       .free = ipc3_dtrace_free,
+       .fw_crashed = ipc3_dtrace_fw_crashed,
+       .suspend = ipc3_dtrace_suspend,
+       .resume = ipc3_dtrace_resume,
+};
diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c
new file mode 100644 (file)
index 0000000..f3c741b
--- /dev/null
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license.  When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <linux/firmware.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ipc3-priv.h"
+#include "ops.h"
+
+static int ipc3_fw_ext_man_get_version(struct snd_sof_dev *sdev,
+                                      const struct sof_ext_man_elem_header *hdr)
+{
+       const struct sof_ext_man_fw_version *v =
+               container_of(hdr, struct sof_ext_man_fw_version, hdr);
+
+       memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
+       sdev->fw_ready.flags = v->flags;
+
+       /* log ABI versions and check FW compatibility */
+       return sof_ipc3_validate_fw_version(sdev);
+}
+
+static int ipc3_fw_ext_man_get_windows(struct snd_sof_dev *sdev,
+                                      const struct sof_ext_man_elem_header *hdr)
+{
+       const struct sof_ext_man_window *w;
+
+       w = container_of(hdr, struct sof_ext_man_window, hdr);
+
+       return sof_ipc3_get_ext_windows(sdev, &w->ipc_window.ext_hdr);
+}
+
+static int ipc3_fw_ext_man_get_cc_info(struct snd_sof_dev *sdev,
+                                      const struct sof_ext_man_elem_header *hdr)
+{
+       const struct sof_ext_man_cc_version *cc;
+
+       cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
+
+       return sof_ipc3_get_cc_info(sdev, &cc->cc_version.ext_hdr);
+}
+
+static int ipc3_fw_ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
+                                           const struct sof_ext_man_elem_header *hdr)
+{
+       const struct ext_man_dbg_abi *dbg_abi =
+               container_of(hdr, struct ext_man_dbg_abi, hdr);
+
+       if (sdev->first_boot)
+               dev_dbg(sdev->dev,
+                       "Firmware: DBG_ABI %d:%d:%d\n",
+                       SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
+                       SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
+                       SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
+
+       return 0;
+}
+
+static int ipc3_fw_ext_man_get_config_data(struct snd_sof_dev *sdev,
+                                          const struct sof_ext_man_elem_header *hdr)
+{
+       const struct sof_ext_man_config_data *config =
+               container_of(hdr, struct sof_ext_man_config_data, hdr);
+       const struct sof_config_elem *elem;
+       int elems_counter;
+       int elems_size;
+       int ret = 0;
+       int i;
+
+       /* calculate elements counter */
+       elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
+       elems_counter = elems_size / sizeof(struct sof_config_elem);
+
+       dev_dbg(sdev->dev, "%s can hold up to %d config elements\n",
+               __func__, elems_counter);
+
+       for (i = 0; i < elems_counter; ++i) {
+               elem = &config->elems[i];
+               dev_dbg(sdev->dev, "%s get index %d token %d val %d\n",
+                       __func__, i, elem->token, elem->value);
+               switch (elem->token) {
+               case SOF_EXT_MAN_CONFIG_EMPTY:
+                       /* unused memory space is zero filled - mapped to EMPTY elements */
+                       break;
+               case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
+                       /* TODO: use ipc msg size from config data */
+                       break;
+               case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
+                       if (sdev->first_boot && elem->value)
+                               ret = snd_sof_dbg_memory_info_init(sdev);
+                       break;
+               default:
+                       dev_info(sdev->dev,
+                                "Unknown firmware configuration token %d value %d",
+                                elem->token, elem->value);
+                       break;
+               }
+               if (ret < 0) {
+                       dev_err(sdev->dev,
+                               "%s: processing failed for token %d value %#x, %d\n",
+                               __func__, elem->token, elem->value, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static ssize_t ipc3_fw_ext_man_size(const struct firmware *fw)
+{
+       const struct sof_ext_man_header *head;
+
+       head = (struct sof_ext_man_header *)fw->data;
+
+       /*
+        * assert fw size is big enough to contain extended manifest header,
+        * it prevents from reading unallocated memory from `head` in following
+        * step.
+        */
+       if (fw->size < sizeof(*head))
+               return -EINVAL;
+
+       /*
+        * When fw points to extended manifest,
+        * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
+        */
+       if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
+               return head->full_size;
+
+       /* otherwise given fw don't have an extended manifest */
+       return 0;
+}
+
+static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       const struct firmware *fw = plat_data->fw;
+       const struct sof_ext_man_elem_header *elem_hdr;
+       const struct sof_ext_man_header *head;
+       ssize_t ext_man_size;
+       ssize_t remaining;
+       uintptr_t iptr;
+       int ret = 0;
+
+       head = (struct sof_ext_man_header *)fw->data;
+       remaining = head->full_size - head->header_size;
+       ext_man_size = ipc3_fw_ext_man_size(fw);
+
+       /* Assert firmware starts with extended manifest */
+       if (ext_man_size <= 0)
+               return ext_man_size;
+
+       /* incompatible version */
+       if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
+                                            head->header_version)) {
+               dev_err(sdev->dev,
+                       "extended manifest version %#x differ from used %#x\n",
+                       head->header_version, SOF_EXT_MAN_VERSION);
+               return -EINVAL;
+       }
+
+       /* get first extended manifest element header */
+       iptr = (uintptr_t)fw->data + head->header_size;
+
+       while (remaining > sizeof(*elem_hdr)) {
+               elem_hdr = (struct sof_ext_man_elem_header *)iptr;
+
+               dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n",
+                       elem_hdr->type, elem_hdr->size);
+
+               if (elem_hdr->size < sizeof(*elem_hdr) ||
+                   elem_hdr->size > remaining) {
+                       dev_err(sdev->dev,
+                               "invalid sof_ext_man header size, type %d size %#x\n",
+                               elem_hdr->type, elem_hdr->size);
+                       return -EINVAL;
+               }
+
+               /* process structure data */
+               switch (elem_hdr->type) {
+               case SOF_EXT_MAN_ELEM_FW_VERSION:
+                       ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr);
+                       break;
+               case SOF_EXT_MAN_ELEM_WINDOW:
+                       ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr);
+                       break;
+               case SOF_EXT_MAN_ELEM_CC_VERSION:
+                       ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr);
+                       break;
+               case SOF_EXT_MAN_ELEM_DBG_ABI:
+                       ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr);
+                       break;
+               case SOF_EXT_MAN_ELEM_CONFIG_DATA:
+                       ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr);
+                       break;
+               case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
+                       ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
+                       break;
+               default:
+                       dev_info(sdev->dev,
+                                "unknown sof_ext_man header type %d size %#x\n",
+                                elem_hdr->type, elem_hdr->size);
+                       break;
+               }
+
+               if (ret < 0) {
+                       dev_err(sdev->dev,
+                               "failed to parse sof_ext_man header type %d size %#x\n",
+                               elem_hdr->type, elem_hdr->size);
+                       return ret;
+               }
+
+               remaining -= elem_hdr->size;
+               iptr += elem_hdr->size;
+       }
+
+       if (remaining) {
+               dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
+               return -EINVAL;
+       }
+
+       return ext_man_size;
+}
+
+/* generic module parser for mmaped DSPs */
+static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
+                                       struct snd_sof_mod_hdr *module)
+{
+       struct snd_sof_blk_hdr *block;
+       int count, ret;
+       u32 offset;
+       size_t remaining;
+
+       dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n",
+               module->size, module->num_blocks, module->type);
+
+       block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
+
+       /* module->size doesn't include header size */
+       remaining = module->size;
+       for (count = 0; count < module->num_blocks; count++) {
+               /* check for wrap */
+               if (remaining < sizeof(*block)) {
+                       dev_err(sdev->dev, "not enough data remaining\n");
+                       return -EINVAL;
+               }
+
+               /* minus header size of block */
+               remaining -= sizeof(*block);
+
+               if (block->size == 0) {
+                       dev_warn(sdev->dev,
+                                "warning: block %d size zero\n", count);
+                       dev_warn(sdev->dev, " type %#x offset %#x\n",
+                                block->type, block->offset);
+                       continue;
+               }
+
+               switch (block->type) {
+               case SOF_FW_BLK_TYPE_RSRVD0:
+               case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
+                       continue;       /* not handled atm */
+               case SOF_FW_BLK_TYPE_IRAM:
+               case SOF_FW_BLK_TYPE_DRAM:
+               case SOF_FW_BLK_TYPE_SRAM:
+                       offset = block->offset;
+                       break;
+               default:
+                       dev_err(sdev->dev, "%s: bad type %#x for block %#x\n",
+                               __func__, block->type, count);
+                       return -EINVAL;
+               }
+
+               dev_dbg(sdev->dev, "block %d type %#x size %#x ==>  offset %#x\n",
+                       count, block->type, block->size, offset);
+
+               /* checking block->size to avoid unaligned access */
+               if (block->size % sizeof(u32)) {
+                       dev_err(sdev->dev, "%s: invalid block size %#x\n",
+                               __func__, block->size);
+                       return -EINVAL;
+               }
+               ret = snd_sof_dsp_block_write(sdev, block->type, offset,
+                                             block + 1, block->size);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "%s: write to block type %#x failed\n",
+                               __func__, block->type);
+                       return ret;
+               }
+
+               if (remaining < block->size) {
+                       dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
+                       return -EINVAL;
+               }
+
+               /* minus body size of block */
+               remaining -= block->size;
+               /* next block */
+               block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
+                       + block->size);
+       }
+
+       return 0;
+}
+
+static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       const struct firmware *fw = plat_data->fw;
+       struct snd_sof_fw_header *header;
+       struct snd_sof_mod_hdr *module;
+       int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr);
+       size_t remaining;
+       int ret, count;
+
+       if (!plat_data->fw)
+               return -EINVAL;
+
+       header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
+       load_module = sof_ops(sdev)->load_module;
+       if (!load_module) {
+               dev_dbg(sdev->dev, "%s: Using generic module loading\n", __func__);
+               load_module = sof_ipc3_parse_module_memcpy;
+       } else {
+               dev_dbg(sdev->dev, "%s: Using custom module loading\n", __func__);
+       }
+
+       /* parse each module */
+       module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset +
+                                           sizeof(*header));
+       remaining = fw->size - sizeof(*header) - plat_data->fw_offset;
+       /* check for wrap */
+       if (remaining > fw->size) {
+               dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__);
+               return -EINVAL;
+       }
+
+       for (count = 0; count < header->num_modules; count++) {
+               /* check for wrap */
+               if (remaining < sizeof(*module)) {
+                       dev_err(sdev->dev, "%s: not enough data for a module\n",
+                               __func__);
+                       return -EINVAL;
+               }
+
+               /* minus header size of module */
+               remaining -= sizeof(*module);
+
+               /* module */
+               ret = load_module(sdev, module);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count);
+                       return ret;
+               }
+
+               if (remaining < module->size) {
+                       dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
+                       return -EINVAL;
+               }
+
+               /* minus body size of module */
+               remaining -=  module->size;
+               module = (struct snd_sof_mod_hdr *)((u8 *)module +
+                        sizeof(*module) + module->size);
+       }
+
+       return 0;
+}
+
+static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       const struct firmware *fw = plat_data->fw;
+       struct snd_sof_fw_header *header;
+       size_t fw_size = fw->size - plat_data->fw_offset;
+
+       if (fw->size <= plat_data->fw_offset) {
+               dev_err(sdev->dev,
+                       "firmware size must be greater than firmware offset\n");
+               return -EINVAL;
+       }
+
+       /* Read the header information from the data pointer */
+       header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
+
+       /* verify FW sig */
+       if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
+               dev_err(sdev->dev, "invalid firmware signature\n");
+               return -EINVAL;
+       }
+
+       /* check size is valid */
+       if (fw_size != header->file_size + sizeof(*header)) {
+               dev_err(sdev->dev,
+                       "invalid filesize mismatch got 0x%zx expected 0x%zx\n",
+                       fw_size, header->file_size + sizeof(*header));
+               return -EINVAL;
+       }
+
+       dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
+               header->file_size, header->num_modules,
+               header->abi, sizeof(*header));
+
+       return 0;
+}
+
+const struct sof_ipc_fw_loader_ops ipc3_loader_ops = {
+       .validate = sof_ipc3_validate_firmware,
+       .parse_ext_manifest = sof_ipc3_fw_parse_ext_man,
+       .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp,
+};
diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h
deleted file mode 100644 (file)
index a478462..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
-/*
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * Copyright(c) 2021 Intel Corporation. All rights reserved.
- *
- * Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
-
-#ifndef __SOUND_SOC_SOF_IPC3_OPS_H
-#define __SOUND_SOC_SOF_IPC3_OPS_H
-
-#include "sof-priv.h"
-
-extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
-extern const struct sof_ipc_ops ipc3_ops;
-extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
-extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
-
-#endif
index 58b7594..c8774a8 100644 (file)
@@ -8,7 +8,7 @@
 //
 
 #include <sound/pcm_params.h>
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
 #include "ops.h"
 #include "sof-priv.h"
 #include "sof-audio.h"
@@ -34,8 +34,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
        stream.comp_id = spcm->stream[substream->stream].comp_id;
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
-                                 sizeof(stream), &reply, sizeof(reply));
+       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
 }
 
 static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
@@ -119,7 +118,7 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
        dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
 
        /* send hw_params IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+       ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
                                 &ipc_params_reply, sizeof(ipc_params_reply));
        if (ret < 0) {
                dev_err(component->dev, "HW params ipc failed for stream %d\n",
@@ -175,8 +174,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
        }
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
-                                 sizeof(stream), &reply, sizeof(reply));
+       return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
 }
 
 static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h
new file mode 100644 (file)
index 0000000..f504420
--- /dev/null
@@ -0,0 +1,65 @@
+/* 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 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __SOUND_SOC_SOF_IPC3_PRIV_H
+#define __SOUND_SOC_SOF_IPC3_PRIV_H
+
+#include "sof-priv.h"
+
+/* IPC3 specific ops */
+extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
+extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
+extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
+extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops;
+extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops;
+
+/* helpers for fw_ready and ext_manifest parsing */
+int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
+                            const struct sof_ipc_ext_data_hdr *ext_hdr);
+int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev,
+                        const struct sof_ipc_ext_data_hdr *ext_hdr);
+int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev);
+
+/* dtrace position update */
+int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
+                           struct sof_ipc_dma_trace_posn *posn);
+
+/* dtrace platform callback wrappers */
+static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev,
+                                      struct snd_dma_buffer *dmatb,
+                                      struct sof_ipc_dma_trace_params_ext *dtrace_params)
+{
+       struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+       if (dsp_ops->trace_init)
+               return dsp_ops->trace_init(sdev, dmatb, dtrace_params);
+
+       return 0;
+}
+
+static inline int sof_dtrace_host_release(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+       if (dsp_ops->trace_release)
+               return dsp_ops->trace_release(sdev);
+
+       return 0;
+}
+
+static inline int sof_dtrace_host_trigger(struct snd_sof_dev *sdev, int cmd)
+{
+       struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+       if (dsp_ops->trace_trigger)
+               return dsp_ops->trace_trigger(sdev, cmd);
+
+       return 0;
+}
+
+#endif
index 2f8450a..043554d 100644 (file)
@@ -11,7 +11,7 @@
 #include <sound/pcm_params.h>
 #include "sof-priv.h"
 #include "sof-audio.h"
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
 #include "ops.h"
 
 /* Full volume for default values */
@@ -20,7 +20,8 @@
 struct sof_widget_data {
        int ctrl_type;
        int ipc_cmd;
-       struct sof_abi_hdr *pdata;
+       void *pdata;
+       size_t pdata_size;
        struct snd_sof_control *control;
 };
 
@@ -784,16 +785,26 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
                }
 
                cdata = wdata[i].control->ipc_control_data;
-               wdata[i].pdata = cdata->data;
-               if (!wdata[i].pdata)
-                       return -EINVAL;
 
-               /* make sure data is valid - data can be updated at runtime */
-               if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES &&
-                   wdata[i].pdata->magic != SOF_ABI_MAGIC)
-                       return -EINVAL;
+               if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) {
+                       /* make sure data is valid - data can be updated at runtime */
+                       if (cdata->data->magic != SOF_ABI_MAGIC)
+                               return -EINVAL;
+
+                       wdata[i].pdata = cdata->data->data;
+                       wdata[i].pdata_size = cdata->data->size;
+               } else {
+                       /* points to the control data union */
+                       wdata[i].pdata = cdata->chanv;
+                       /*
+                        * wdata[i].control->size is calculated with struct_size
+                        * and includes the size of struct sof_ipc_ctrl_data
+                        */
+                       wdata[i].pdata_size = wdata[i].control->size -
+                                             sizeof(struct sof_ipc_ctrl_data);
+               }
 
-               *size += wdata[i].pdata->size;
+               *size += wdata[i].pdata_size;
 
                /* get data type */
                switch (cdata->cmd) {
@@ -876,10 +887,12 @@ static int sof_process_load(struct snd_soc_component *scomp,
         */
        if (ipc_data_size) {
                for (i = 0; i < widget->num_kcontrols; i++) {
-                       memcpy(&process->data[offset],
-                              wdata[i].pdata->data,
-                              wdata[i].pdata->size);
-                       offset += wdata[i].pdata->size;
+                       if (!wdata[i].pdata_size)
+                               continue;
+
+                       memcpy(&process->data[offset], wdata[i].pdata,
+                              wdata[i].pdata_size);
+                       offset += wdata[i].pdata_size;
                }
        }
 
@@ -1551,8 +1564,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
                sroute->sink_widget->widget->name);
 
        /* send ipc */
-       ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect),
-                                &reply, sizeof(reply));
+       ret = sof_ipc_tx_message(sdev->ipc, &connect, sizeof(connect), &reply, sizeof(reply));
        if (ret < 0)
                dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,
                        sroute->src_widget->widget->name, sroute->sink_widget->widget->name);
@@ -1592,6 +1604,7 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
        if (scontrol->priv_size > 0) {
                memcpy(cdata->data, scontrol->priv, scontrol->priv_size);
                kfree(scontrol->priv);
+               scontrol->priv = NULL;
 
                if (cdata->data->magic != SOF_ABI_MAGIC) {
                        dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);
@@ -1697,7 +1710,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro
        fcomp.id = scontrol->comp_id;
 
        /* send IPC to the DSP */
-       return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0);
+       return sof_ipc_tx_message(sdev->ipc, &fcomp, sizeof(fcomp), NULL, 0);
 }
 
 /* send pcm params ipc */
@@ -1749,7 +1762,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
        }
 
        /* send IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+       ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
                                 &ipc_params_reply, sizeof(ipc_params_reply));
        if (ret < 0)
                dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
@@ -1773,8 +1786,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
        stream.comp_id = swidget->comp_id;
 
        /* send IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
-                                sizeof(stream), &reply, sizeof(reply));
+       ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
        if (ret < 0)
                dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
 
@@ -1902,8 +1914,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w
        ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
        ready.comp_id = swidget->comp_id;
 
-       ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply,
-                                sizeof(reply));
+       ret = sof_ipc_tx_message(sdev->ipc, &ready, sizeof(ready), &reply, sizeof(reply));
        if (ret < 0)
                return ret;
 
@@ -1939,7 +1950,7 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
                break;
        }
 
-       ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+       ret = sof_ipc_tx_message(sdev->ipc, &ipc_free, sizeof(ipc_free),
                                 &reply, sizeof(reply));
        if (ret < 0)
                dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);
@@ -2003,7 +2014,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
 
        /* only send the IPC if the widget is set up in the DSP */
        if (swidget->use_count > 0) {
-               ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+               ret = sof_ipc_tx_message(sdev->ipc, config, config->hdr.size,
                                         &reply, sizeof(reply));
                if (ret < 0)
                        dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);
@@ -2028,7 +2039,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_dai_private_data *dai_data = dai->private;
                struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;
 
-               ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai,
+               ret = sof_ipc_tx_message(sdev->ipc, dai_data->comp_dai,
                                         comp->hdr.size, &reply, sizeof(reply));
                break;
        }
@@ -2037,8 +2048,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_ipc_pipe_new *pipeline;
 
                pipeline = swidget->private;
-               ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
-                                        sizeof(*pipeline), &reply, sizeof(reply));
+               ret = sof_ipc_tx_message(sdev->ipc, pipeline, sizeof(*pipeline),
+                                        &reply, sizeof(reply));
                break;
        }
        default:
@@ -2046,7 +2057,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
                struct sof_ipc_cmd_hdr *hdr;
 
                hdr = swidget->private;
-               ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
+               ret = sof_ipc_tx_message(sdev->ipc, swidget->private, hdr->size,
                                         &reply, sizeof(reply));
                break;
        }
@@ -2249,6 +2260,18 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
        list_for_each_entry(sroute, &sdev->route_list, list)
                sroute->setup = false;
 
+       /*
+        * before suspending, make sure the refcounts are all zeroed out. There's no way
+        * to recover at this point but this will help root cause bad sequences leading to
+        * more issues on resume
+        */
+       list_for_each_entry(swidget, &sdev->widget_list, list) {
+               if (swidget->use_count != 0) {
+                       dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n",
+                               __func__, swidget->widget->name, swidget->use_count);
+               }
+       }
+
        return 0;
 }
 
index 03e914b..dff5fea 100644 (file)
 //
 //
 
+#include <sound/sof/stream.h>
+#include <sound/sof/control.h>
 #include "sof-priv.h"
-#include "ipc3-ops.h"
+#include "sof-audio.h"
+#include "ipc3-priv.h"
+#include "ops.h"
+
+typedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
+static void ipc3_log_header(struct device *dev, u8 *text, u32 cmd)
+{
+       u8 *str;
+       u8 *str2 = NULL;
+       u32 glb;
+       u32 type;
+       bool vdbg = false;
+
+       glb = cmd & SOF_GLB_TYPE_MASK;
+       type = cmd & SOF_CMD_TYPE_MASK;
+
+       switch (glb) {
+       case SOF_IPC_GLB_REPLY:
+               str = "GLB_REPLY"; break;
+       case SOF_IPC_GLB_COMPOUND:
+               str = "GLB_COMPOUND"; break;
+       case SOF_IPC_GLB_TPLG_MSG:
+               str = "GLB_TPLG_MSG";
+               switch (type) {
+               case SOF_IPC_TPLG_COMP_NEW:
+                       str2 = "COMP_NEW"; break;
+               case SOF_IPC_TPLG_COMP_FREE:
+                       str2 = "COMP_FREE"; break;
+               case SOF_IPC_TPLG_COMP_CONNECT:
+                       str2 = "COMP_CONNECT"; break;
+               case SOF_IPC_TPLG_PIPE_NEW:
+                       str2 = "PIPE_NEW"; break;
+               case SOF_IPC_TPLG_PIPE_FREE:
+                       str2 = "PIPE_FREE"; break;
+               case SOF_IPC_TPLG_PIPE_CONNECT:
+                       str2 = "PIPE_CONNECT"; break;
+               case SOF_IPC_TPLG_PIPE_COMPLETE:
+                       str2 = "PIPE_COMPLETE"; break;
+               case SOF_IPC_TPLG_BUFFER_NEW:
+                       str2 = "BUFFER_NEW"; break;
+               case SOF_IPC_TPLG_BUFFER_FREE:
+                       str2 = "BUFFER_FREE"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_PM_MSG:
+               str = "GLB_PM_MSG";
+               switch (type) {
+               case SOF_IPC_PM_CTX_SAVE:
+                       str2 = "CTX_SAVE"; break;
+               case SOF_IPC_PM_CTX_RESTORE:
+                       str2 = "CTX_RESTORE"; break;
+               case SOF_IPC_PM_CTX_SIZE:
+                       str2 = "CTX_SIZE"; break;
+               case SOF_IPC_PM_CLK_SET:
+                       str2 = "CLK_SET"; break;
+               case SOF_IPC_PM_CLK_GET:
+                       str2 = "CLK_GET"; break;
+               case SOF_IPC_PM_CLK_REQ:
+                       str2 = "CLK_REQ"; break;
+               case SOF_IPC_PM_CORE_ENABLE:
+                       str2 = "CORE_ENABLE"; break;
+               case SOF_IPC_PM_GATE:
+                       str2 = "GATE"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_COMP_MSG:
+               str = "GLB_COMP_MSG";
+               switch (type) {
+               case SOF_IPC_COMP_SET_VALUE:
+                       str2 = "SET_VALUE"; break;
+               case SOF_IPC_COMP_GET_VALUE:
+                       str2 = "GET_VALUE"; break;
+               case SOF_IPC_COMP_SET_DATA:
+                       str2 = "SET_DATA"; break;
+               case SOF_IPC_COMP_GET_DATA:
+                       str2 = "GET_DATA"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_STREAM_MSG:
+               str = "GLB_STREAM_MSG";
+               switch (type) {
+               case SOF_IPC_STREAM_PCM_PARAMS:
+                       str2 = "PCM_PARAMS"; break;
+               case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
+                       str2 = "PCM_REPLY"; break;
+               case SOF_IPC_STREAM_PCM_FREE:
+                       str2 = "PCM_FREE"; break;
+               case SOF_IPC_STREAM_TRIG_START:
+                       str2 = "TRIG_START"; break;
+               case SOF_IPC_STREAM_TRIG_STOP:
+                       str2 = "TRIG_STOP"; break;
+               case SOF_IPC_STREAM_TRIG_PAUSE:
+                       str2 = "TRIG_PAUSE"; break;
+               case SOF_IPC_STREAM_TRIG_RELEASE:
+                       str2 = "TRIG_RELEASE"; break;
+               case SOF_IPC_STREAM_TRIG_DRAIN:
+                       str2 = "TRIG_DRAIN"; break;
+               case SOF_IPC_STREAM_TRIG_XRUN:
+                       str2 = "TRIG_XRUN"; break;
+               case SOF_IPC_STREAM_POSITION:
+                       vdbg = true;
+                       str2 = "POSITION"; break;
+               case SOF_IPC_STREAM_VORBIS_PARAMS:
+                       str2 = "VORBIS_PARAMS"; break;
+               case SOF_IPC_STREAM_VORBIS_FREE:
+                       str2 = "VORBIS_FREE"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_FW_READY:
+               str = "FW_READY"; break;
+       case SOF_IPC_GLB_DAI_MSG:
+               str = "GLB_DAI_MSG";
+               switch (type) {
+               case SOF_IPC_DAI_CONFIG:
+                       str2 = "CONFIG"; break;
+               case SOF_IPC_DAI_LOOPBACK:
+                       str2 = "LOOPBACK"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_TRACE_MSG:
+               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) {
+               case SOF_IPC_TEST_IPC_FLOOD:
+                       str2 = "IPC_FLOOD"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_DEBUG:
+               str = "GLB_DEBUG";
+               switch (type) {
+               case SOF_IPC_DEBUG_MEM_USAGE:
+                       str2 = "MEM_USAGE"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       case SOF_IPC_GLB_PROBE:
+               str = "GLB_PROBE";
+               switch (type) {
+               case SOF_IPC_PROBE_INIT:
+                       str2 = "INIT"; break;
+               case SOF_IPC_PROBE_DEINIT:
+                       str2 = "DEINIT"; break;
+               case SOF_IPC_PROBE_DMA_ADD:
+                       str2 = "DMA_ADD"; break;
+               case SOF_IPC_PROBE_DMA_INFO:
+                       str2 = "DMA_INFO"; break;
+               case SOF_IPC_PROBE_DMA_REMOVE:
+                       str2 = "DMA_REMOVE"; break;
+               case SOF_IPC_PROBE_POINT_ADD:
+                       str2 = "POINT_ADD"; break;
+               case SOF_IPC_PROBE_POINT_INFO:
+                       str2 = "POINT_INFO"; break;
+               case SOF_IPC_PROBE_POINT_REMOVE:
+                       str2 = "POINT_REMOVE"; break;
+               default:
+                       str2 = "unknown type"; break;
+               }
+               break;
+       default:
+               str = "unknown GLB command"; break;
+       }
+
+       if (str2) {
+               if (vdbg)
+                       dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
+               else
+                       dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
+       } else {
+               dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
+       }
+}
+#else
+static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd)
+{
+       if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
+               dev_dbg(dev, "%s: 0x%x\n", text, cmd);
+}
+#endif
+
+static int sof_ipc3_get_reply(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_ipc_msg *msg = sdev->msg;
+       struct sof_ipc_reply *reply;
+       int ret = 0;
+
+       /* get the generic reply */
+       reply = msg->reply_data;
+       snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, reply, sizeof(*reply));
+
+       if (reply->error < 0)
+               return reply->error;
+
+       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");
+
+               return -EINVAL;
+       }
+
+       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
+                * and the reply->hdr.size > sizeof(struct sof_ipc_reply)
+                */
+               if (!ret && msg->reply_size > sizeof(*reply))
+                       snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset,
+                                                msg->reply_data, msg->reply_size);
+       }
+
+       return ret;
+}
+
+/* wait for IPC message reply */
+static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
+{
+       struct snd_sof_ipc_msg *msg = &ipc->msg;
+       struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
+       struct snd_sof_dev *sdev = ipc->sdev;
+       int ret;
+
+       /* wait for DSP IPC completion */
+       ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
+                                msecs_to_jiffies(sdev->ipc_timeout));
+
+       if (ret == 0) {
+               dev_err(sdev->dev,
+                       "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n",
+                       hdr->cmd, hdr->size, msg->reply_size);
+               snd_sof_handle_fw_exception(ipc->sdev);
+               ret = -ETIMEDOUT;
+       } else {
+               ret = msg->reply_error;
+               if (ret < 0) {
+                       dev_err(sdev->dev,
+                               "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n",
+                               hdr->cmd, hdr->size, msg->reply_size, ret);
+               } else {
+                       ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
+                       if (msg->reply_size)
+                               /* copy the data returned from DSP */
+                               memcpy(reply_data, msg->reply_data,
+                                      msg->reply_size);
+               }
+
+               /* re-enable dumps after successful IPC tx */
+               if (sdev->ipc_dump_printed) {
+                       sdev->dbg_dump_printed = false;
+                       sdev->ipc_dump_printed = false;
+               }
+       }
+
+       return ret;
+}
+
+/* send IPC message from host to DSP */
+static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc,
+                               void *msg_data, size_t msg_bytes,
+                               void *reply_data, size_t reply_bytes)
+{
+       struct sof_ipc_cmd_hdr *hdr = msg_data;
+       struct snd_sof_dev *sdev = ipc->sdev;
+       int ret;
+
+       ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
+
+       if (ret) {
+               dev_err_ratelimited(sdev->dev,
+                                   "%s: ipc message send for %#x failed: %d\n",
+                                   __func__, hdr->cmd, ret);
+               return ret;
+       }
+
+       ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd);
+
+       /* now wait for completion */
+       return ipc3_wait_tx_done(ipc, reply_data);
+}
+
+static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+                          void *reply_data, size_t reply_bytes, bool no_pm)
+{
+       struct snd_sof_ipc *ipc = sdev->ipc;
+       int ret;
+
+       if (!msg_data || msg_bytes < sizeof(struct sof_ipc_cmd_hdr)) {
+               dev_err_ratelimited(sdev->dev, "No IPC message to send\n");
+               return -EINVAL;
+       }
+
+       if (!no_pm) {
+               const struct sof_dsp_power_state target_state = {
+                       .state = SOF_DSP_PM_D0,
+               };
+
+               /* ensure the DSP is in D0 before sending a new IPC */
+               ret = snd_sof_dsp_set_power_state(sdev, &target_state);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "%s: resuming DSP failed: %d\n",
+                               __func__, ret);
+                       return ret;
+               }
+       }
+
+       /* Serialise IPC TX */
+       mutex_lock(&ipc->tx_mutex);
+
+       ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+
+       mutex_unlock(&ipc->tx_mutex);
+
+       return ret;
+}
+
+static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t data_bytes,
+                                bool set)
+{
+       size_t msg_bytes, hdr_bytes, payload_size, send_bytes;
+       struct sof_ipc_ctrl_data *cdata = data;
+       struct sof_ipc_ctrl_data *cdata_chunk;
+       struct snd_sof_ipc *ipc = sdev->ipc;
+       size_t offset = 0;
+       u8 *src, *dst;
+       u32 num_msg;
+       int ret = 0;
+       int i;
+
+       if (!cdata || data_bytes < sizeof(*cdata))
+               return -EINVAL;
+
+       if ((cdata->rhdr.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_COMP_MSG) {
+               dev_err(sdev->dev, "%s: Not supported message type of %#x\n",
+                       __func__, cdata->rhdr.hdr.cmd);
+               return -EINVAL;
+       }
+
+       /* send normal size ipc in one part */
+       if (cdata->rhdr.hdr.size <= ipc->max_payload_size)
+               return sof_ipc3_tx_msg(sdev, cdata, cdata->rhdr.hdr.size,
+                                      cdata, cdata->rhdr.hdr.size, false);
+
+       cdata_chunk = kzalloc(ipc->max_payload_size, GFP_KERNEL);
+       if (!cdata_chunk)
+               return -ENOMEM;
+
+       switch (cdata->type) {
+       case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+       case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+               hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
+               if (set) {
+                       src = (u8 *)cdata->chanv;
+                       dst = (u8 *)cdata_chunk->chanv;
+               } else {
+                       src = (u8 *)cdata_chunk->chanv;
+                       dst = (u8 *)cdata->chanv;
+               }
+               break;
+       case SOF_CTRL_TYPE_DATA_GET:
+       case SOF_CTRL_TYPE_DATA_SET:
+               hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr);
+               if (set) {
+                       src = (u8 *)cdata->data->data;
+                       dst = (u8 *)cdata_chunk->data->data;
+               } else {
+                       src = (u8 *)cdata_chunk->data->data;
+                       dst = (u8 *)cdata->data->data;
+               }
+               break;
+       default:
+               kfree(cdata_chunk);
+               return -EINVAL;
+       }
+
+       msg_bytes = cdata->rhdr.hdr.size - hdr_bytes;
+       payload_size = ipc->max_payload_size - hdr_bytes;
+       num_msg = DIV_ROUND_UP(msg_bytes, payload_size);
+
+       /* copy the header data */
+       memcpy(cdata_chunk, cdata, hdr_bytes);
+
+       /* Serialise IPC TX */
+       mutex_lock(&sdev->ipc->tx_mutex);
+
+       /* copy the payload data in a loop */
+       for (i = 0; i < num_msg; i++) {
+               send_bytes = min(msg_bytes, payload_size);
+               cdata_chunk->num_elems = send_bytes;
+               cdata_chunk->rhdr.hdr.size = hdr_bytes + send_bytes;
+               cdata_chunk->msg_index = i;
+               msg_bytes -= send_bytes;
+               cdata_chunk->elems_remaining = msg_bytes;
+
+               if (set)
+                       memcpy(dst, src + offset, send_bytes);
+
+               ret = ipc3_tx_msg_unlocked(sdev->ipc,
+                                          cdata_chunk, cdata_chunk->rhdr.hdr.size,
+                                          cdata_chunk, cdata_chunk->rhdr.hdr.size);
+               if (ret < 0)
+                       break;
+
+               if (!set)
+                       memcpy(dst + offset, src, send_bytes);
+
+               offset += payload_size;
+       }
+
+       mutex_unlock(&sdev->ipc->tx_mutex);
+
+       kfree(cdata_chunk);
+
+       return ret;
+}
+
+int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
+                            const struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+       const struct sof_ipc_window *w =
+               container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
+
+       if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
+               return -EINVAL;
+
+       if (sdev->info_window) {
+               if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
+                       dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox");
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       /* keep a local copy of the data */
+       sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL);
+       if (!sdev->info_window)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev,
+                        const struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+       int ret;
+
+       const struct sof_ipc_cc_version *cc =
+               container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
+
+       if (sdev->cc_version) {
+               if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
+                       dev_err(sdev->dev,
+                               "Receive diverged cc_version descriptions");
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       dev_dbg(sdev->dev,
+               "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
+               cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim);
+
+       /* create read-only cc_version debugfs to store compiler version info */
+       /* use local copy of the cc_version to prevent data corruption */
+       if (sdev->first_boot) {
+               sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
+                                               GFP_KERNEL);
+
+               if (!sdev->cc_version)
+                       return -ENOMEM;
+
+               memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
+               ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
+                                              cc->ext_hdr.hdr.size,
+                                              "cc_version", 0444);
+
+               /* errors are only due to memory allocation, not debugfs */
+               if (ret < 0) {
+                       dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/* parse the extended FW boot data structures from FW boot message */
+static int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
+{
+       struct sof_ipc_ext_data_hdr *ext_hdr;
+       void *ext_data;
+       int ret = 0;
+
+       ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!ext_data)
+               return -ENOMEM;
+
+       /* get first header */
+       snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
+                              sizeof(*ext_hdr));
+       ext_hdr = ext_data;
+
+       while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
+               /* read in ext structure */
+               snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                      offset + sizeof(*ext_hdr),
+                                      (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
+                                      ext_hdr->hdr.size - sizeof(*ext_hdr));
+
+               dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
+                       ext_hdr->type, ext_hdr->hdr.size);
+
+               /* process structure data */
+               switch (ext_hdr->type) {
+               case SOF_IPC_EXT_WINDOW:
+                       ret = sof_ipc3_get_ext_windows(sdev, ext_hdr);
+                       break;
+               case SOF_IPC_EXT_CC_INFO:
+                       ret = sof_ipc3_get_cc_info(sdev, ext_hdr);
+                       break;
+               case SOF_IPC_EXT_UNUSED:
+               case SOF_IPC_EXT_PROBE_INFO:
+               case SOF_IPC_EXT_USER_ABI_INFO:
+                       /* They are supported but we don't do anything here */
+                       break;
+               default:
+                       dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
+                                ext_hdr->type, ext_hdr->hdr.size);
+                       ret = 0;
+                       break;
+               }
+
+               if (ret < 0) {
+                       dev_err(sdev->dev, "Failed to parse ext data type %d\n",
+                               ext_hdr->type);
+                       break;
+               }
+
+               /* move to next header */
+               offset += ext_hdr->hdr.size;
+               snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
+                                      sizeof(*ext_hdr));
+               ext_hdr = ext_data;
+       }
+
+       kfree(ext_data);
+       return ret;
+}
+
+static void ipc3_get_windows(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc_window_elem *elem;
+       u32 outbox_offset = 0;
+       u32 stream_offset = 0;
+       u32 inbox_offset = 0;
+       u32 outbox_size = 0;
+       u32 stream_size = 0;
+       u32 inbox_size = 0;
+       u32 debug_size = 0;
+       u32 debug_offset = 0;
+       int window_offset;
+       int i;
+
+       if (!sdev->info_window) {
+               dev_err(sdev->dev, "%s: No window info present\n", __func__);
+               return;
+       }
+
+       for (i = 0; i < sdev->info_window->num_windows; i++) {
+               elem = &sdev->info_window->window[i];
+
+               window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
+               if (window_offset < 0) {
+                       dev_warn(sdev->dev, "No offset for window %d\n", elem->id);
+                       continue;
+               }
+
+               switch (elem->type) {
+               case SOF_IPC_REGION_UPBOX:
+                       inbox_offset = window_offset + elem->offset;
+                       inbox_size = elem->size;
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       inbox_offset,
+                                                       elem->size, "inbox",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_DOWNBOX:
+                       outbox_offset = window_offset + elem->offset;
+                       outbox_size = elem->size;
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       outbox_offset,
+                                                       elem->size, "outbox",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_TRACE:
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       window_offset + elem->offset,
+                                                       elem->size, "etrace",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_DEBUG:
+                       debug_offset = window_offset + elem->offset;
+                       debug_size = elem->size;
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       window_offset + elem->offset,
+                                                       elem->size, "debug",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_STREAM:
+                       stream_offset = window_offset + elem->offset;
+                       stream_size = elem->size;
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       stream_offset,
+                                                       elem->size, "stream",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_REGS:
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       window_offset + elem->offset,
+                                                       elem->size, "regs",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               case SOF_IPC_REGION_EXCEPTION:
+                       sdev->dsp_oops_offset = window_offset + elem->offset;
+                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+                                                       window_offset + elem->offset,
+                                                       elem->size, "exception",
+                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
+                       break;
+               default:
+                       dev_err(sdev->dev, "%s: Illegal window info: %u\n",
+                               __func__, elem->type);
+                       return;
+               }
+       }
+
+       if (outbox_size == 0 || inbox_size == 0) {
+               dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__);
+               return;
+       }
+
+       sdev->dsp_box.offset = inbox_offset;
+       sdev->dsp_box.size = inbox_size;
+
+       sdev->host_box.offset = outbox_offset;
+       sdev->host_box.size = outbox_size;
+
+       sdev->stream_box.offset = stream_offset;
+       sdev->stream_box.size = stream_size;
+
+       sdev->debug_box.offset = debug_offset;
+       sdev->debug_box.size = debug_size;
+
+       dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
+               inbox_offset, inbox_size);
+       dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
+               outbox_offset, outbox_size);
+       dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
+               stream_offset, stream_size);
+       dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
+               debug_offset, debug_size);
+}
+
+static int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
+
+       msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+       if (!msg->reply_data)
+               return -ENOMEM;
+
+       sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE;
+
+       return 0;
+}
+
+int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+       struct sof_ipc_fw_version *v = &ready->version;
+
+       dev_info(sdev->dev,
+                "Firmware info: version %d:%d:%d-%s\n",  v->major, v->minor,
+                v->micro, v->tag);
+       dev_info(sdev->dev,
+                "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
+                SOF_ABI_VERSION_MAJOR(v->abi_version),
+                SOF_ABI_VERSION_MINOR(v->abi_version),
+                SOF_ABI_VERSION_PATCH(v->abi_version),
+                SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
+
+       if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
+               dev_err(sdev->dev, "incompatible FW ABI version\n");
+               return -EINVAL;
+       }
+
+       if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
+               if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
+                       dev_warn(sdev->dev, "FW ABI is more recent than kernel\n");
+               } else {
+                       dev_err(sdev->dev, "FW ABI is more recent than kernel\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (ready->flags & SOF_IPC_INFO_BUILD) {
+               dev_info(sdev->dev,
+                        "Firmware debug build %d on %s-%s - options:\n"
+                        " GDB: %s\n"
+                        " lock debug: %s\n"
+                        " lock vdebug: %s\n",
+                        v->build, v->date, v->time,
+                        (ready->flags & SOF_IPC_INFO_GDB) ?
+                               "enabled" : "disabled",
+                        (ready->flags & SOF_IPC_INFO_LOCKS) ?
+                               "enabled" : "disabled",
+                        (ready->flags & SOF_IPC_INFO_LOCKSV) ?
+                               "enabled" : "disabled");
+       }
+
+       /* copy the fw_version into debugfs at first boot */
+       memcpy(&sdev->fw_version, v, sizeof(*v));
+
+       return 0;
+}
+
+static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd)
+{
+       struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
+       int offset;
+       int ret;
+
+       /* mailbox must be on 4k boundary */
+       offset = snd_sof_dsp_get_mailbox_offset(sdev);
+       if (offset < 0) {
+               dev_err(sdev->dev, "%s: no mailbox offset\n", __func__);
+               return offset;
+       }
+
+       dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset);
+
+       /* no need to re-check version/ABI for subsequent boots */
+       if (!sdev->first_boot)
+               return 0;
+
+       /*
+        * copy data from the DSP FW ready offset
+        * Subsequent error handling is not needed for BLK_TYPE_SRAM
+        */
+       ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
+                                    sizeof(*fw_ready));
+       if (ret) {
+               dev_err(sdev->dev,
+                       "Unable to read fw_ready, read from TYPE_SRAM failed\n");
+               return ret;
+       }
+
+       /* make sure ABI version is compatible */
+       ret = sof_ipc3_validate_fw_version(sdev);
+       if (ret < 0)
+               return ret;
+
+       /* now check for extended data */
+       ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
+
+       ipc3_get_windows(sdev);
+
+       return ipc3_init_reply_data_buffer(sdev);
+}
+
+/* IPC stream position. */
+static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
+{
+       struct snd_soc_component *scomp = sdev->component;
+       struct snd_sof_pcm_stream *stream;
+       struct sof_ipc_stream_posn posn;
+       struct snd_sof_pcm *spcm;
+       int direction, ret;
+
+       spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
+       if (!spcm) {
+               dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n",
+                       msg_id);
+               return;
+       }
+
+       stream = &spcm->stream[direction];
+       ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+       if (ret < 0) {
+               dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
+               return;
+       }
+
+       dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
+                posn.host_posn, posn.dai_posn, posn.wallclock);
+
+       memcpy(&stream->posn, &posn, sizeof(posn));
+
+       if (spcm->pcm.compress)
+               snd_sof_compr_fragment_elapsed(stream->cstream);
+       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);
+}
+
+/* DSP notifies host of an XRUN within FW */
+static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
+{
+       struct snd_soc_component *scomp = sdev->component;
+       struct snd_sof_pcm_stream *stream;
+       struct sof_ipc_stream_posn posn;
+       struct snd_sof_pcm *spcm;
+       int direction, ret;
+
+       spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
+       if (!spcm) {
+               dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n",
+                       msg_id);
+               return;
+       }
+
+       stream = &spcm->stream[direction];
+       ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+       if (ret < 0) {
+               dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
+               return;
+       }
+
+       dev_dbg(sdev->dev,  "posn XRUN: host %llx comp %d size %d\n",
+               posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
+
+#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
+       /* stop PCM on XRUN - used for pipeline debug */
+       memcpy(&stream->posn, &posn, sizeof(posn));
+       snd_pcm_stop_xrun(stream->substream);
+#endif
+}
+
+/* stream notifications from firmware */
+static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
+{
+       struct sof_ipc_cmd_hdr *hdr = msg_buf;
+       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+       u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
+
+       switch (msg_type) {
+       case SOF_IPC_STREAM_POSITION:
+               ipc3_period_elapsed(sdev, msg_id);
+               break;
+       case SOF_IPC_STREAM_TRIG_XRUN:
+               ipc3_xrun(sdev, msg_id);
+               break;
+       default:
+               dev_err(sdev->dev, "unhandled stream message %#x\n",
+                       msg_id);
+               break;
+       }
+}
+
+/* component notifications from firmware */
+static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
+{
+       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+       struct sof_ipc_cmd_hdr *hdr = msg_buf;
+       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+
+       switch (msg_type) {
+       case SOF_IPC_COMP_GET_VALUE:
+       case SOF_IPC_COMP_GET_DATA:
+               break;
+       default:
+               dev_err(sdev->dev, "unhandled component message %#x\n", msg_type);
+               return;
+       }
+
+       if (tplg_ops->control->update)
+               tplg_ops->control->update(sdev, msg_buf);
+}
+
+static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
+{
+       struct sof_ipc_cmd_hdr *hdr = msg_buf;
+       u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+
+       switch (msg_type) {
+       case SOF_IPC_TRACE_DMA_POSITION:
+               ipc3_dtrace_posn_update(sdev, msg_buf);
+               break;
+       default:
+               dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type);
+               break;
+       }
+}
+
+/* DSP firmware has sent host a message  */
+static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
+{
+       ipc3_rx_callback rx_callback = NULL;
+       struct sof_ipc_cmd_hdr hdr;
+       void *msg_buf;
+       u32 cmd;
+       int err;
+
+       /* read back header */
+       err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
+       if (err < 0) {
+               dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
+               return;
+       }
+
+       if (hdr.size < sizeof(hdr)) {
+               dev_err(sdev->dev, "The received message size is invalid\n");
+               return;
+       }
+
+       ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd);
+
+       cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
+
+       /* check message type */
+       switch (cmd) {
+       case SOF_IPC_GLB_REPLY:
+               dev_err(sdev->dev, "ipc reply unknown\n");
+               break;
+       case SOF_IPC_FW_READY:
+               /* check for FW boot completion */
+               if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
+                       err = ipc3_fw_ready(sdev, cmd);
+                       if (err < 0)
+                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
+                       else
+                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
+
+                       /* wake up firmware loader */
+                       wake_up(&sdev->boot_wait);
+               }
+               break;
+       case SOF_IPC_GLB_COMPOUND:
+       case SOF_IPC_GLB_TPLG_MSG:
+       case SOF_IPC_GLB_PM_MSG:
+               break;
+       case SOF_IPC_GLB_COMP_MSG:
+               rx_callback = ipc3_comp_notification;
+               break;
+       case SOF_IPC_GLB_STREAM_MSG:
+               rx_callback = ipc3_stream_message;
+               break;
+       case SOF_IPC_GLB_TRACE_MSG:
+               rx_callback = ipc3_trace_message;
+               break;
+       default:
+               dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
+               break;
+       }
+
+       /* read the full message */
+       msg_buf = kmalloc(hdr.size, GFP_KERNEL);
+       if (!msg_buf)
+               return;
+
+       err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
+       if (err < 0) {
+               dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
+       } else {
+               /* Call local handler for the message */
+               if (rx_callback)
+                       rx_callback(sdev, msg_buf);
+
+               /* Notify registered clients */
+               sof_client_ipc_rx_dispatcher(sdev, msg_buf);
+       }
+
+       kfree(msg_buf);
+
+       ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd);
+}
 
 static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
 {
@@ -19,8 +1046,8 @@ static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
        struct sof_ipc_reply reply;
 
        /* send ctx save ipc to dsp */
-       return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx,
-                                 sizeof(pm_ctx), &reply, sizeof(reply));
+       return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx),
+                              &reply, sizeof(reply), false);
 }
 
 static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
@@ -42,4 +1069,11 @@ const struct sof_ipc_ops ipc3_ops = {
        .tplg = &ipc3_tplg_ops,
        .pm = &ipc3_pm_ops,
        .pcm = &ipc3_pcm_ops,
+       .fw_loader = &ipc3_loader_ops,
+       .fw_tracing = &ipc3_dtrace_ops,
+
+       .tx_msg = sof_ipc3_tx_msg,
+       .rx_msg = sof_ipc3_rx_msg,
+       .set_get_data = sof_ipc3_set_get_data,
+       .get_reply = sof_ipc3_get_reply,
 };
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
new file mode 100644 (file)
index 0000000..9fadae8
--- /dev/null
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license.  When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <linux/firmware.h>
+#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
+#include "ipc4-priv.h"
+#include "sof-audio.h"
+#include "sof-priv.h"
+#include "ops.h"
+
+static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       struct sof_man4_fw_binary_header *fw_header;
+       const struct firmware *fw = plat_data->fw;
+       struct sof_ext_manifest4_hdr *ext_man_hdr;
+       struct sof_man4_module_config *fm_config;
+       struct sof_ipc4_fw_module *fw_module;
+       struct sof_man4_module *fm_entry;
+       ssize_t remaining;
+       u32 fw_hdr_offset;
+       int i;
+
+       if (!ipc4_data) {
+               dev_err(sdev->dev, "%s: ipc4_data is not available\n", __func__);
+               return -EINVAL;
+       }
+
+       remaining = fw->size;
+       if (remaining <= sizeof(*ext_man_hdr)) {
+               dev_err(sdev->dev, "Firmware size is too small: %zu\n", remaining);
+               return -EINVAL;
+       }
+
+       ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
+
+       fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
+       if (!fw_hdr_offset)
+               return -EINVAL;
+
+       if (remaining <= ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)) {
+               dev_err(sdev->dev, "Invalid firmware size %zu, should be at least %zu\n",
+                       remaining, ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header));
+               return -EINVAL;
+       }
+
+       fw_header = (struct sof_man4_fw_binary_header *)
+                               (fw->data + ext_man_hdr->len + fw_hdr_offset);
+       remaining -= (ext_man_hdr->len + fw_hdr_offset);
+
+       if (remaining <= fw_header->len) {
+               dev_err(sdev->dev, "Invalid fw_header->len %u\n", fw_header->len);
+               return -EINVAL;
+       }
+
+       dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n",
+                fw_header->major_version, fw_header->minor_version,
+                fw_header->hotfix_version, fw_header->build_version);
+       dev_dbg(sdev->dev, "Firmware name: %s, header length: %u, module count: %u\n",
+               fw_header->name, fw_header->len, fw_header->num_module_entries);
+
+       ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev,
+                                                  fw_header->num_module_entries,
+                                                  sizeof(*fw_module), GFP_KERNEL);
+       if (!ipc4_data->fw_modules)
+               return -ENOMEM;
+
+       ipc4_data->num_fw_modules = fw_header->num_module_entries;
+       fw_module = ipc4_data->fw_modules;
+
+       fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len);
+       remaining -= fw_header->len;
+
+       if (remaining < fw_header->num_module_entries * sizeof(*fm_entry)) {
+               dev_err(sdev->dev, "Invalid num_module_entries %u\n",
+                       fw_header->num_module_entries);
+               return -EINVAL;
+       }
+
+       fm_config = (struct sof_man4_module_config *)
+                               (fm_entry + fw_header->num_module_entries);
+       remaining -= (fw_header->num_module_entries * sizeof(*fm_entry));
+       for (i = 0; i < fw_header->num_module_entries; i++) {
+               memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry));
+
+               if (fm_entry->cfg_count) {
+                       if (remaining < (fm_entry->cfg_offset + fm_entry->cfg_count) *
+                           sizeof(*fm_config)) {
+                               dev_err(sdev->dev, "Invalid module cfg_offset %u\n",
+                                       fm_entry->cfg_offset);
+                               return -EINVAL;
+                       }
+
+                       /* a module's config is always the same size */
+                       fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes;
+
+                       dev_dbg(sdev->dev,
+                               "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n",
+                               fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count,
+                               fw_module->bss_size);
+               } else {
+                       fw_module->bss_size = 0;
+
+                       dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name,
+                               &fm_entry->uuid);
+               }
+
+               fw_module->man4_module_entry.id = i;
+               ida_init(&fw_module->m_ida);
+               fw_module->private = NULL;
+
+               fw_module++;
+               fm_entry++;
+       }
+
+       return ext_man_hdr->len;
+}
+
+static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+       u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       struct sof_man4_fw_binary_header *fw_header;
+       const struct firmware *fw = plat_data->fw;
+       struct sof_ext_manifest4_hdr *ext_man_hdr;
+
+       ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
+       fw_header = (struct sof_man4_fw_binary_header *)
+                               (fw->data + ext_man_hdr->len + fw_hdr_offset);
+
+       /* TODO: Add firmware verification code here */
+
+       dev_dbg(sdev->dev, "Validated firmware version: %u.%u.%u.%u\n",
+               fw_header->major_version, fw_header->minor_version,
+               fw_header->hotfix_version, fw_header->build_version);
+
+       return 0;
+}
+
+static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
+{
+       const struct sof_ipc_ops *iops = sdev->ipc->ops;
+       struct sof_ipc4_fw_version *fw_ver;
+       struct sof_ipc4_tuple *tuple;
+       struct sof_ipc4_msg msg;
+       size_t offset = 0;
+       int ret;
+
+       /* Get the firmware configuration */
+       msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+       msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+       msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+       msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+       msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_FW_CONFIG);
+
+       msg.data_size = sdev->ipc->max_payload_size;
+       msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
+       if (!msg.data_ptr)
+               return -ENOMEM;
+
+       ret = iops->set_get_data(sdev, &msg, msg.data_size, false);
+       if (ret)
+               goto out;
+
+       while (offset < msg.data_size) {
+               tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset);
+
+               switch (tuple->type) {
+               case SOF_IPC4_FW_CFG_FW_VERSION:
+                       fw_ver = (struct sof_ipc4_fw_version *)tuple->value;
+
+                       dev_info(sdev->dev,
+                                "Booted firmware version: %u.%u.%u.%u\n",
+                                fw_ver->major, fw_ver->minor, fw_ver->hotfix,
+                                fw_ver->build);
+                       break;
+               case SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES:
+                       dev_vdbg(sdev->dev, "DL mailbox size: %u\n", *tuple->value);
+                       break;
+               case SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES:
+                       dev_vdbg(sdev->dev, "UL mailbox size: %u\n", *tuple->value);
+                       break;
+               case SOF_IPC4_FW_CFG_TRACE_LOG_BYTES:
+                       dev_vdbg(sdev->dev, "Trace log size: %u\n", *tuple->value);
+                       break;
+               default:
+                       break;
+               }
+
+               offset += sizeof(*tuple) + tuple->size;
+       }
+
+out:
+       kfree(msg.data_ptr);
+
+       return ret;
+}
+
+const struct sof_ipc_fw_loader_ops ipc4_loader_ops = {
+       .validate = sof_ipc4_validate_firmware,
+       .parse_ext_manifest = sof_ipc4_fw_parse_ext_man,
+       .query_fw_configuration = sof_ipc4_query_fw_configuration,
+};
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
new file mode 100644 (file)
index 0000000..2b71d56
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __SOUND_SOC_SOF_IPC4_PRIV_H
+#define __SOUND_SOC_SOF_IPC4_PRIV_H
+
+#include <linux/idr.h>
+#include <sound/sof/ext_manifest4.h>
+#include "sof-priv.h"
+
+/**
+ * struct sof_ipc4_fw_data - IPC4-specific data
+ * @manifest_fw_hdr_offset: FW header offset in the manifest
+ * @num_fw_modules : Number of modules in base FW
+ * @fw_modules: Array of base FW modules
+ */
+struct sof_ipc4_fw_data {
+       u32 manifest_fw_hdr_offset;
+       int num_fw_modules;
+       void *fw_modules;
+};
+
+/**
+ * struct sof_ipc4_fw_module - IPC4 module info
+ * @sof_man4_module : Module info
+ * @m_ida: Module instance identifier
+ * @bss_size: Module object size
+ * @private: Module private data
+ */
+struct sof_ipc4_fw_module {
+       struct sof_man4_module man4_module_entry;
+       struct ida m_ida;
+       u32 bss_size;
+       void *private;
+};
+
+extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
+
+#endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
new file mode 100644 (file)
index 0000000..658802c
--- /dev/null
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license.  When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Rander Wang <rander.wang@linux.intel.com>
+//         Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+#include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ipc4-priv.h"
+#include "ops.h"
+
+#ifdef DEBUG_VERBOSE
+#define sof_ipc4_dump_payload(sdev, ipc_data, size)                    \
+               print_hex_dump_debug("Message payload: ",               \
+                                    DUMP_PREFIX_OFFSET,                \
+                                    16, 4, ipc_data, size, false)
+#else
+#define sof_ipc4_dump_payload(sdev, ipc_data, size)    do { } while (0)
+#endif
+
+static const struct sof_ipc4_fw_status {
+       int status;
+       char *msg;
+} ipc4_status[] = {
+       {0, "The operation was successful"},
+       {1, "Invalid parameter specified"},
+       {2, "Unknown message type specified"},
+       {3, "Not enough space in the IPC reply buffer to complete the request"},
+       {4, "The system or resource is busy"},
+       {5, "Replaced ADSP IPC PENDING (unused)"},
+       {6, "Unknown error while processing the request"},
+       {7, "Unsupported operation requested"},
+       {8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"},
+       {9, "Specified resource not found"},
+       {10, "A resource's ID requested to be created is already assigned"},
+       {11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+       {12, "Required resource is in invalid state"},
+       {13, "Requested power transition failed to complete"},
+       {14, "Manifest of the library being loaded is invalid"},
+       {15, "Requested service or data is unavailable on the target platform"},
+       {42, "Library target address is out of storage memory range"},
+       {43, "Reserved"},
+       {44, "Image verification by CSE failed"},
+       {100, "General module management error"},
+       {101, "Module loading failed"},
+       {102, "Integrity check of the loaded module content failed"},
+       {103, "Attempt to unload code of the module in use"},
+       {104, "Other failure of module instance initialization request"},
+       {105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+       {106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"},
+       {107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"},
+       {108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"},
+       {109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"},
+       {110, "Invalid (out of range) module ID provided"},
+       {111, "Invalid module instance ID provided"},
+       {112, "Invalid queue (pin) ID provided"},
+       {113, "Invalid destination queue (pin) ID provided"},
+       {114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"},
+       {115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"},
+       {116, "Invalid target code ID provided"},
+       {117, "Injection DMA buffer is too small for probing the input pin"},
+       {118, "Extraction DMA buffer is too small for probing the output pin"},
+       {120, "Invalid ID of configuration item provided in TLV list"},
+       {121, "Invalid length of configuration item provided in TLV list"},
+       {122, "Invalid structure of configuration item provided"},
+       {140, "Initialization of DMA Gateway failed"},
+       {141, "Invalid ID of gateway provided"},
+       {142, "Setting state of DMA Gateway failed"},
+       {143, "DMA_CONTROL message targeting gateway not allocated yet"},
+       {150, "Attempt to configure SCLK while I2S port is running"},
+       {151, "Attempt to configure MCLK while I2S port is running"},
+       {152, "Attempt to stop SCLK that is not running"},
+       {153, "Attempt to stop MCLK that is not running"},
+       {160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"},
+       {161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"},
+       {162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"},
+       {163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"},
+       {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},
+};
+
+static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status)
+{
+       int i, ret;
+
+       status &= SOF_IPC4_REPLY_STATUS;
+
+       if (!status)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) {
+               if (ipc4_status[i].status == status) {
+                       dev_err(sdev->dev, "FW reported error: %u - %s\n",
+                               status, ipc4_status[i].msg);
+                       goto to_errno;
+               }
+       }
+
+       if (i == ARRAY_SIZE(ipc4_status))
+               dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status);
+
+to_errno:
+       switch (status) {
+       case 8:
+       case 11:
+       case 105 ... 109:
+       case 114 ... 115:
+       case 160 ... 163:
+       case 165:
+               ret = -ENOENT;
+               break;
+       case 4:
+       case 150:
+       case 151:
+               ret = -EBUSY;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
+#define DBG_IPC4_MSG_TYPE_ENTRY(type)  [SOF_IPC4_##type] = #type
+static const char * const ipc4_dbg_mod_msg_type[] = {
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE),
+       DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE),
+};
+
+static const char * const ipc4_dbg_glb_msg_type[] = {
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE),
+       DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION),
+};
+
+#define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type
+static const char * const ipc4_dbg_notification_type[] = {
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE),
+       DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE),
+};
+
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+                               bool data_size_valid)
+{
+       u32 val, type;
+       const u8 *str2 = NULL;
+       const u8 *str = NULL;
+
+       val = msg->primary & SOF_IPC4_MSG_TARGET_MASK;
+       type = SOF_IPC4_MSG_TYPE_GET(msg->primary);
+
+       if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) {
+               /* Module message */
+               if (type < SOF_IPC4_MOD_TYPE_LAST)
+                       str = ipc4_dbg_mod_msg_type[type];
+               if (!str)
+                       str = "Unknown Module message type";
+       } else {
+               /* Global FW message */
+               if (type < SOF_IPC4_GLB_TYPE_LAST)
+                       str = ipc4_dbg_glb_msg_type[type];
+               if (!str)
+                       str = "Unknown Global message type";
+
+               if (type == SOF_IPC4_GLB_NOTIFICATION) {
+                       /* Notification message */
+                       u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+
+                       if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)
+                               str2 = ipc4_dbg_notification_type[notif];
+                       if (!str2)
+                               str2 = "Unknown Global notification";
+               }
+       }
+
+       if (str2) {
+               if (data_size_valid && msg->data_size)
+                       dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n",
+                               text, msg->primary, msg->extension, str, str2,
+                               msg->data_size);
+               else
+                       dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary,
+                               msg->extension, str, str2);
+       } else {
+               if (data_size_valid && msg->data_size)
+                       dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n",
+                               text, msg->primary, msg->extension, str,
+                               msg->data_size);
+               else
+                       dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary,
+                               msg->extension, str);
+       }
+}
+#else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+                               bool data_size_valid)
+{
+       if (data_size_valid && msg->data_size)
+               dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,
+                       msg->primary, msg->extension, msg->data_size);
+       else
+               dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);
+}
+#endif
+
+static int sof_ipc4_get_reply(struct snd_sof_dev *sdev)
+{
+       struct snd_sof_ipc_msg *msg = sdev->msg;
+       struct sof_ipc4_msg *ipc4_reply;
+       int ret;
+
+       /* get the generic reply */
+       ipc4_reply = msg->reply_data;
+
+       sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false);
+
+       ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary);
+       if (ret)
+               return ret;
+
+       /* No other information is expected for non large config get replies */
+       if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) ||
+           (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET))
+               return 0;
+
+       /* Read the requested payload */
+       snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr,
+                                msg->reply_size);
+
+       return 0;
+}
+
+/* wait for IPC message reply */
+static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
+{
+       struct snd_sof_ipc_msg *msg = &ipc->msg;
+       struct sof_ipc4_msg *ipc4_msg = msg->msg_data;
+       struct snd_sof_dev *sdev = ipc->sdev;
+       int ret;
+
+       /* wait for DSP IPC completion */
+       ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
+                                msecs_to_jiffies(sdev->ipc_timeout));
+       if (ret == 0) {
+               dev_err(sdev->dev, "ipc timed out for %#x|%#x\n",
+                       ipc4_msg->primary, ipc4_msg->extension);
+               return -ETIMEDOUT;
+       }
+
+       if (msg->reply_error) {
+               dev_err(sdev->dev, "ipc error for msg %#x|%#x\n",
+                       ipc4_msg->primary, ipc4_msg->extension);
+               ret =  msg->reply_error;
+       } else {
+               if (reply_data) {
+                       struct sof_ipc4_msg *ipc4_reply = msg->reply_data;
+                       struct sof_ipc4_msg *ipc4_reply_data = reply_data;
+
+                       /* Copy the header */
+                       ipc4_reply_data->header_u64 = ipc4_reply->header_u64;
+                       if (msg->reply_size && ipc4_reply_data->data_ptr) {
+                               /* copy the payload returned from DSP */
+                               memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr,
+                                      msg->reply_size);
+                               ipc4_reply_data->data_size = msg->reply_size;
+                       }
+               }
+
+               ret = 0;
+               sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true);
+       }
+
+       /* re-enable dumps after successful IPC tx */
+       if (sdev->ipc_dump_printed) {
+               sdev->dbg_dump_printed = false;
+               sdev->ipc_dump_printed = false;
+       }
+
+       return ret;
+}
+
+static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc,
+                               void *msg_data, size_t msg_bytes,
+                               void *reply_data, size_t reply_bytes)
+{
+       struct sof_ipc4_msg *ipc4_msg = msg_data;
+       struct snd_sof_dev *sdev = ipc->sdev;
+       int ret;
+
+       if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size)
+               return -EINVAL;
+
+       ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
+       if (ret) {
+               dev_err_ratelimited(sdev->dev,
+                                   "%s: ipc message send for %#x|%#x failed: %d\n",
+                                   __func__, ipc4_msg->primary, ipc4_msg->extension, ret);
+               return ret;
+       }
+
+       sof_ipc4_log_header(sdev->dev, "ipc tx      ", msg_data, true);
+
+       /* now wait for completion */
+       return ipc4_wait_tx_done(ipc, reply_data);
+}
+
+static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+                          void *reply_data, size_t reply_bytes, bool no_pm)
+{
+       struct snd_sof_ipc *ipc = sdev->ipc;
+#ifdef DEBUG_VERBOSE
+       struct sof_ipc4_msg *msg = NULL;
+#endif
+       int ret;
+
+       if (!msg_data)
+               return -EINVAL;
+
+       /* Serialise IPC TX */
+       mutex_lock(&ipc->tx_mutex);
+
+       ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+
+       mutex_unlock(&ipc->tx_mutex);
+
+#ifdef DEBUG_VERBOSE
+       /* payload is indicated by non zero msg/reply_bytes */
+       if (msg_bytes)
+               msg = msg_data;
+       else if (reply_bytes)
+               msg = reply_data;
+
+       if (msg)
+               sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
+#endif
+
+       return ret;
+}
+
+static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
+                                size_t payload_bytes, bool set)
+{
+       size_t payload_limit = sdev->ipc->max_payload_size;
+       struct sof_ipc4_msg *ipc4_msg = data;
+       struct sof_ipc4_msg tx = {{ 0 }};
+       struct sof_ipc4_msg rx = {{ 0 }};
+       size_t remaining = payload_bytes;
+       size_t offset = 0;
+       size_t chunk_size;
+       int ret;
+
+       if (!data)
+               return -EINVAL;
+
+       if ((ipc4_msg->primary & SOF_IPC4_MSG_TARGET_MASK) !=
+           SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG))
+               return -EINVAL;
+
+       ipc4_msg->primary &= ~SOF_IPC4_MSG_TYPE_MASK;
+       tx.primary = ipc4_msg->primary;
+       tx.extension = ipc4_msg->extension;
+
+       if (set)
+               tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+       else
+               tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_GET);
+
+       tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+       tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(payload_bytes);
+
+       tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+
+       /* Serialise IPC TX */
+       mutex_lock(&sdev->ipc->tx_mutex);
+
+       do {
+               size_t tx_size, rx_size;
+
+               if (remaining > payload_limit) {
+                       chunk_size = payload_limit;
+               } else {
+                       chunk_size = remaining;
+                       if (set)
+                               tx.extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
+               }
+
+               if (offset) {
+                       tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK;
+                       tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+                       tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(offset);
+               }
+
+               if (set) {
+                       tx.data_size = chunk_size;
+                       tx.data_ptr = ipc4_msg->data_ptr + offset;
+
+                       tx_size = chunk_size;
+                       rx_size = 0;
+               } else {
+                       rx.primary = 0;
+                       rx.extension = 0;
+                       rx.data_size = chunk_size;
+                       rx.data_ptr = ipc4_msg->data_ptr + offset;
+
+                       tx_size = 0;
+                       rx_size = chunk_size;
+               }
+
+               /* Send the message for the current chunk */
+               ret = ipc4_tx_msg_unlocked(sdev->ipc, &tx, tx_size, &rx, rx_size);
+               if (ret < 0) {
+                       dev_err(sdev->dev,
+                               "%s: large config %s failed at offset %zu: %d\n",
+                               __func__, set ? "set" : "get", offset, ret);
+                       goto out;
+               }
+
+               if (!set && rx.extension & SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK) {
+                       /* Verify the firmware reported total payload size */
+                       rx_size = rx.extension & SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+
+                       if (rx_size > payload_bytes) {
+                               dev_err(sdev->dev,
+                                       "%s: Receive buffer (%zu) is too small for %zu\n",
+                                       __func__, payload_bytes, rx_size);
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+
+                       if (rx_size < chunk_size) {
+                               chunk_size = rx_size;
+                               remaining = rx_size;
+                       } else if (rx_size < payload_bytes) {
+                               remaining = rx_size;
+                       }
+               }
+
+               offset += chunk_size;
+               remaining -= chunk_size;
+       } while (remaining);
+
+       /* Adjust the received data size if needed */
+       if (!set && payload_bytes != offset)
+               ipc4_msg->data_size = offset;
+
+       sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
+
+out:
+       mutex_unlock(&sdev->ipc->tx_mutex);
+
+       return ret;
+}
+
+static int sof_ipc4_init_msg_memory(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc4_msg *ipc4_msg;
+       struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
+
+       /* TODO: get max_payload_size from firmware */
+       sdev->ipc->max_payload_size = SOF_IPC4_MSG_MAX_SIZE;
+
+       /* Allocate memory for the ipc4 container and the maximum payload */
+       msg->reply_data = devm_kzalloc(sdev->dev, sdev->ipc->max_payload_size +
+                                      sizeof(struct sof_ipc4_msg), GFP_KERNEL);
+       if (!msg->reply_data)
+               return -ENOMEM;
+
+       ipc4_msg = msg->reply_data;
+       ipc4_msg->data_ptr = msg->reply_data + sizeof(struct sof_ipc4_msg);
+
+       return 0;
+}
+
+static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
+{
+       int inbox_offset, inbox_size, outbox_offset, outbox_size;
+
+       /* no need to re-check version/ABI for subsequent boots */
+       if (!sdev->first_boot)
+               return 0;
+
+       /* Set up the windows for IPC communication */
+       inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
+       if (inbox_offset < 0) {
+               dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
+               return inbox_offset;
+       }
+       inbox_size = SOF_IPC4_MSG_MAX_SIZE;
+       outbox_offset = snd_sof_dsp_get_window_offset(sdev, 1);
+       outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+
+       sdev->dsp_box.offset = inbox_offset;
+       sdev->dsp_box.size = inbox_size;
+       sdev->host_box.offset = outbox_offset;
+       sdev->host_box.size = outbox_size;
+
+       dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
+               inbox_offset, inbox_size);
+       dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
+               outbox_offset, outbox_size);
+
+       return sof_ipc4_init_msg_memory(sdev);
+}
+
+static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
+{
+       struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data;
+       size_t data_size = 0;
+       int err;
+
+       if (!ipc4_msg || !SOF_IPC4_MSG_IS_NOTIFICATION(ipc4_msg->primary))
+               return;
+
+       ipc4_msg->data_ptr = NULL;
+       ipc4_msg->data_size = 0;
+
+       sof_ipc4_log_header(sdev->dev, "ipc rx      ", ipc4_msg, false);
+
+       switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) {
+       case SOF_IPC4_NOTIFY_FW_READY:
+               /* check for FW boot completion */
+               if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
+                       err = ipc4_fw_ready(sdev, ipc4_msg);
+                       if (err < 0)
+                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
+                       else
+                               sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
+
+                       /* wake up firmware loader */
+                       wake_up(&sdev->boot_wait);
+               }
+
+               break;
+       case SOF_IPC4_NOTIFY_RESOURCE_EVENT:
+               data_size = sizeof(struct sof_ipc4_notify_resource_data);
+               break;
+       default:
+               dev_dbg(sdev->dev, "%s: Unhandled DSP message: %#x|%#x\n", __func__,
+                       ipc4_msg->primary, ipc4_msg->extension);
+               break;
+       }
+
+       if (data_size) {
+               ipc4_msg->data_ptr = kmalloc(data_size, GFP_KERNEL);
+               if (!ipc4_msg->data_ptr)
+                       return;
+
+               ipc4_msg->data_size = data_size;
+               snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
+       }
+
+       sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true);
+
+       if (data_size) {
+               kfree(ipc4_msg->data_ptr);
+               ipc4_msg->data_ptr = NULL;
+               ipc4_msg->data_size = 0;
+       }
+}
+
+const struct sof_ipc_ops ipc4_ops = {
+       .tx_msg = sof_ipc4_tx_msg,
+       .rx_msg = sof_ipc4_rx_msg,
+       .set_get_data = sof_ipc4_set_get_data,
+       .get_reply = sof_ipc4_get_reply,
+       .fw_loader = &ipc4_loader_ops,
+};
index 697f035..5f51d93 100644 (file)
 //
 
 #include <linux/firmware.h>
-#include <sound/sof.h>
-#include <sound/sof/ext_manifest.h>
 #include "sof-priv.h"
 #include "ops.h"
 
-static int get_ext_windows(struct snd_sof_dev *sdev,
-                          const struct sof_ipc_ext_data_hdr *ext_hdr)
-{
-       const struct sof_ipc_window *w =
-               container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
-
-       if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
-               return -EINVAL;
-
-       if (sdev->info_window) {
-               if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
-                       dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox");
-                       return -EINVAL;
-               }
-               return 0;
-       }
-
-       /* keep a local copy of the data */
-       sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size,
-                                        GFP_KERNEL);
-       if (!sdev->info_window)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static int get_cc_info(struct snd_sof_dev *sdev,
-                      const struct sof_ipc_ext_data_hdr *ext_hdr)
-{
-       int ret;
-
-       const struct sof_ipc_cc_version *cc =
-               container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
-
-       if (sdev->cc_version) {
-               if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
-                       dev_err(sdev->dev, "error: receive diverged cc_version descriptions");
-                       return -EINVAL;
-               }
-               return 0;
-       }
-
-       dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
-               cc->name, cc->major, cc->minor, cc->micro, cc->desc,
-               cc->optim);
-
-       /* create read-only cc_version debugfs to store compiler version info */
-       /* use local copy of the cc_version to prevent data corruption */
-       if (sdev->first_boot) {
-               sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
-                                               GFP_KERNEL);
-
-               if (!sdev->cc_version)
-                       return -ENOMEM;
-
-               memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
-               ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
-                                              cc->ext_hdr.hdr.size,
-                                              "cc_version", 0444);
-
-               /* errors are only due to memory allocation, not debugfs */
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-/* parse the extended FW boot data structures from FW boot message */
-static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
-{
-       struct sof_ipc_ext_data_hdr *ext_hdr;
-       void *ext_data;
-       int ret = 0;
-
-       ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!ext_data)
-               return -ENOMEM;
-
-       /* get first header */
-       snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
-                              sizeof(*ext_hdr));
-       ext_hdr = ext_data;
-
-       while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
-               /* read in ext structure */
-               snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                      offset + sizeof(*ext_hdr),
-                                      (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
-                                      ext_hdr->hdr.size - sizeof(*ext_hdr));
-
-               dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
-                       ext_hdr->type, ext_hdr->hdr.size);
-
-               /* process structure data */
-               switch (ext_hdr->type) {
-               case SOF_IPC_EXT_WINDOW:
-                       ret = get_ext_windows(sdev, ext_hdr);
-                       break;
-               case SOF_IPC_EXT_CC_INFO:
-                       ret = get_cc_info(sdev, ext_hdr);
-                       break;
-               case SOF_IPC_EXT_UNUSED:
-               case SOF_IPC_EXT_PROBE_INFO:
-               case SOF_IPC_EXT_USER_ABI_INFO:
-                       /* They are supported but we don't do anything here */
-                       break;
-               default:
-                       dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
-                                ext_hdr->type, ext_hdr->hdr.size);
-                       ret = 0;
-                       break;
-               }
-
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
-                               ext_hdr->type);
-                       break;
-               }
-
-               /* move to next header */
-               offset += ext_hdr->hdr.size;
-               snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
-                                      sizeof(*ext_hdr));
-               ext_hdr = ext_data;
-       }
-
-       kfree(ext_data);
-       return ret;
-}
-
-static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
-                                 const struct sof_ext_man_elem_header *hdr)
-{
-       const struct sof_ext_man_fw_version *v =
-               container_of(hdr, struct sof_ext_man_fw_version, hdr);
-
-       memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
-       sdev->fw_ready.flags = v->flags;
-
-       /* log ABI versions and check FW compatibility */
-       return snd_sof_ipc_valid(sdev);
-}
-
-static int ext_man_get_windows(struct snd_sof_dev *sdev,
-                              const struct sof_ext_man_elem_header *hdr)
-{
-       const struct sof_ext_man_window *w;
-
-       w = container_of(hdr, struct sof_ext_man_window, hdr);
-
-       return get_ext_windows(sdev, &w->ipc_window.ext_hdr);
-}
-
-static int ext_man_get_cc_info(struct snd_sof_dev *sdev,
-                              const struct sof_ext_man_elem_header *hdr)
-{
-       const struct sof_ext_man_cc_version *cc;
-
-       cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
-
-       return get_cc_info(sdev, &cc->cc_version.ext_hdr);
-}
-
-static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
-                                   const struct sof_ext_man_elem_header *hdr)
-{
-       const struct ext_man_dbg_abi *dbg_abi =
-               container_of(hdr, struct ext_man_dbg_abi, hdr);
-
-       if (sdev->first_boot)
-               dev_dbg(sdev->dev,
-                       "Firmware: DBG_ABI %d:%d:%d\n",
-                       SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
-                       SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
-                       SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
-
-       return 0;
-}
-
-static int ext_man_get_config_data(struct snd_sof_dev *sdev,
-                                  const struct sof_ext_man_elem_header *hdr)
-{
-       const struct sof_ext_man_config_data *config =
-               container_of(hdr, struct sof_ext_man_config_data, hdr);
-       const struct sof_config_elem *elem;
-       int elems_counter;
-       int elems_size;
-       int ret = 0;
-       int i;
-
-       /* calculate elements counter */
-       elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
-       elems_counter = elems_size / sizeof(struct sof_config_elem);
-
-       dev_dbg(sdev->dev, "%s can hold up to %d config elements\n",
-               __func__, elems_counter);
-
-       for (i = 0; i < elems_counter; ++i) {
-               elem = &config->elems[i];
-               dev_dbg(sdev->dev, "%s get index %d token %d val %d\n",
-                       __func__, i, elem->token, elem->value);
-               switch (elem->token) {
-               case SOF_EXT_MAN_CONFIG_EMPTY:
-                       /* unused memory space is zero filled - mapped to EMPTY elements */
-                       break;
-               case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
-                       /* TODO: use ipc msg size from config data */
-                       break;
-               case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
-                       if (sdev->first_boot && elem->value)
-                               ret = snd_sof_dbg_memory_info_init(sdev);
-                       break;
-               default:
-                       dev_info(sdev->dev, "Unknown firmware configuration token %d value %d",
-                                elem->token, elem->value);
-                       break;
-               }
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n",
-                               elem->token, elem->value, ret);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
-{
-       const struct sof_ext_man_header *head;
-
-       head = (struct sof_ext_man_header *)fw->data;
-
-       /*
-        * assert fw size is big enough to contain extended manifest header,
-        * it prevents from reading unallocated memory from `head` in following
-        * step.
-        */
-       if (fw->size < sizeof(*head))
-               return -EINVAL;
-
-       /*
-        * When fw points to extended manifest,
-        * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
-        */
-       if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
-               return head->full_size;
-
-       /* otherwise given fw don't have an extended manifest */
-       return 0;
-}
-
-/* parse extended FW manifest data structures */
-static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev,
-                                   const struct firmware *fw)
-{
-       const struct sof_ext_man_elem_header *elem_hdr;
-       const struct sof_ext_man_header *head;
-       ssize_t ext_man_size;
-       ssize_t remaining;
-       uintptr_t iptr;
-       int ret = 0;
-
-       head = (struct sof_ext_man_header *)fw->data;
-       remaining = head->full_size - head->header_size;
-       ext_man_size = snd_sof_ext_man_size(fw);
-
-       /* Assert firmware starts with extended manifest */
-       if (ext_man_size <= 0)
-               return ext_man_size;
-
-       /* incompatible version */
-       if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
-                                            head->header_version)) {
-               dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n",
-                       head->header_version, SOF_EXT_MAN_VERSION);
-               return -EINVAL;
-       }
-
-       /* get first extended manifest element header */
-       iptr = (uintptr_t)fw->data + head->header_size;
-
-       while (remaining > sizeof(*elem_hdr)) {
-               elem_hdr = (struct sof_ext_man_elem_header *)iptr;
-
-               dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n",
-                       elem_hdr->type, elem_hdr->size);
-
-               if (elem_hdr->size < sizeof(*elem_hdr) ||
-                   elem_hdr->size > remaining) {
-                       dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n",
-                               elem_hdr->type, elem_hdr->size);
-                       return -EINVAL;
-               }
-
-               /* process structure data */
-               switch (elem_hdr->type) {
-               case SOF_EXT_MAN_ELEM_FW_VERSION:
-                       ret = ext_man_get_fw_version(sdev, elem_hdr);
-                       break;
-               case SOF_EXT_MAN_ELEM_WINDOW:
-                       ret = ext_man_get_windows(sdev, elem_hdr);
-                       break;
-               case SOF_EXT_MAN_ELEM_CC_VERSION:
-                       ret = ext_man_get_cc_info(sdev, elem_hdr);
-                       break;
-               case SOF_EXT_MAN_ELEM_DBG_ABI:
-                       ret = ext_man_get_dbg_abi_info(sdev, elem_hdr);
-                       break;
-               case SOF_EXT_MAN_ELEM_CONFIG_DATA:
-                       ret = ext_man_get_config_data(sdev, elem_hdr);
-                       break;
-               case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
-                       ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
-                       break;
-               default:
-                       dev_info(sdev->dev, "unknown sof_ext_man header type %d size 0x%X\n",
-                                elem_hdr->type, elem_hdr->size);
-                       break;
-               }
-
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n",
-                               elem_hdr->type, elem_hdr->size);
-                       return ret;
-               }
-
-               remaining -= elem_hdr->size;
-               iptr += elem_hdr->size;
-       }
-
-       if (remaining) {
-               dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
-               return -EINVAL;
-       }
-
-       return ext_man_size;
-}
-
-/*
- * IPC Firmware ready.
- */
-static void sof_get_windows(struct snd_sof_dev *sdev)
-{
-       struct sof_ipc_window_elem *elem;
-       u32 outbox_offset = 0;
-       u32 stream_offset = 0;
-       u32 inbox_offset = 0;
-       u32 outbox_size = 0;
-       u32 stream_size = 0;
-       u32 inbox_size = 0;
-       u32 debug_size = 0;
-       u32 debug_offset = 0;
-       int window_offset;
-       int i;
-
-       if (!sdev->info_window) {
-               dev_err(sdev->dev, "error: have no window info\n");
-               return;
-       }
-
-       for (i = 0; i < sdev->info_window->num_windows; i++) {
-               elem = &sdev->info_window->window[i];
-
-               window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
-               if (window_offset < 0) {
-                       dev_warn(sdev->dev, "warn: no offset for window %d\n",
-                                elem->id);
-                       continue;
-               }
-
-               switch (elem->type) {
-               case SOF_IPC_REGION_UPBOX:
-                       inbox_offset = window_offset + elem->offset;
-                       inbox_size = elem->size;
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       inbox_offset,
-                                                       elem->size, "inbox",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_DOWNBOX:
-                       outbox_offset = window_offset + elem->offset;
-                       outbox_size = elem->size;
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       outbox_offset,
-                                                       elem->size, "outbox",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_TRACE:
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       window_offset + elem->offset,
-                                                       elem->size, "etrace",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_DEBUG:
-                       debug_offset = window_offset + elem->offset;
-                       debug_size = elem->size;
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       window_offset + elem->offset,
-                                                       elem->size, "debug",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_STREAM:
-                       stream_offset = window_offset + elem->offset;
-                       stream_size = elem->size;
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       stream_offset,
-                                                       elem->size, "stream",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_REGS:
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       window_offset + elem->offset,
-                                                       elem->size, "regs",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               case SOF_IPC_REGION_EXCEPTION:
-                       sdev->dsp_oops_offset = window_offset + elem->offset;
-                       snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
-                                                       window_offset + elem->offset,
-                                                       elem->size, "exception",
-                                                       SOF_DEBUGFS_ACCESS_D0_ONLY);
-                       break;
-               default:
-                       dev_err(sdev->dev, "error: get illegal window info\n");
-                       return;
-               }
-       }
-
-       if (outbox_size == 0 || inbox_size == 0) {
-               dev_err(sdev->dev, "error: get illegal mailbox window\n");
-               return;
-       }
-
-       sdev->dsp_box.offset = inbox_offset;
-       sdev->dsp_box.size = inbox_size;
-
-       sdev->host_box.offset = outbox_offset;
-       sdev->host_box.size = outbox_size;
-
-       sdev->stream_box.offset = stream_offset;
-       sdev->stream_box.size = stream_size;
-
-       sdev->debug_box.offset = debug_offset;
-       sdev->debug_box.size = debug_size;
-
-       dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
-               inbox_offset, inbox_size);
-       dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
-               outbox_offset, outbox_size);
-       dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
-               stream_offset, stream_size);
-       dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
-               debug_offset, debug_size);
-}
-
-/* check for ABI compatibility and create memory windows on first boot */
-int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
-{
-       struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
-       int offset;
-       int ret;
-
-       /* mailbox must be on 4k boundary */
-       offset = snd_sof_dsp_get_mailbox_offset(sdev);
-       if (offset < 0) {
-               dev_err(sdev->dev, "error: have no mailbox offset\n");
-               return offset;
-       }
-
-       dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
-               msg_id, offset);
-
-       /* no need to re-check version/ABI for subsequent boots */
-       if (!sdev->first_boot)
-               return 0;
-
-       /*
-        * copy data from the DSP FW ready offset
-        * Subsequent error handling is not needed for BLK_TYPE_SRAM
-        */
-       ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
-                                    sizeof(*fw_ready));
-       if (ret) {
-               dev_err(sdev->dev,
-                       "error: unable to read fw_ready, read from TYPE_SRAM failed\n");
-               return ret;
-       }
-
-       /* make sure ABI version is compatible */
-       ret = snd_sof_ipc_valid(sdev);
-       if (ret < 0)
-               return ret;
-
-       /* now check for extended data */
-       snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
-
-       sof_get_windows(sdev);
-
-       return sof_ipc_init_msg_memory(sdev);
-}
-EXPORT_SYMBOL(sof_fw_ready);
-
-/* generic module parser for mmaped DSPs */
-int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
-                               struct snd_sof_mod_hdr *module)
-{
-       struct snd_sof_blk_hdr *block;
-       int count, ret;
-       u32 offset;
-       size_t remaining;
-
-       dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
-               module->size, module->num_blocks, module->type);
-
-       block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
-
-       /* module->size doesn't include header size */
-       remaining = module->size;
-       for (count = 0; count < module->num_blocks; count++) {
-               /* check for wrap */
-               if (remaining < sizeof(*block)) {
-                       dev_err(sdev->dev, "error: not enough data remaining\n");
-                       return -EINVAL;
-               }
-
-               /* minus header size of block */
-               remaining -= sizeof(*block);
-
-               if (block->size == 0) {
-                       dev_warn(sdev->dev,
-                                "warning: block %d size zero\n", count);
-                       dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
-                                block->type, block->offset);
-                       continue;
-               }
-
-               switch (block->type) {
-               case SOF_FW_BLK_TYPE_RSRVD0:
-               case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
-                       continue;       /* not handled atm */
-               case SOF_FW_BLK_TYPE_IRAM:
-               case SOF_FW_BLK_TYPE_DRAM:
-               case SOF_FW_BLK_TYPE_SRAM:
-                       offset = block->offset;
-                       break;
-               default:
-                       dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
-                               block->type, count);
-                       return -EINVAL;
-               }
-
-               dev_dbg(sdev->dev,
-                       "block %d type 0x%x size 0x%x ==>  offset 0x%x\n",
-                       count, block->type, block->size, offset);
-
-               /* checking block->size to avoid unaligned access */
-               if (block->size % sizeof(u32)) {
-                       dev_err(sdev->dev, "error: invalid block size 0x%x\n",
-                               block->size);
-                       return -EINVAL;
-               }
-               ret = snd_sof_dsp_block_write(sdev, block->type, offset,
-                                             block + 1, block->size);
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: write to block type 0x%x failed\n",
-                               block->type);
-                       return ret;
-               }
-
-               if (remaining < block->size) {
-                       dev_err(sdev->dev, "error: not enough data remaining\n");
-                       return -EINVAL;
-               }
-
-               /* minus body size of block */
-               remaining -= block->size;
-               /* next block */
-               block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
-                       + block->size);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
-
-static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
-                       size_t fw_offset)
-{
-       struct snd_sof_fw_header *header;
-       size_t fw_size = fw->size - fw_offset;
-
-       if (fw->size <= fw_offset) {
-               dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
-               return -EINVAL;
-       }
-
-       /* Read the header information from the data pointer */
-       header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
-
-       /* verify FW sig */
-       if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
-               dev_err(sdev->dev, "error: invalid firmware signature\n");
-               return -EINVAL;
-       }
-
-       /* check size is valid */
-       if (fw_size != header->file_size + sizeof(*header)) {
-               dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
-                       fw_size, header->file_size + sizeof(*header));
-               return -EINVAL;
-       }
-
-       dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
-               header->file_size, header->num_modules,
-               header->abi, sizeof(*header));
-
-       return 0;
-}
-
-static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
-                       size_t fw_offset)
-{
-       struct snd_sof_fw_header *header;
-       struct snd_sof_mod_hdr *module;
-       int (*load_module)(struct snd_sof_dev *sof_dev,
-                          struct snd_sof_mod_hdr *hdr);
-       int ret, count;
-       size_t remaining;
-
-       header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
-       load_module = sof_ops(sdev)->load_module;
-       if (!load_module)
-               return -EINVAL;
-
-       /* parse each module */
-       module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
-                                           sizeof(*header));
-       remaining = fw->size - sizeof(*header) - fw_offset;
-       /* check for wrap */
-       if (remaining > fw->size) {
-               dev_err(sdev->dev, "error: fw size smaller than header size\n");
-               return -EINVAL;
-       }
-
-       for (count = 0; count < header->num_modules; count++) {
-               /* check for wrap */
-               if (remaining < sizeof(*module)) {
-                       dev_err(sdev->dev, "error: not enough data remaining\n");
-                       return -EINVAL;
-               }
-
-               /* minus header size of module */
-               remaining -= sizeof(*module);
-
-               /* module */
-               ret = load_module(sdev, module);
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: invalid module %d\n", count);
-                       return ret;
-               }
-
-               if (remaining < module->size) {
-                       dev_err(sdev->dev, "error: not enough data remaining\n");
-                       return -EINVAL;
-               }
-
-               /* minus body size of module */
-               remaining -=  module->size;
-               module = (struct snd_sof_mod_hdr *)((u8 *)module
-                       + sizeof(*module) + module->size);
-       }
-
-       return 0;
-}
-
 int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
 {
        struct snd_sof_pdata *plat_data = sdev->pdata;
@@ -726,7 +45,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
        }
 
        /* check for extended manifest */
-       ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
+       ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
        if (ext_man_size > 0) {
                /* when no error occurred, drop extended manifest */
                plat_data->fw_offset = ext_man_size;
@@ -756,7 +75,7 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
                return ret;
 
        /* make sure the FW header and file is valid */
-       ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
+       ret = sdev->ipc->ops->fw_loader->validate(sdev);
        if (ret < 0) {
                dev_err(sdev->dev, "error: invalid FW header\n");
                goto error;
@@ -770,10 +89,12 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
        }
 
        /* parse and load firmware modules to DSP */
-       ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
-       if (ret < 0) {
-               dev_err(sdev->dev, "error: invalid FW modules\n");
-               goto error;
+       if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
+               ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "Firmware loading failed\n");
+                       goto error;
+               }
        }
 
        return 0;
@@ -854,6 +175,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
        dev_dbg(sdev->dev, "firmware boot complete\n");
        sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
 
+       if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration)
+               return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev);
+
        return 0;
 }
 EXPORT_SYMBOL(snd_sof_run_firmware);
index aeacf0e..a149dd1 100644 (file)
@@ -21,9 +21,20 @@ config SND_SOC_SOF_MTK_COMMON
          This option is not user-selectable but automagically handled by
          'select' statements at a higher level
 
+config SND_SOC_SOF_MT8186
+       tristate "SOF support for MT8186 audio DSP"
+       select SND_SOC_SOF_MTK_COMMON
+       depends on MTK_ADSP_IPC
+       help
+         This adds support for Sound Open Firmware for Mediatek platforms
+         using the mt8186 processors.
+         Say Y if you have such a device.
+         If unsure select "N".
+
 config SND_SOC_SOF_MT8195
        tristate "SOF support for MT8195 audio DSP"
        select SND_SOC_SOF_MTK_COMMON
+       depends on MTK_ADSP_IPC
        help
          This adds support for Sound Open Firmware for Mediatek platforms
          using the mt8195 processors.
index e8ec6da..29c5afb 100644 (file)
@@ -1,2 +1,4 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+obj-$(CONFIG_SND_SOC_SOF_MTK_COMMON) += mtk-adsp-common.o
 obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/
+obj-$(CONFIG_SND_SOC_SOF_MT8186) += mt8186/
index 6734e2c..4ab9987 100644 (file)
@@ -7,37 +7,41 @@
 #ifndef __MTK_ADSP_HELPER_H__
 #define __MTK_ADSP_HELPER_H__
 
+#include <linux/firmware/mediatek/mtk-adsp-ipc.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*/
+
+       phys_addr_t pa_secreg;
+       u32 secregsize;
+       void __iomem *va_secreg;
+
+       phys_addr_t pa_busreg;
+       u32 busregsize;
+       void __iomem *va_busreg;
 };
 
 struct adsp_priv {
        struct device *dev;
        struct snd_sof_dev *sdev;
-
-       /* DSP IPC handler */
-       struct mbox_controller *adsp_mbox;
-
+       struct mtk_adsp_ipc *dsp_ipc;
+       struct platform_device *ipc_dev;
        struct mtk_adsp_chip_info *adsp;
        struct clk **clk;
        u32 (*ap2adsp_addr)(u32 addr, void *data);
diff --git a/sound/soc/sof/mediatek/mt8186/Makefile b/sound/soc/sof/mediatek/mt8186/Makefile
new file mode 100644 (file)
index 0000000..c1f5fc4
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+snd-sof-mt8186-objs := mt8186.o mt8186-clk.o mt8186-loader.o
+obj-$(CONFIG_SND_SOC_SOF_MT8186) += snd-sof-mt8186.o
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
new file mode 100644 (file)
index 0000000..22220fd
--- /dev/null
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2022 Mediatek Corporation. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+//         Tinghan Shen <tinghan.shen@mediatek.com>
+//
+// Hardware interface for mt8186 DSP clock
+
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+
+#include "../../sof-audio.h"
+#include "../../ops.h"
+#include "../adsp_helper.h"
+#include "mt8186.h"
+#include "mt8186-clk.h"
+
+static const char *adsp_clks[ADSP_CLK_MAX] = {
+       [CLK_TOP_AUDIODSP] = "audiodsp_sel",
+       [CLK_TOP_ADSP_BUS] = "adsp_bus_sel",
+};
+
+int mt8186_adsp_init_clock(struct snd_sof_dev *sdev)
+{
+       struct adsp_priv *priv = sdev->pdata->hw_pdata;
+       struct device *dev = sdev->dev;
+       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 adsp_priv *priv = sdev->pdata->hw_pdata;
+       struct device *dev = sdev->dev;
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIODSP]);
+       if (ret) {
+               dev_err(dev, "%s clk_prepare_enable(audiodsp) fail %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP_BUS]);
+       if (ret) {
+               dev_err(dev, "%s clk_prepare_enable(adsp_bus) fail %d\n",
+                       __func__, ret);
+               clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]);
+               return ret;
+       }
+
+       return 0;
+}
+
+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_ADSP_BUS]);
+       clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]);
+}
+
+int mt8186_adsp_clock_on(struct snd_sof_dev *sdev)
+{
+       struct device *dev = sdev->dev;
+       int ret;
+
+       ret = adsp_enable_all_clock(sdev);
+       if (ret) {
+               dev_err(dev, "failed to adsp_enable_clock: %d\n", ret);
+               return ret;
+       }
+       snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN,
+                         UART_EN | DMA_EN | TIMER_EN | COREDBG_EN | CORE_CLK_EN);
+       snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL,
+                         UART_BCLK_CG | UART_RSTN);
+
+       return 0;
+}
+
+void mt8186_adsp_clock_off(struct snd_sof_dev *sdev)
+{
+       snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN, 0);
+       snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL, 0);
+       adsp_disable_all_clock(sdev);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.h b/sound/soc/sof/mediatek/mt8186/mt8186-clk.h
new file mode 100644 (file)
index 0000000..89c23ca
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ *
+ *  Header file for the mt8186 DSP clock definition
+ */
+
+#ifndef __MT8186_CLK_H
+#define __MT8186_CLK_H
+
+struct snd_sof_dev;
+
+/* DSP clock */
+enum adsp_clk_id {
+       CLK_TOP_AUDIODSP,
+       CLK_TOP_ADSP_BUS,
+       ADSP_CLK_MAX
+};
+
+int mt8186_adsp_init_clock(struct snd_sof_dev *sdev);
+int mt8186_adsp_clock_on(struct snd_sof_dev *sdev);
+void mt8186_adsp_clock_off(struct snd_sof_dev *sdev);
+#endif
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-loader.c b/sound/soc/sof/mediatek/mt8186/mt8186-loader.c
new file mode 100644 (file)
index 0000000..946e6c4
--- /dev/null
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright (c) 2022 Mediatek Corporation. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+//         Tinghan Shen <tinghan.shen@mediatek.com>
+//
+// Hardware interface for mt8186 DSP code loader
+
+#include <sound/sof.h>
+#include "mt8186.h"
+#include "../../ops.h"
+
+void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr)
+{
+       /* set RUNSTALL to stop core */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+                               RUNSTALL, RUNSTALL);
+
+       /* enable mbox 0 & 1 IRQ */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_MBOX_IRQ_EN,
+                               DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN,
+                               DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN);
+
+       /* set core boot address */
+       snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVEC_C0, boot_addr);
+       snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVECSEL, ADSP_ALTVECSEL_C0);
+
+       /* assert core reset */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+                               SW_RSTN_C0 | SW_DBG_RSTN_C0,
+                               SW_RSTN_C0 | SW_DBG_RSTN_C0);
+
+       /* hardware requirement */
+       udelay(1);
+
+       /* release core reset */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+                               SW_RSTN_C0 | SW_DBG_RSTN_C0,
+                               0);
+
+       /* clear RUNSTALL (bit31) to start core */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+                               RUNSTALL, 0);
+}
+
+void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev)
+{
+       /* set RUNSTALL to stop core */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+                               RUNSTALL, RUNSTALL);
+
+       /* assert core reset */
+       snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+                               SW_RSTN_C0 | SW_DBG_RSTN_C0,
+                               SW_RSTN_C0 | SW_DBG_RSTN_C0);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
new file mode 100644 (file)
index 0000000..3333a06
--- /dev/null
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2022 Mediatek Inc. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+//         Tinghan Shen <tinghan.shen@mediatek.com>
+
+/*
+ * Hardware interface for audio DSP on mt8186
+ */
+
+#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 "mt8186.h"
+#include "mt8186-clk.h"
+
+static int mt8186_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+       return MBOX_OFFSET;
+}
+
+static int mt8186_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+       return MBOX_OFFSET;
+}
+
+static int mt8186_send_msg(struct snd_sof_dev *sdev,
+                          struct snd_sof_ipc_msg *msg)
+{
+       struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+       sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+                         msg->msg_size);
+
+       return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ);
+}
+
+static void mt8186_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 void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc)
+{
+       struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+       mt8186_get_reply(priv->sdev);
+       snd_sof_ipc_reply(priv->sdev, 0);
+       spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc)
+{
+       struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+       u32 p; /* panic code */
+       int ret;
+
+       /* Read the message from the debug box. */
+       sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4,
+                        &p, sizeof(p));
+
+       /* 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, true);
+       } else {
+               snd_sof_ipc_msgs_rx(priv->sdev);
+
+               /* tell DSP cmd is done */
+               ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP);
+               if (ret)
+                       dev_err(priv->dev, "request send ipc failed");
+       }
+}
+
+static struct mtk_adsp_ipc_ops dsp_ops = {
+       .handle_reply           = mt8186_dsp_handle_reply,
+       .handle_request         = mt8186_dsp_handle_request,
+};
+
+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;
+       if (adsp->pa_dram & DRAM_REMAP_MASK) {
+               dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n",
+                       (u32)adsp->pa_dram);
+               return -EINVAL;
+       }
+
+       adsp->dramsize = resource_size(&res);
+       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 size=%#x\n", &adsp->pa_dram, adsp->dramsize);
+
+       mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+       if (!mmio) {
+               dev_err(dev, "no ADSP-CFG register resource\n");
+               return -ENXIO;
+       }
+
+       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 pbase=%pa size=%#x\n", &adsp->pa_cfgreg, adsp->cfgregsize);
+
+       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);
+
+       dev_dbg(dev, "sram pbase=%pa size=%#x\n", &adsp->pa_sram, adsp->sramsize);
+
+       mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sec");
+       if (!mmio) {
+               dev_err(dev, "no SEC register resource\n");
+               return -ENXIO;
+       }
+
+       adsp->va_secreg = devm_ioremap_resource(dev, mmio);
+       if (IS_ERR(adsp->va_secreg))
+               return PTR_ERR(adsp->va_secreg);
+
+       adsp->pa_secreg = (phys_addr_t)mmio->start;
+       adsp->secregsize = resource_size(mmio);
+
+       dev_dbg(dev, "secreg pbase=%pa size=%#x\n", &adsp->pa_secreg, adsp->secregsize);
+
+       mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bus");
+       if (!mmio) {
+               dev_err(dev, "no BUS register resource\n");
+               return -ENXIO;
+       }
+
+       adsp->va_busreg = devm_ioremap_resource(dev, mmio);
+       if (IS_ERR(adsp->va_busreg))
+               return PTR_ERR(adsp->va_busreg);
+
+       adsp->pa_busreg = (phys_addr_t)mmio->start;
+       adsp->busregsize = resource_size(mmio);
+
+       dev_dbg(dev, "busreg pbase=%pa size=%#x\n", &adsp->pa_busreg, adsp->busregsize);
+
+       return 0;
+}
+
+static void adsp_sram_power_on(struct snd_sof_dev *sdev)
+{
+       snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON,
+                               DSP_SRAM_POOL_PD_MASK, 0);
+}
+
+static void adsp_sram_power_off(struct snd_sof_dev *sdev)
+{
+       snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON,
+                               DSP_SRAM_POOL_PD_MASK, DSP_SRAM_POOL_PD_MASK);
+}
+
+/*  Init the basic DSP DRAM address */
+static int adsp_memory_remap_init(struct snd_sof_dev *sdev, struct mtk_adsp_chip_info *adsp)
+{
+       u32 offset;
+
+       offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW;
+       adsp->dram_offset = offset;
+       offset >>= DRAM_REMAP_SHIFT;
+
+       dev_dbg(sdev->dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset);
+
+       snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR, offset);
+       snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR, offset);
+
+       if (offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR) ||
+           offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR)) {
+               dev_err(sdev->dev, "emi remap fail\n");
+               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 mt8186_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);
+       mt8186_sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr);
+
+       return 0;
+}
+
+static int mt8186_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;
+
+       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);
+               return -ENOMEM;
+       }
+
+       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);
+               return -ENOMEM;
+       }
+
+       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");
+               return ret;
+       }
+
+       sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
+       sdev->bar[DSP_SECREG_BAR] = priv->adsp->va_secreg;
+       sdev->bar[DSP_BUSREG_BAR] = priv->adsp->va_busreg;
+
+       sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM;
+       sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+
+       /* set default mailbox offset for FW ready message */
+       sdev->dsp_box.offset = mt8186_get_mailbox_offset(sdev);
+
+       ret = adsp_memory_remap_init(sdev, priv->adsp);
+       if (ret) {
+               dev_err(sdev->dev, "adsp_memory_remap_init fail!\n");
+               return ret;
+       }
+
+       /* enable adsp clock before touching registers */
+       ret = mt8186_adsp_init_clock(sdev);
+       if (ret) {
+               dev_err(sdev->dev, "mt8186_adsp_init_clock failed\n");
+               return ret;
+       }
+
+       ret = mt8186_adsp_clock_on(sdev);
+       if (ret) {
+               dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n");
+               return ret;
+       }
+
+       adsp_sram_power_on(sdev);
+
+       priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc",
+                                                     PLATFORM_DEVID_NONE,
+                                                     pdev, sizeof(*pdev));
+       if (IS_ERR(priv->ipc_dev)) {
+               ret = IS_ERR(priv->ipc_dev);
+               dev_err(sdev->dev, "failed to create mtk-adsp-ipc device\n");
+               goto err_adsp_off;
+       }
+
+       priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+       if (!priv->dsp_ipc) {
+               ret = -EPROBE_DEFER;
+               dev_err(sdev->dev, "failed to get drvdata\n");
+               goto exit_pdev_unregister;
+       }
+
+       mtk_adsp_ipc_set_data(priv->dsp_ipc, priv);
+       priv->dsp_ipc->ops = &dsp_ops;
+
+       return 0;
+
+exit_pdev_unregister:
+       platform_device_unregister(priv->ipc_dev);
+err_adsp_off:
+       adsp_sram_power_off(sdev);
+       mt8186_adsp_clock_off(sdev);
+
+       return ret;
+}
+
+static int mt8186_dsp_remove(struct snd_sof_dev *sdev)
+{
+       struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+       platform_device_unregister(priv->ipc_dev);
+       mt8186_sof_hifixdsp_shutdown(sdev);
+       adsp_sram_power_off(sdev);
+       mt8186_adsp_clock_off(sdev);
+
+       return 0;
+}
+
+static int mt8186_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
+{
+       mt8186_sof_hifixdsp_shutdown(sdev);
+       adsp_sram_power_off(sdev);
+       mt8186_adsp_clock_off(sdev);
+
+       return 0;
+}
+
+static int mt8186_dsp_resume(struct snd_sof_dev *sdev)
+{
+       int ret;
+
+       ret = mt8186_adsp_clock_on(sdev);
+       if (ret) {
+               dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n");
+               return ret;
+       }
+
+       adsp_sram_power_on(sdev);
+
+       return ret;
+}
+
+/* on mt8186 there is 1 to 1 match between type and BAR idx */
+static int mt8186_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+       return type;
+}
+
+static int mt8186_ipc_msg_data(struct snd_sof_dev *sdev,
+                              struct snd_pcm_substream *substream,
+                              void *p, size_t sz)
+{
+       sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+       return 0;
+}
+
+/* mt8186 ops */
+static struct snd_sof_dsp_ops sof_mt8186_ops = {
+       /* probe and remove */
+       .probe          = mt8186_dsp_probe,
+       .remove         = mt8186_dsp_remove,
+
+       /* DSP core boot */
+       .run            = mt8186_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,
+
+       /* ipc */
+       .send_msg               = mt8186_send_msg,
+       .get_mailbox_offset     = mt8186_get_mailbox_offset,
+       .get_window_offset      = mt8186_get_window_offset,
+       .ipc_msg_data           = mt8186_ipc_msg_data,
+       .set_stream_data_offset = sof_set_stream_data_offset,
+
+       /* misc */
+       .get_bar_index  = mt8186_get_bar_index,
+
+       /* firmware loading */
+       .load_firmware  = snd_sof_load_firmware_memcpy,
+
+       /* Firmware ops */
+       .dsp_arch_ops = &sof_xtensa_arch_ops,
+
+       /* PM */
+       .suspend        = mt8186_dsp_suspend,
+       .resume         = mt8186_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_mt8186_desc = {
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "mediatek/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "mediatek/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-mt8186.ri",
+       },
+       .nocodec_tplg_filename = "sof-mt8186-nocodec.tplg",
+       .ops = &sof_mt8186_ops,
+};
+
+static const struct of_device_id sof_of_mt8186_ids[] = {
+       { .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc},
+       { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_mt8186_driver = {
+       .probe = sof_of_probe,
+       .remove = sof_of_remove,
+       .driver = {
+       .name = "sof-audio-of-mt8186",
+               .pm = &sof_of_pm,
+               .of_match_table = sof_of_mt8186_ids,
+       },
+};
+module_platform_driver(snd_sof_of_mt8186_driver);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_IMPORT_NS(SND_SOC_SOF_MTK_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h
new file mode 100644 (file)
index 0000000..98b2965
--- /dev/null
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ *
+ *  Header file for the mt8186 DSP register definition
+ */
+
+#ifndef __MT8186_H
+#define __MT8186_H
+
+struct mtk_adsp_chip_info;
+struct snd_sof_dev;
+
+#define DSP_REG_BAR                    4
+#define DSP_SECREG_BAR                 5
+#define DSP_BUSREG_BAR                 6
+
+/*****************************************************************************
+ *                  R E G I S T E R       TABLE
+ *****************************************************************************/
+/* dsp cfg */
+#define ADSP_CFGREG_SW_RSTN            0x0000
+#define SW_DBG_RSTN_C0                 BIT(0)
+#define SW_RSTN_C0                     BIT(4)
+#define ADSP_HIFI_IO_CONFIG            0x000C
+#define TRACEMEMREADY                  BIT(15)
+#define RUNSTALL                       BIT(31)
+#define ADSP_IRQ_MASK                  0x0030
+#define ADSP_DVFSRC_REQ                        0x0040
+#define ADSP_DDREN_REQ_0               0x0044
+#define ADSP_SEMAPHORE                 0x0064
+#define ADSP_WDT_CON_C0                        0x007C
+#define ADSP_MBOX_IRQ_EN               0x009C
+#define DSP_MBOX0_IRQ_EN               BIT(0)
+#define DSP_MBOX1_IRQ_EN               BIT(1)
+#define DSP_MBOX2_IRQ_EN               BIT(2)
+#define DSP_MBOX3_IRQ_EN               BIT(3)
+#define DSP_MBOX4_IRQ_EN               BIT(4)
+#define DSP_PDEBUGPC                   0x013C
+#define ADSP_CK_EN                     0x1000
+#define CORE_CLK_EN                    BIT(0)
+#define COREDBG_EN                     BIT(1)
+#define TIMER_EN                       BIT(3)
+#define DMA_EN                         BIT(4)
+#define UART_EN                                BIT(5)
+#define ADSP_UART_CTRL                 0x1010
+#define UART_BCLK_CG                   BIT(0)
+#define UART_RSTN                      BIT(3)
+
+/* dsp sec */
+#define ADSP_PRID                      0x0
+#define ADSP_ALTVEC_C0                 0x04
+#define ADSP_ALTVECSEL                 0x0C
+#define ADSP_ALTVECSEL_C0              BIT(1)
+
+/* dsp bus */
+#define ADSP_SRAM_POOL_CON             0x190
+#define DSP_SRAM_POOL_PD_MASK          0xF00F /* [0:3] and [12:15] */
+#define DSP_C0_EMI_MAP_ADDR            0xA00  /* ADSP Core0 To EMI Address Remap */
+#define DSP_C0_DMAEMI_MAP_ADDR         0xA08  /* DMA0 To EMI Address Remap */
+
+/* DSP memories */
+#define MBOX_OFFSET                    0x500000 /* DRAM */
+#define MBOX_SIZE                      0x1000   /* consistent with which in memory.h of sof fw */
+#define DSP_DRAM_SIZE                  0xA00000 /* 16M */
+
+/*remap dram between AP and DSP view, 4KB aligned*/
+#define SRAM_PHYS_BASE_FROM_DSP_VIEW   0x4E100000 /* MT8186 DSP view */
+#define DRAM_PHYS_BASE_FROM_DSP_VIEW   0x60000000 /* MT8186 DSP view */
+#define DRAM_REMAP_SHIFT               12
+#define DRAM_REMAP_MASK                        0xFFF
+
+#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)
+
+void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr);
+void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev);
+#endif
index 3ab12f3..30111ab 100644 (file)
 #include "../../sof-of-dev.h"
 #include "../../sof-audio.h"
 #include "../adsp_helper.h"
+#include "../mtk-adsp-common.h"
 #include "mt8195.h"
 #include "mt8195-clk.h"
 
+static int mt8195_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+       return MBOX_OFFSET;
+}
+
+static int mt8195_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+       return MBOX_OFFSET;
+}
+
+static int mt8195_send_msg(struct snd_sof_dev *sdev,
+                          struct snd_sof_ipc_msg *msg)
+{
+       struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+       sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+                         msg->msg_size);
+
+       return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ);
+}
+
+static void mt8195_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 void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc)
+{
+       struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+       mt8195_get_reply(priv->sdev);
+       snd_sof_ipc_reply(priv->sdev, 0);
+       spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc)
+{
+       struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+       u32 p; /* panic code */
+       int ret;
+
+       /* Read the message from the debug box. */
+       sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4,
+                        &p, sizeof(p));
+
+       /* 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, true);
+       } else {
+               snd_sof_ipc_msgs_rx(priv->sdev);
+
+               /* tell DSP cmd is done */
+               ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP);
+               if (ret)
+                       dev_err(priv->dev, "request send ipc failed");
+       }
+}
+
+static struct mtk_adsp_ipc_ops dsp_ops = {
+       .handle_reply           = mt8195_dsp_handle_reply,
+       .handle_request         = mt8195_dsp_handle_request,
+};
+
 static int platform_parse_resource(struct platform_device *pdev, void *data)
 {
        struct resource *mmio;
@@ -285,15 +379,36 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev)
        }
 
        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;
 
+       /* set default mailbox offset for FW ready message */
+       sdev->dsp_box.offset = mt8195_get_mailbox_offset(sdev);
+
+       priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc",
+                                                     PLATFORM_DEVID_NONE,
+                                                     pdev, sizeof(*pdev));
+       if (IS_ERR(priv->ipc_dev)) {
+               ret = PTR_ERR(priv->ipc_dev);
+               dev_err(sdev->dev, "failed to register mtk-adsp-ipc device\n");
+               goto err_adsp_sram_power_off;
+       }
+
+       priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+       if (!priv->dsp_ipc) {
+               ret = -EPROBE_DEFER;
+               dev_err(sdev->dev, "failed to get drvdata\n");
+               goto exit_pdev_unregister;
+       }
+
+       mtk_adsp_ipc_set_data(priv->dsp_ipc, priv);
+       priv->dsp_ipc->ops = &dsp_ops;
+
        return 0;
 
+exit_pdev_unregister:
+       platform_device_unregister(priv->ipc_dev);
 err_adsp_sram_power_off:
        adsp_sram_power_on(&pdev->dev, false);
 exit_clk_disable:
@@ -302,10 +417,17 @@ exit_clk_disable:
        return ret;
 }
 
+static int mt8195_dsp_shutdown(struct snd_sof_dev *sdev)
+{
+       return snd_sof_suspend(sdev->dev);
+}
+
 static int mt8195_dsp_remove(struct snd_sof_dev *sdev)
 {
        struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+       struct adsp_priv *priv = sdev->pdata->hw_pdata;
 
+       platform_device_unregister(priv->ipc_dev);
        adsp_sram_power_on(&pdev->dev, false);
        adsp_clock_off(sdev);
 
@@ -356,6 +478,39 @@ static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type)
        return type;
 }
 
+static int mt8195_ipc_msg_data(struct snd_sof_dev *sdev,
+                              struct snd_pcm_substream *substream,
+                              void *p, size_t sz)
+{
+       sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+       return 0;
+}
+
+static void mt8195_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+       u32 dbg_pc, dbg_data, dbg_bus0, dbg_bus1, dbg_inst;
+       u32 dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo, swrest;
+
+       /* dump debug registers */
+       dbg_pc = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGPC);
+       dbg_data = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGDATA);
+       dbg_bus0 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0);
+       dbg_bus1 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS1);
+       dbg_inst = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGINST);
+       dbg_ls0stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS0STAT);
+       dbg_ls1stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS1STAT);
+       faultbus = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTBUS);
+       faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO);
+       swrest = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_RESET_SW);
+
+       dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, bus0 %#x, bus1 %#x, swrest %#x",
+                dbg_pc, dbg_data, dbg_bus0, dbg_bus1, swrest);
+       dev_info(sdev->dev, "dbg_inst %#x, ls0stat %#x, ls1stat %#x, faultbus %#x, faultinfo %#x",
+                dbg_inst, dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo);
+
+       mtk_adsp_dump(sdev, flags);
+}
+
 static struct snd_soc_dai_driver mt8195_dai[] = {
 {
        .name = "SOF_DL2",
@@ -388,10 +543,11 @@ static struct snd_soc_dai_driver mt8195_dai[] = {
 };
 
 /* mt8195 ops */
-static const struct snd_sof_dsp_ops sof_mt8195_ops = {
+static struct snd_sof_dsp_ops sof_mt8195_ops = {
        /* probe and remove */
        .probe          = mt8195_dsp_probe,
        .remove         = mt8195_dsp_remove,
+       .shutdown       = mt8195_dsp_shutdown,
 
        /* DSP core boot */
        .run            = mt8195_run,
@@ -406,17 +562,25 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = {
        .write64        = sof_io_write64,
        .read64         = sof_io_read64,
 
+       /* ipc */
+       .send_msg               = mt8195_send_msg,
+       .get_mailbox_offset     = mt8195_get_mailbox_offset,
+       .get_window_offset      = mt8195_get_window_offset,
+       .ipc_msg_data           = mt8195_ipc_msg_data,
+       .set_stream_data_offset = sof_set_stream_data_offset,
+
        /* 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,
 
+       /* Debug information */
+       .dbg_dump = mt8195_adsp_dump,
+
        /* DAI drivers */
        .drv = mt8195_dai,
        .num_drv = ARRAY_SIZE(mt8195_dai),
@@ -434,11 +598,20 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = {
 };
 
 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",
+       .ipc_supported_mask     = BIT(SOF_IPC),
+       .ipc_default            = SOF_IPC,
+       .default_fw_path = {
+               [SOF_IPC] = "mediatek/sof",
+       },
+       .default_tplg_path = {
+               [SOF_IPC] = "mediatek/sof-tplg",
+       },
+       .default_fw_filename = {
+               [SOF_IPC] = "sof-mt8195.ri",
+       },
        .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg",
        .ops = &sof_mt8195_ops,
+       .ipc_timeout = 1000,
 };
 
 static const struct of_device_id sof_of_mt8195_ids[] = {
@@ -451,6 +624,7 @@ MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids);
 static struct platform_driver snd_sof_of_mt8195_driver = {
        .probe = sof_of_probe,
        .remove = sof_of_remove,
+       .shutdown = sof_of_shutdown,
        .driver = {
        .name = "sof-audio-of-mt8195",
                .pm = &sof_of_pm,
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c
new file mode 100644 (file)
index 0000000..1e0769c
--- /dev/null
@@ -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) 2022 MediaTek Inc. All rights reserved.
+//
+// Author: YC Hung <yc.hung@mediatek.com>
+
+/*
+ * Common helpers for the audio DSP on MediaTek platforms
+ */
+
+#include <linux/module.h>
+#include <sound/sof/xtensa.h>
+#include "../ops.h"
+#include "mtk-adsp-common.h"
+
+/**
+ * mtk_adsp_get_registers() - This function is called in case of DSP oops
+ * in order to gather information about the registers, filename and
+ * linenumber and stack.
+ * @sdev: SOF device
+ * @xoops: Stores information about registers.
+ * @panic_info: Stores information about filename and line number.
+ * @stack: Stores the stack dump.
+ * @stack_words: Size of the stack dump.
+ */
+static void mtk_adsp_get_registers(struct snd_sof_dev *sdev,
+                                  struct sof_ipc_dsp_oops_xtensa *xoops,
+                                  struct sof_ipc_panic_info *panic_info,
+                                  u32 *stack, size_t stack_words)
+{
+       u32 offset = sdev->dsp_oops_offset;
+
+       /* first read registers */
+       sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+       /* then get panic info */
+       if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+               dev_err(sdev->dev, "invalid header size 0x%x\n",
+                       xoops->arch_hdr.totalsize);
+               return;
+       }
+       offset += xoops->arch_hdr.totalsize;
+       sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
+
+       /* then get the stack */
+       offset += sizeof(*panic_info);
+       sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
+}
+
+/**
+ * mtk_adsp_dump() - This function is called when a panic message is
+ * received from the firmware.
+ * @sdev: SOF device
+ * @flags: parameter not used but required by ops prototype
+ */
+void mtk_adsp_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[MTK_ADSP_STACK_DUMP_SIZE];
+       u32 status;
+
+       /* Get information about the panic status from the debug box area.
+        * Compute the trace point based on the status.
+        */
+       sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, &status, 4);
+
+       /* Get information about the registers, the filename and line
+        * number and the stack.
+        */
+       mtk_adsp_get_registers(sdev, &xoops, &panic_info, stack,
+                              MTK_ADSP_STACK_DUMP_SIZE);
+
+       /* Print the information to the console */
+       sof_print_oops_and_stack(sdev, level, status, status, &xoops, &panic_info,
+                                stack, MTK_ADSP_STACK_DUMP_SIZE);
+}
+EXPORT_SYMBOL(mtk_adsp_dump);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h
new file mode 100644 (file)
index 0000000..612cff1
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef __MEDIATEK_ADSP_COMMON_H__
+#define __MEDIATEK_ADSP_COMMON_H__
+
+#define EXCEPT_MAX_HDR_SIZE    0x400
+#define MTK_ADSP_STACK_DUMP_SIZE 32
+
+void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags);
+#endif
index 235e2ef..ff066de 100644 (file)
@@ -177,7 +177,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl
                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);
+               sof_fw_trace_fw_crashed(sdev);
        } else {
                snd_sof_dsp_dbg_dump(sdev,
                                     "DSP panic (recovery will be attempted)",
index a194746..b79ae4f 100644 (file)
 #define sof_ops(sdev) \
        ((sdev)->pdata->desc->ops)
 
+static inline int sof_ops_init(struct snd_sof_dev *sdev)
+{
+       if (sdev->pdata->desc->ops_init)
+               return sdev->pdata->desc->ops_init(sdev);
+
+       return 0;
+}
+
 /* Mandatory operations are verified during probing */
 
 /* init */
@@ -367,32 +375,6 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev,
        return sof_ops(sdev)->send_msg(sdev, msg);
 }
 
-/* host DMA trace */
-static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev,
-                                        struct sof_ipc_dma_trace_params_ext *dtrace_params)
-{
-       if (sof_ops(sdev)->trace_init)
-               return sof_ops(sdev)->trace_init(sdev, dtrace_params);
-
-       return 0;
-}
-
-static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev)
-{
-       if (sof_ops(sdev)->trace_release)
-               return sof_ops(sdev)->trace_release(sdev);
-
-       return 0;
-}
-
-static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd)
-{
-       if (sof_ops(sdev)->trace_trigger)
-               return sof_ops(sdev)->trace_trigger(sdev, cmd);
-
-       return 0;
-}
-
 /* host PCM ops */
 static inline int
 snd_sof_pcm_platform_open(struct snd_sof_dev *sdev,
index 658cd89..a76d0b5 100644 (file)
@@ -82,8 +82,10 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
 }
 EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
 
-int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
-                                   struct snd_sof_pcm *spcm, int dir)
+static int
+sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
+                               struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
+                               struct snd_sof_platform_stream_params *platform_params, int dir)
 {
        struct snd_soc_dai *dai;
        int ret, j;
@@ -102,7 +104,7 @@ int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm
 
                spcm->stream[dir].list = list;
 
-               ret = sof_widget_list_setup(sdev, spcm, dir);
+               ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
                if (ret < 0) {
                        dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
                                spcm->pcm.pcm_id, dir);
@@ -150,9 +152,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
        dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
                spcm->pcm.pcm_id, substream->stream);
 
+       ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
+       if (ret < 0) {
+               dev_err(component->dev, "platform hw params failed\n");
+               return ret;
+       }
+
        /* if this is a repeated hw_params without hw_free, skip setting up widgets */
        if (!spcm->stream[substream->stream].list) {
-               ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+               ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
+                                                     substream->stream);
                if (ret < 0)
                        return ret;
        }
@@ -166,12 +175,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
                        return ret;
        }
 
-       ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
-       if (ret < 0) {
-               dev_err(component->dev, "platform hw params failed\n");
-               return ret;
-       }
-
        if (pcm_ops->hw_params) {
                ret = pcm_ops->hw_params(component, substream, params, &platform_params);
                if (ret < 0)
@@ -347,12 +350,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
                snd_sof_pcm_platform_trigger(sdev, substream, cmd);
 
        /* free PCM if reset_hw_params is set and the STOP IPC is successful */
-       if (!ret && reset_hw_params) {
+       if (!ret && reset_hw_params)
                ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
                                          free_widget_list);
-               if (ret < 0)
-                       return ret;
-       }
 
        return ret;
 }
@@ -396,7 +396,7 @@ static int sof_pcm_open(struct snd_soc_component *component,
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-       const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
+       struct snd_sof_dsp_ops *ops = sof_ops(sdev);
        struct snd_sof_pcm *spcm;
        struct snd_soc_tplg_stream_caps *caps;
        int ret;
index 1c31958..18eb327 100644 (file)
@@ -102,11 +102,18 @@ static int sof_resume(struct device *dev, bool runtime_resume)
 
        /*
         * Nothing further to be done for platforms that support the low power
-        * D0 substate.
+        * D0 substate. Resume trace and return when resuming from
+        * low-power D0 substate
         */
        if (!runtime_resume && sof_ops(sdev)->set_power_state &&
-           old_state == SOF_DSP_PM_D0)
+           old_state == SOF_DSP_PM_D0) {
+               ret = sof_fw_trace_resume(sdev);
+               if (ret < 0)
+                       /* non fatal */
+                       dev_warn(sdev->dev,
+                                "failed to enable trace after resume %d\n", ret);
                return 0;
+       }
 
        sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
 
@@ -135,8 +142,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                return ret;
        }
 
-       /* resume DMA trace, only need send ipc */
-       ret = snd_sof_init_trace_ipc(sdev);
+       /* resume DMA trace */
+       ret = sof_fw_trace_resume(sdev);
        if (ret < 0) {
                /* non fatal */
                dev_warn(sdev->dev,
@@ -187,7 +194,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
 
        /* prepare for streams to be resumed properly upon resume */
        if (!runtime_suspend) {
-               ret = sof_set_hw_params_upon_resume(sdev->dev);
+               ret = snd_sof_dsp_hw_params_upon_resume(sdev);
                if (ret < 0) {
                        dev_err(sdev->dev,
                                "error: setting hw_params flag during suspend %d\n",
@@ -201,6 +208,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
 
        /* Skip to platform-specific suspend if DSP is entering D0 */
        if (target_state == SOF_DSP_PM_D0) {
+               sof_fw_trace_suspend(sdev, pm_state);
                /* Notify clients not managed by pm framework about core suspend */
                sof_suspend_clients(sdev, pm_state);
                goto suspend;
@@ -209,8 +217,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
        if (tplg_ops->tear_down_all_pipelines)
                tplg_ops->tear_down_all_pipelines(sdev, false);
 
-       /* release trace */
-       snd_sof_release_trace(sdev);
+       /* suspend DMA trace */
+       sof_fw_trace_suspend(sdev, pm_state);
 
        /* Notify clients not managed by pm framework about core suspend */
        sof_suspend_clients(sdev, pm_state);
index 74982c0..1b04dcb 100644 (file)
@@ -74,20 +74,20 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
 
        sof_pdata->desc = desc;
        sof_pdata->dev = &pdev->dev;
-       sof_pdata->fw_filename = desc->default_fw_filename;
+       sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC];
 
        /* alternate fw and tplg filenames ? */
        if (fw_path)
                sof_pdata->fw_filename_prefix = fw_path;
        else
                sof_pdata->fw_filename_prefix =
-                       sof_pdata->desc->default_fw_path;
+                       sof_pdata->desc->default_fw_path[SOF_IPC];
 
        if (tplg_path)
                sof_pdata->tplg_filename_prefix = tplg_path;
        else
                sof_pdata->tplg_filename_prefix =
-                       sof_pdata->desc->default_tplg_path;
+                       sof_pdata->desc->default_tplg_path[SOF_IPC];
 
        /* set callback to be called on successful device probe to enable runtime_pm */
        sof_pdata->sof_probe_complete = sof_acpi_probe_complete;
index b2f009a..8d74063 100644 (file)
 #include "sof-audio.h"
 #include "ops.h"
 
-static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
-{
-       int ret;
-
-       /* reset readback offset for scontrol */
-       scontrol->readback_offset = 0;
-
-       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);
-
-       return ret;
-}
-
-static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
-{
-       struct snd_sof_control *scontrol;
-       int ret;
-
-       /* set up all controls for the widget */
-       list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
-               if (scontrol->comp_id == swidget->comp_id) {
-                       /* 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;
-}
-
 static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
 {
+       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
        struct snd_sof_route *sroute;
 
        list_for_each_entry(sroute, &sdev->route_list, list)
-               if (sroute->src_widget == widget || sroute->sink_widget == widget)
+               if (sroute->src_widget == widget || sroute->sink_widget == widget) {
+                       if (sroute->setup && tplg_ops->route_free)
+                               tplg_ops->route_free(sdev, sroute);
+
                        sroute->setup = false;
+               }
 }
 
 int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
@@ -82,6 +39,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
        if (--swidget->use_count)
                return 0;
 
+       /* reset route setup status for all routes that contain this widget */
+       sof_reset_route_setup_status(sdev, swidget);
+
        /* continue to disable core even if IPC fails */
        if (tplg_ops->widget_free)
                err = tplg_ops->widget_free(sdev, swidget);
@@ -98,10 +58,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
                        err = ret;
        }
 
-       /* reset route setup status for all routes that contain this widget */
-       sof_reset_route_setup_status(sdev, swidget);
-       swidget->complete = 0;
-
        /*
         * free the scheduler widget (same as pipe_widget) associated with the current swidget.
         * skip for static pipelines
@@ -110,6 +66,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
                ret = sof_widget_free(sdev, swidget->pipe_widget);
                if (ret < 0 && !err)
                        err = ret;
+               swidget->pipe_widget->complete = 0;
        }
 
        if (!err)
@@ -179,11 +136,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
        }
 
        /* restore kcontrols for widget */
-       ret = sof_widget_kcontrol_setup(sdev, swidget);
-       if (ret < 0) {
-               dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
-                       swidget->widget->name);
-               goto widget_free;
+       if (tplg_ops->control->widget_kcontrol_setup) {
+               ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
+               if (ret < 0)
+                       goto widget_free;
        }
 
        dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
@@ -301,28 +257,249 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
        return 0;
 }
 
-int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+static void
+sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
+{
+       const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+       const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+       struct snd_sof_widget *swidget = widget->dobj.private;
+       struct snd_soc_dapm_path *p;
+
+       if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
+               goto sink_unprepare;
+
+       /* unprepare the source widget */
+       widget_ops[widget->id].ipc_unprepare(swidget);
+       swidget->prepared = false;
+
+sink_unprepare:
+       /* unprepare all widgets in the sink paths */
+       snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+               if (!p->walking && p->sink->dobj.private) {
+                       p->walking = true;
+                       sof_unprepare_widgets_in_path(sdev, p->sink);
+                       p->walking = false;
+               }
+       }
+}
+
+static int
+sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+                           struct snd_pcm_hw_params *fe_params,
+                           struct snd_sof_platform_stream_params *platform_params,
+                           struct snd_pcm_hw_params *pipeline_params, int dir)
+{
+       const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+       const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+       struct snd_sof_widget *swidget = widget->dobj.private;
+       struct snd_soc_dapm_path *p;
+       int ret;
+
+       if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
+               goto sink_prepare;
+
+       /* prepare the source widget */
+       ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
+                                            pipeline_params, dir);
+       if (ret < 0) {
+               dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
+               return ret;
+       }
+
+       swidget->prepared = true;
+
+sink_prepare:
+       /* prepare all widgets in the sink paths */
+       snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+               if (!p->walking && p->sink->dobj.private) {
+                       p->walking = true;
+                       ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,
+                                                         platform_params, pipeline_params, dir);
+                       p->walking = false;
+                       if (ret < 0) {
+                               /* unprepare the source widget */
+                               if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
+                                       widget_ops[widget->id].ipc_unprepare(swidget);
+                                       swidget->prepared = false;
+                               }
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * free all widgets in the sink path starting from the source widget
+ * (DAI type for capture, AIF type for playback)
+ */
+static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+                                   int dir)
+{
+       struct snd_soc_dapm_path *p;
+       int err;
+       int ret = 0;
+
+       /* free all widgets even in case of error to keep use counts balanced */
+       snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+               if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+                       p->walking = true;
+                       if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
+                               err = sof_widget_free(sdev, widget->dobj.private);
+                               if (err < 0)
+                                       ret = err;
+                       }
+
+                       err = sof_widget_free(sdev, p->sink->dobj.private);
+                       if (err < 0)
+                               ret = err;
+
+                       err = sof_free_widgets_in_path(sdev, p->sink, dir);
+                       if (err < 0)
+                               ret = err;
+                       p->walking = false;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * set up all widgets in the sink path starting from the source widget
+ * (DAI type for capture, AIF type for playback).
+ * The error path in this function ensures that all successfully set up widgets getting freed.
+ */
+static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+                                     int dir)
+{
+       struct snd_soc_dapm_path *p;
+       int ret;
+
+       snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+               if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+                       p->walking = true;
+                       if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
+                               ret = sof_widget_setup(sdev, widget->dobj.private);
+                               if (ret < 0)
+                                       goto out;
+                       }
+
+                       ret = sof_widget_setup(sdev, p->sink->dobj.private);
+                       if (ret < 0) {
+                               if (WIDGET_IS_AIF_OR_DAI(widget->id))
+                                       sof_widget_free(sdev, widget->dobj.private);
+                               goto out;
+                       }
+
+                       ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
+                       if (ret < 0) {
+                               if (WIDGET_IS_AIF_OR_DAI(widget->id))
+                                       sof_widget_free(sdev, widget->dobj.private);
+                               sof_widget_free(sdev, p->sink->dobj.private);
+                       }
+out:
+                       p->walking = false;
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int
+sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
+                         struct snd_pcm_hw_params *fe_params,
+                         struct snd_sof_platform_stream_params *platform_params, int dir,
+                         enum sof_widget_op op)
+{
+       struct snd_soc_dapm_widget *widget;
+       char *str;
+       int ret = 0;
+       int i;
+
+       for_each_dapm_widgets(list, i, widget) {
+               /* starting widget for playback is AIF type */
+               if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
+                       continue;
+
+               /* starting widget for capture is DAI type */
+               if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
+                       continue;
+
+               switch (op) {
+               case SOF_WIDGET_SETUP:
+                       ret = sof_set_up_widgets_in_path(sdev, widget, dir);
+                       str = "set up";
+                       break;
+               case SOF_WIDGET_FREE:
+                       ret = sof_free_widgets_in_path(sdev, widget, dir);
+                       str = "free";
+                       break;
+               case SOF_WIDGET_PREPARE:
+               {
+                       struct snd_pcm_hw_params pipeline_params;
+
+                       str = "prepare";
+                       /*
+                        * When walking the list of connected widgets, the pipeline_params for each
+                        * widget is modified by the source widget in the path. Use a local
+                        * copy of the runtime params as the pipeline_params so that the runtime
+                        * params does not get overwritten.
+                        */
+                       memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
+
+                       ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
+                                                         platform_params, &pipeline_params, dir);
+                       break;
+               }
+               case SOF_WIDGET_UNPREPARE:
+                       sof_unprepare_widgets_in_path(sdev, widget);
+                       break;
+               default:
+                       dev_err(sdev->dev, "Invalid widget op %d\n", op);
+                       return -EINVAL;
+               }
+               if (ret < 0) {
+                       dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+                         struct snd_pcm_hw_params *fe_params,
+                         struct snd_sof_platform_stream_params *platform_params,
+                         int dir)
 {
        const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
        struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
        struct snd_soc_dapm_widget *widget;
-       int i, ret, num_widgets;
+       int i, ret;
 
        /* nothing to set up */
        if (!list)
                return 0;
 
-       /* set up widgets in the list */
-       for_each_dapm_widgets(list, num_widgets, widget) {
-               struct snd_sof_widget *swidget = widget->dobj.private;
-
-               if (!swidget)
-                       continue;
+       /*
+        * Prepare widgets for set up. The prepare step is used to allocate memory, assign
+        * instance ID and pick the widget configuration based on the runtime PCM params.
+        */
+       ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+                                       dir, SOF_WIDGET_PREPARE);
+       if (ret < 0)
+               return ret;
 
-               /* set up the widget */
-               ret = sof_widget_setup(sdev, swidget);
-               if (ret < 0)
-                       goto widget_free;
+       /* Set up is used to send the IPC to the DSP to create the widget */
+       ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+                                       dir, SOF_WIDGET_SETUP);
+       if (ret < 0) {
+               ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+                                               dir, SOF_WIDGET_UNPREPARE);
+               return ret;
        }
 
        /*
@@ -364,18 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
        return 0;
 
 widget_free:
-       /* free all widgets that have been set up successfully */
-       for_each_dapm_widgets(list, i, widget) {
-               struct snd_sof_widget *swidget = widget->dobj.private;
-
-               if (!swidget)
-                       continue;
-
-               if (!num_widgets--)
-                       break;
-
-               sof_widget_free(sdev, swidget);
-       }
+       sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
+                                 SOF_WIDGET_FREE);
+       sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
 
        return ret;
 }
@@ -383,37 +551,22 @@ widget_free:
 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
 {
        struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
-       struct snd_soc_dapm_widget *widget;
-       int i, ret;
-       int ret1 = 0;
+       int ret;
 
        /* nothing to free */
        if (!list)
                return 0;
 
-       /*
-        * Free widgets in the list. This can fail but continue freeing other widgets to keep
-        * use_counts balanced.
-        */
-       for_each_dapm_widgets(list, i, widget) {
-               struct snd_sof_widget *swidget = widget->dobj.private;
-
-               if (!swidget)
-                       continue;
+       /* send IPC to free widget in the DSP */
+       ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
 
-               /*
-                * free widget and its pipe_widget. Either of these can fail, but free as many as
-                * possible before freeing the list and returning the error.
-                */
-               ret = sof_widget_free(sdev, swidget);
-               if (ret < 0)
-                       ret1 = ret;
-       }
+       /* unprepare the widget */
+       sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
 
        snd_soc_dapm_dai_free_widgets(&list);
        spcm->stream[dir].list = NULL;
 
-       return ret1;
+       return ret;
 }
 
 /*
@@ -462,42 +615,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
        return false;
 }
 
-int sof_set_hw_params_upon_resume(struct device *dev)
-{
-       struct snd_sof_dev *sdev = dev_get_drvdata(dev);
-       struct snd_pcm_substream *substream;
-       struct snd_sof_pcm *spcm;
-       snd_pcm_state_t state;
-       int dir;
-
-       /*
-        * SOF requires hw_params to be set-up internally upon resume.
-        * So, set the flag to indicate this for those streams that
-        * have been suspended.
-        */
-       list_for_each_entry(spcm, &sdev->pcm_list, list) {
-               for_each_pcm_streams(dir) {
-                       /*
-                        * do not reset hw_params upon resume for streams that
-                        * were kept running during suspend
-                        */
-                       if (spcm->stream[dir].suspend_ignored)
-                               continue;
-
-                       substream = spcm->stream[dir].substream;
-                       if (!substream || !substream->runtime)
-                               continue;
-
-                       state = substream->runtime->status->state;
-                       if (state == SNDRV_PCM_STATE_SUSPENDED)
-                               spcm->prepared[dir] = false;
-               }
-       }
-
-       /* set internal flag for BE */
-       return snd_sof_dsp_hw_params_upon_resume(sdev);
-}
-
 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)
 {
@@ -701,7 +818,8 @@ int sof_machine_check(struct snd_sof_dev *sdev)
                return -ENOMEM;
 
        mach->drv_name = "sof-nocodec";
-       sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+       if (!sof_pdata->tplg_filename)
+               sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
 
        sof_pdata->machine = mach;
        snd_sof_set_mach_params(mach, sdev);
index 7f15b3b..27cc5fb 100644 (file)
 #define DMA_CHAN_INVALID       0xFFFFFFFF
 
 #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
+#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
 
 #define SOF_DAI_CLK_INTEL_SSP_MCLK     0
 #define SOF_DAI_CLK_INTEL_SSP_BCLK     1
 
+enum sof_widget_op {
+       SOF_WIDGET_PREPARE,
+       SOF_WIDGET_SETUP,
+       SOF_WIDGET_FREE,
+       SOF_WIDGET_UNPREPARE,
+};
+
 /*
  * Volume fractional word length define to 16 sets
  * the volume linear gain value to use Qx.16 format
  */
 #define VOLUME_FWL     16
 
+#define SOF_TLV_ITEMS 3
+
+static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
+{
+       if (value >= size)
+               return volume_map[size - 1];
+
+       return volume_map[value];
+}
+
+static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
+{
+       int i;
+
+       for (i = 0; i < size; i++) {
+               if (volume_map[i] >= value)
+                       return i;
+       }
+
+       return i - 1;
+}
+
 struct snd_sof_widget;
 struct snd_sof_route;
 struct snd_sof_control;
@@ -86,6 +117,11 @@ struct sof_ipc_tplg_control_ops {
                             const unsigned int __user *binary_data, unsigned int size);
        /* update control data based on notification from the DSP */
        void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message);
+       /* Optional callback to setup kcontrols associated with an swidget */
+       int (*widget_kcontrol_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+       /* mandatory callback to set up volume table for volume kcontrols */
+       int (*set_up_volume_table)(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS],
+                                  int size);
 };
 
 /**
@@ -95,6 +131,8 @@ struct sof_ipc_tplg_control_ops {
  * @token_list: List of token ID's that should be parsed for the widget
  * @token_list_size: number of elements in token_list
  * @bind_event: Function pointer for binding events to the widget
+ * @ipc_prepare: Optional op for preparing a widget for set up
+ * @ipc_unprepare: Optional op for unpreparing a widget
  */
 struct sof_ipc_tplg_widget_ops {
        int (*ipc_setup)(struct snd_sof_widget *swidget);
@@ -103,6 +141,11 @@ struct sof_ipc_tplg_widget_ops {
        int token_list_size;
        int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
                          u16 event_type);
+       int (*ipc_prepare)(struct snd_sof_widget *swidget,
+                          struct snd_pcm_hw_params *fe_params,
+                          struct snd_sof_platform_stream_params *platform_params,
+                          struct snd_pcm_hw_params *source_params, int dir);
+       void (*ipc_unprepare)(struct snd_sof_widget *swidget);
 };
 
 /**
@@ -112,6 +155,7 @@ struct sof_ipc_tplg_widget_ops {
  *         initialized to 0.
  * @control: Pointer to the IPC-specific ops for topology kcontrol IO
  * @route_setup: Function pointer for setting up pipeline connections
+ * @route_free: Optional op for freeing pipeline connections.
  * @token_list: List of all tokens supported by the IPC version. The size of the token_list
  *             array should be SOF_TOKEN_COUNT. The unused elements in the array will be
  *             initialized to 0.
@@ -129,6 +173,7 @@ struct sof_ipc_tplg_ops {
        const struct sof_ipc_tplg_widget_ops *widget;
        const struct sof_ipc_tplg_control_ops *control;
        int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
+       int (*route_free)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
        const struct sof_token_info *token_list;
        int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
        int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
@@ -247,7 +292,6 @@ struct snd_sof_control {
        int max_volume_step; /* max volume step for volume_table */
        int num_channels;
        unsigned int access;
-       u32 readback_offset; /* offset to mmapped data if used */
        int info_type;
        int index; /* pipeline ID */
        void *priv; /* private data copied from topology */
@@ -292,10 +336,24 @@ struct snd_sof_widget {
        struct snd_soc_component *scomp;
        int comp_id;
        int pipeline_id;
+       /*
+        * complete flag is used to indicate that pipeline set up is complete for scheduler type
+        * widgets. It is unused for all other widget types.
+        */
        int complete;
+       /*
+        * the prepared flag is used to indicate that a widget has been prepared for getting set
+        * up in the DSP.
+        */
+       bool prepared;
        int use_count; /* use_count will be protected by the PCM mutex held by the core */
        int core;
-       int id;
+       int id; /* id is the DAPM widget type */
+       /*
+        * Instance ID is set dynamically when the widget gets set up in the FW. It should be
+        * unique for each module type across all pipelines. This will not be used in SOF_IPC.
+        */
+       int instance_id;
 
        /*
         * Flag indicating if the widget should be set up dynamically when a PCM is opened.
@@ -310,6 +368,7 @@ struct snd_sof_widget {
        struct snd_soc_dapm_widget *widget;
        struct list_head list;  /* list in sdev widget list */
        struct snd_sof_widget *pipe_widget;
+       void *module_info;
 
        const guid_t uuid;
 
@@ -403,8 +462,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp,
                                          struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-
-       struct snd_sof_pcm *spcm = NULL;
+       struct snd_sof_pcm *spcm;
 
        list_for_each_entry(spcm, &sdev->pcm_list, list) {
                if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id)
@@ -430,16 +488,10 @@ static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstre
 static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { }
 #endif
 
-/*
- * Mixer IPC
- */
-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);
 
 /* PM */
-int sof_set_hw_params_upon_resume(struct device *dev);
 bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
 bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
 
@@ -453,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
                    struct snd_soc_dapm_widget *wsink);
 
 /* PCM */
-int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+                         struct snd_pcm_hw_params *fe_params,
+                         struct snd_sof_platform_stream_params *platform_params,
+                         int dir);
 int sof_widget_list_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);
@@ -467,6 +522,5 @@ int get_token_uuid(void *elem, void *object, u32 offset);
 int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
                          struct snd_sof_tuple *tuples, int num_tuples,
                          size_t object_size, int token_instance_num);
-int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
-                                   struct snd_sof_pcm *spcm, int dir);
+u32 vol_compute_gain(u32 value, int *tlv);
 #endif
index db3a052..4bdecd8 100644 (file)
@@ -217,10 +217,9 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf
                        ipc_count = MAX_IPC_FLOOD_COUNT;
        }
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0 && ret != -EACCES) {
                dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
-               pm_runtime_put_noidle(dev);
                goto out;
        }
 
index dba6cfd..03490a4 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
 
 #include "sof-client.h"
 
@@ -22,6 +23,8 @@
 
 struct sof_msg_inject_priv {
        struct dentry *dfs_file;
+       size_t max_msg_size;
+       enum sof_ipc_type ipc_type;
 
        void *tx_buffer;
        void *rx_buffer;
@@ -66,44 +69,157 @@ static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer,
        return count;
 }
 
-static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
-                                       size_t count, loff_t *ppos)
+static ssize_t sof_msg_inject_ipc4_dfs_read(struct file *file,
+                                           char __user *buffer,
+                                           size_t count, loff_t *ppos)
 {
        struct sof_client_dev *cdev = file->private_data;
        struct sof_msg_inject_priv *priv = cdev->data;
-       struct device *dev = &cdev->auxdev.dev;
-       int ret, err;
-       size_t size;
+       struct sof_ipc4_msg *ipc4_msg = priv->rx_buffer;
+       size_t header_size = sizeof(ipc4_msg->header_u64);
+       size_t remaining;
 
-       if (*ppos)
+       if (!ipc4_msg->header_u64 || !count || *ppos)
                return 0;
 
-       size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE,
-                                     ppos, buffer, count);
-       if (size != count)
-               return size > 0 ? -EFAULT : size;
+       /* we need space for the header at minimum (u64) */
+       if (count < header_size)
+               return -ENOSPC;
+
+       remaining = header_size;
+
+       /* Only get large config have payload */
+       if (SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_msg->primary) &&
+           (SOF_IPC4_MSG_TYPE_GET(ipc4_msg->primary) == SOF_IPC4_MOD_LARGE_CONFIG_GET))
+               remaining += ipc4_msg->data_size;
+
+       if (count > remaining)
+               count = remaining;
+       else if (count < remaining)
+               remaining = count;
+
+       /* copy the header first */
+       if (copy_to_user(buffer, &ipc4_msg->header_u64, header_size))
+               return -EFAULT;
+
+       *ppos += header_size;
+       remaining -= header_size;
+
+       if (!remaining)
+               return count;
+
+       if (remaining > ipc4_msg->data_size)
+               remaining = ipc4_msg->data_size;
+
+       /* Copy the payload */
+       if (copy_to_user(buffer + *ppos, ipc4_msg->data_ptr, remaining))
+               return -EFAULT;
+
+       *ppos += remaining;
+       return count;
+}
 
-       ret = pm_runtime_get_sync(dev);
+static int sof_msg_inject_send_message(struct sof_client_dev *cdev)
+{
+       struct sof_msg_inject_priv *priv = cdev->data;
+       struct device *dev = &cdev->auxdev.dev;
+       int ret, err;
+
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0 && ret != -EACCES) {
                dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
-               pm_runtime_put_noidle(dev);
                return ret;
        }
 
        /* send the message */
-       memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE);
        ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
-                                       SOF_IPC_MSG_MAX_SIZE);
+                                       priv->max_msg_size);
+       if (ret)
+               dev_err(dev, "IPC message send failed: %d\n", ret);
+
        pm_runtime_mark_last_busy(dev);
        err = pm_runtime_put_autosuspend(dev);
        if (err < 0)
                dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
 
-       /* return size if test is successful */
-       if (ret >= 0)
-               ret = size;
-
        return ret;
+}
+
+static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
+                                       size_t count, loff_t *ppos)
+{
+       struct sof_client_dev *cdev = file->private_data;
+       struct sof_msg_inject_priv *priv = cdev->data;
+       size_t size;
+       int ret;
+
+       if (*ppos)
+               return 0;
+
+       size = simple_write_to_buffer(priv->tx_buffer, priv->max_msg_size,
+                                     ppos, buffer, count);
+       if (size != count)
+               return size > 0 ? -EFAULT : size;
+
+       memset(priv->rx_buffer, 0, priv->max_msg_size);
+
+       ret = sof_msg_inject_send_message(cdev);
+
+       /* return the error code if test failed */
+       if (ret < 0)
+               size = ret;
+
+       return size;
+};
+
+static ssize_t sof_msg_inject_ipc4_dfs_write(struct file *file,
+                                            const char __user *buffer,
+                                            size_t count, loff_t *ppos)
+{
+       struct sof_client_dev *cdev = file->private_data;
+       struct sof_msg_inject_priv *priv = cdev->data;
+       struct sof_ipc4_msg *ipc4_msg = priv->tx_buffer;
+       size_t size;
+       int ret;
+
+       if (*ppos)
+               return 0;
+
+       if (count < sizeof(ipc4_msg->header_u64))
+               return -EINVAL;
+
+       /* copy the header first */
+       size = simple_write_to_buffer(&ipc4_msg->header_u64,
+                                     sizeof(ipc4_msg->header_u64),
+                                     ppos, buffer, count);
+       if (size != sizeof(ipc4_msg->header_u64))
+               return size > 0 ? -EFAULT : size;
+
+       count -= size;
+       if (!count) {
+               /* Copy the payload */
+               size = simple_write_to_buffer(ipc4_msg->data_ptr,
+                                             priv->max_msg_size, ppos, buffer,
+                                             count);
+               if (size != count)
+                       return size > 0 ? -EFAULT : size;
+       }
+
+       ipc4_msg->data_size = count;
+
+       /* Initialize the reply storage */
+       ipc4_msg = priv->rx_buffer;
+       ipc4_msg->header_u64 = 0;
+       ipc4_msg->data_size = priv->max_msg_size;
+       memset(ipc4_msg->data_ptr, 0, priv->max_msg_size);
+
+       ret = sof_msg_inject_send_message(cdev);
+
+       /* return the error code if test failed */
+       if (ret < 0)
+               size = ret;
+
+       return size;
 };
 
 static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
@@ -123,28 +239,61 @@ static const struct file_operations sof_msg_inject_fops = {
        .owner = THIS_MODULE,
 };
 
+static const struct file_operations sof_msg_inject_ipc4_fops = {
+       .open = sof_msg_inject_dfs_open,
+       .read = sof_msg_inject_ipc4_dfs_read,
+       .write = sof_msg_inject_ipc4_dfs_write,
+       .llseek = default_llseek,
+       .release = sof_msg_inject_dfs_release,
+
+       .owner = THIS_MODULE,
+};
+
 static int sof_msg_inject_probe(struct auxiliary_device *auxdev,
                                const struct auxiliary_device_id *id)
 {
        struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
        struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
+       static const struct file_operations *fops;
        struct device *dev = &auxdev->dev;
        struct sof_msg_inject_priv *priv;
+       size_t alloc_size;
 
        /* allocate memory for client data */
        priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
-       priv->rx_buffer = devm_kzalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+       priv->ipc_type = sof_client_get_ipc_type(cdev);
+       priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
+       alloc_size = priv->max_msg_size;
+
+       if (priv->ipc_type == SOF_INTEL_IPC4)
+               alloc_size += sizeof(struct sof_ipc4_msg);
+
+       priv->tx_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL);
+       priv->rx_buffer = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
        if (!priv->tx_buffer || !priv->rx_buffer)
                return -ENOMEM;
 
+       if (priv->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_msg *ipc4_msg;
+
+               ipc4_msg = priv->tx_buffer;
+               ipc4_msg->data_ptr = priv->tx_buffer + sizeof(struct sof_ipc4_msg);
+
+               ipc4_msg = priv->rx_buffer;
+               ipc4_msg->data_ptr = priv->rx_buffer + sizeof(struct sof_ipc4_msg);
+
+               fops = &sof_msg_inject_ipc4_fops;
+       } else {
+               fops = &sof_msg_inject_fops;
+       }
+
        cdev->data = priv;
 
        priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root,
-                                            cdev, &sof_msg_inject_fops);
+                                            cdev, fops);
 
        /* enable runtime PM */
        pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
index 797dedb..34e6bd3 100644 (file)
@@ -132,6 +132,7 @@ static int sof_probes_deinit(struct sof_client_dev *cdev)
 static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
                           void **params, size_t *num_params)
 {
+       size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
        struct sof_ipc_probe_info_params msg = {{{0}}};
        struct sof_ipc_probe_info_params *reply;
        size_t bytes;
@@ -140,13 +141,13 @@ static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
        *params = NULL;
        *num_params = 0;
 
-       reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+       reply = kzalloc(max_msg_size, GFP_KERNEL);
        if (!reply)
                return -ENOMEM;
        msg.rhdr.hdr.size = sizeof(msg);
        msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
 
-       ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE);
+       ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size);
        if (ret < 0 || reply->rhdr.error < 0)
                goto exit;
 
@@ -503,10 +504,9 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
        if (!buf)
                return -ENOMEM;
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0 && ret != -EACCES) {
                dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
-               pm_runtime_put_noidle(dev);
                goto exit;
        }
 
@@ -568,10 +568,9 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
 
        desc = (struct sof_probe_point_desc *)tkns;
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0 && ret != -EACCES) {
                dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
-               pm_runtime_put_noidle(dev);
                goto exit;
        }
 
@@ -621,10 +620,9 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
                goto exit;
        }
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0) {
                dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
-               pm_runtime_put_noidle(dev);
                goto exit;
        }
 
index 686ad0c..16cca66 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <sound/sof/ipc4/header.h>
 #include "ops.h"
 #include "sof-client.h"
 #include "sof-priv.h"
@@ -72,6 +73,9 @@ static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
        int ret = 0;
        int i;
 
+       if (sdev->pdata->ipc_type != SOF_IPC)
+               return 0;
+
        for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
                ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
                if (ret < 0)
@@ -245,10 +249,19 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
                              void *reply_data, size_t reply_bytes)
 {
-       struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+       if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
+               struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+
+               return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
+                                         reply_data, reply_bytes);
+       } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_msg *msg = ipc_msg;
 
-       return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size,
-                                 reply_data, reply_bytes);
+               return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
+                                         reply_data, reply_bytes);
+       }
+
+       return -EINVAL;
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
 
@@ -319,6 +332,22 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
 
+size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev)
+{
+       struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+       return sdev->ipc->max_payload_size;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT);
+
+enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
+{
+       struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+       return sdev->pdata->ipc_type;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT);
+
 /* module refcount management of SOF core */
 int sof_client_core_module_get(struct sof_client_dev *cdev)
 {
@@ -342,9 +371,22 @@ EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT);
 /* IPC event handling */
 void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
 {
-       struct sof_ipc_cmd_hdr *hdr = msg_buf;
-       u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
        struct sof_ipc_event_entry *event;
+       u32 msg_type;
+
+       if (sdev->pdata->ipc_type == SOF_IPC) {
+               struct sof_ipc_cmd_hdr *hdr = msg_buf;
+
+               msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
+       } else if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               struct sof_ipc4_msg *msg = msg_buf;
+
+               msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+       } else {
+               dev_dbg_once(sdev->dev, "%s: Not supported IPC version: %d\n",
+                            __func__, sdev->pdata->ipc_type);
+               return;
+       }
 
        mutex_lock(&sdev->client_event_handler_mutex);
 
@@ -363,9 +405,21 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
        struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
        struct sof_ipc_event_entry *event;
 
-       if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK))
+       if (!callback)
                return -EINVAL;
 
+       if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
+               if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
+                       return -EINVAL;
+       } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+               if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
+                       return -EINVAL;
+       } else {
+               dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n",
+                        __func__, sdev->pdata->ipc_type);
+               return -EINVAL;
+       }
+
        event = kmalloc(sizeof(*event), GFP_KERNEL);
        if (!event)
                return -ENOMEM;
index 4b6394b..46b215d 100644 (file)
@@ -41,6 +41,8 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
 const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev);
+size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev);
+enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev);
 
 /* module refcount management of SOF core */
 int sof_client_core_module_get(struct sof_client_dev *cdev);
index e371863..53faecc 100644 (file)
@@ -64,17 +64,17 @@ int sof_of_probe(struct platform_device *pdev)
 
        sof_pdata->desc = desc;
        sof_pdata->dev = &pdev->dev;
-       sof_pdata->fw_filename = desc->default_fw_filename;
+       sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC];
 
        if (fw_path)
                sof_pdata->fw_filename_prefix = fw_path;
        else
-               sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path;
+               sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path[SOF_IPC];
 
        if (tplg_path)
                sof_pdata->tplg_filename_prefix = tplg_path;
        else
-               sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path;
+               sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path[SOF_IPC];
 
        /* set callback to be called on successful device probe to enable runtime_pm */
        sof_pdata->sof_probe_complete = sof_of_probe_complete;
@@ -95,4 +95,10 @@ int sof_of_remove(struct platform_device *pdev)
 }
 EXPORT_SYMBOL(sof_of_remove);
 
+void sof_of_shutdown(struct platform_device *pdev)
+{
+       snd_sof_device_shutdown(&pdev->dev);
+}
+EXPORT_SYMBOL(sof_of_shutdown);
+
 MODULE_LICENSE("Dual BSD/GPL");
index 4e0f658..fd950a2 100644 (file)
@@ -13,5 +13,6 @@ 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);
+void sof_of_shutdown(struct platform_device *pdev);
 
 #endif
index 7fa2649..d627092 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/dmi.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/platform_data/x86/soc.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc-acpi.h>
 #include <sound/soc-acpi-intel-match.h>
@@ -23,21 +24,34 @@ static char *fw_path;
 module_param(fw_path, charp, 0444);
 MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware.");
 
+static char *fw_filename;
+module_param(fw_filename, charp, 0444);
+MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware.");
+
 static char *tplg_path;
 module_param(tplg_path, charp, 0444);
 MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
 
+static char *tplg_filename;
+module_param(tplg_filename, charp, 0444);
+MODULE_PARM_DESC(tplg_filename, "alternate filename for SOF topology.");
+
 static int sof_pci_debug;
 module_param_named(sof_pci_debug, sof_pci_debug, int, 0444);
 MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)");
 
-static const char *sof_override_tplg_name;
+static int sof_pci_ipc_type = -1;
+module_param_named(ipc_type, sof_pci_ipc_type, int, 0444);
+MODULE_PARM_DESC(ipc_type, "SOF IPC type (0): SOF, (1) Intel CAVS");
+
+static const char *sof_dmi_override_tplg_name;
+static bool sof_dmi_use_community_key;
 
 #define SOF_PCI_DISABLE_PM_RUNTIME BIT(0)
 
 static int sof_tplg_cb(const struct dmi_system_id *id)
 {
-       sof_override_tplg_name = id->driver_data;
+       sof_dmi_override_tplg_name = id->driver_data;
        return 1;
 }
 
@@ -94,15 +108,35 @@ static const struct dmi_system_id sof_tplg_table[] = {
        {}
 };
 
+/* all Up boards use the community key */
+static int up_use_community_key(const struct dmi_system_id *id)
+{
+       sof_dmi_use_community_key = true;
+       return 1;
+}
+
+/*
+ * For ApolloLake Chromebooks we want to force the use of the Intel production key.
+ * All newer platforms use the community key
+ */
+static int chromebook_use_community_key(const struct dmi_system_id *id)
+{
+       if (!soc_intel_is_apl())
+               sof_dmi_use_community_key = true;
+       return 1;
+}
+
 static const struct dmi_system_id community_key_platforms[] = {
        {
                .ident = "Up boards",
+               .callback = up_use_community_key,
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
                }
        },
        {
                .ident = "Google Chromebooks",
+               .callback = chromebook_use_community_key,
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Google"),
                }
@@ -178,7 +212,36 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        sof_pdata->name = pci_name(pci);
        sof_pdata->desc = desc;
        sof_pdata->dev = dev;
-       sof_pdata->fw_filename = desc->default_fw_filename;
+
+       sof_pdata->ipc_type = desc->ipc_default;
+
+       if (sof_pci_ipc_type < 0) {
+               sof_pdata->ipc_type = desc->ipc_default;
+       } else {
+               dev_info(dev, "overriding default IPC %d to requested %d\n",
+                        desc->ipc_default, sof_pci_ipc_type);
+               if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) {
+                       dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) {
+                       dev_err(dev, "invalid request value %d, supported mask is %#x\n",
+                               sof_pci_ipc_type, desc->ipc_supported_mask);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               sof_pdata->ipc_type = sof_pci_ipc_type;
+       }
+
+       if (fw_filename) {
+               sof_pdata->fw_filename = fw_filename;
+
+               dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
+                       sof_pdata->fw_filename);
+       } else {
+               sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type];
+       }
 
        /*
         * for platforms using the SOF community key, change the
@@ -195,10 +258,10 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
                        "Module parameter used, changed fw path to %s\n",
                        sof_pdata->fw_filename_prefix);
 
-       } else if (dmi_check_system(community_key_platforms)) {
+       } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
                sof_pdata->fw_filename_prefix =
                        devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
-                                      sof_pdata->desc->default_fw_path,
+                                      sof_pdata->desc->default_fw_path[sof_pdata->ipc_type],
                                       "community");
 
                dev_dbg(dev,
@@ -206,24 +269,37 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
                        sof_pdata->fw_filename_prefix);
        } else {
                sof_pdata->fw_filename_prefix =
-                       sof_pdata->desc->default_fw_path;
+                       sof_pdata->desc->default_fw_path[sof_pdata->ipc_type];
        }
 
        if (tplg_path)
                sof_pdata->tplg_filename_prefix = tplg_path;
        else
                sof_pdata->tplg_filename_prefix =
-                       sof_pdata->desc->default_tplg_path;
+                       sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type];
 
-       dmi_check_system(sof_tplg_table);
-       if (sof_override_tplg_name)
-               sof_pdata->tplg_filename = sof_override_tplg_name;
+       /*
+        * the topology filename will be provided in the machine descriptor, unless
+        * it is overridden by a module parameter or DMI quirk.
+        */
+       if (tplg_filename) {
+               sof_pdata->tplg_filename = tplg_filename;
+
+               dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n",
+                       sof_pdata->tplg_filename);
+       } else {
+               dmi_check_system(sof_tplg_table);
+               if (sof_dmi_override_tplg_name)
+                       sof_pdata->tplg_filename = sof_dmi_override_tplg_name;
+       }
 
        /* set callback to be called on successful device probe to enable runtime_pm */
        sof_pdata->sof_probe_complete = sof_pci_probe_complete;
 
        /* call sof helper for DSP hardware probe */
        ret = snd_sof_device_probe(dev, sof_pdata);
+
+out:
        if (ret)
                pci_release_regions(pci);
 
index 0d9b640..9d7f53f 100644 (file)
@@ -181,11 +181,6 @@ struct snd_sof_dsp_ops {
        int (*load_firmware)(struct snd_sof_dev *sof_dev); /* mandatory */
        int (*load_module)(struct snd_sof_dev *sof_dev,
                           struct snd_sof_mod_hdr *hdr); /* optional */
-       /*
-        * FW ready checks for ABI compatibility and creates
-        * memory windows at first boot
-        */
-       int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* mandatory */
 
        /* connect pcm substream to a host stream */
        int (*pcm_open)(struct snd_sof_dev *sdev,
@@ -259,8 +254,9 @@ struct snd_sof_dsp_ops {
                                       size_t size, const char *name,
                                       enum sof_debugfs_access_type access_type); /* optional */
 
-       /* host DMA trace initialization */
+       /* host DMA trace (IPC3) */
        int (*trace_init)(struct snd_sof_dev *sdev,
+                         struct snd_dma_buffer *dmatb,
                          struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */
        int (*trace_release)(struct snd_sof_dev *sdev); /* optional */
        int (*trace_trigger)(struct snd_sof_dev *sdev,
@@ -349,18 +345,36 @@ struct snd_sof_mailbox {
 /* IPC message descriptor for host <-> DSP IO */
 struct snd_sof_ipc_msg {
        /* message data */
-       u32 header;
        void *msg_data;
        void *reply_data;
        size_t msg_size;
        size_t reply_size;
        int reply_error;
 
+       /* notification, firmware initiated messages */
+       void *rx_data;
+
        wait_queue_head_t waitq;
        bool ipc_complete;
 };
 
 /**
+ * struct sof_ipc_fw_tracing_ops - IPC-specific firmware tracing ops
+ * @init:      Function pointer for initialization of the tracing
+ * @free:      Optional function pointer for freeing of the tracing
+ * @fw_crashed:        Optional function pointer to notify the tracing of a firmware crash
+ * @suspend:   Function pointer for system/runtime suspend
+ * @resume:    Function pointer for system/runtime resume
+ */
+struct sof_ipc_fw_tracing_ops {
+       int (*init)(struct snd_sof_dev *sdev);
+       void (*free)(struct snd_sof_dev *sdev);
+       void (*fw_crashed)(struct snd_sof_dev *sdev);
+       void (*suspend)(struct snd_sof_dev *sdev, pm_message_t pm_state);
+       int (*resume)(struct snd_sof_dev *sdev);
+};
+
+/**
  * struct sof_ipc_pm_ops - IPC-specific PM ops
  * @ctx_save:          Function pointer for context save
  * @ctx_restore:       Function pointer for context restore
@@ -370,6 +384,25 @@ struct sof_ipc_pm_ops {
        int (*ctx_restore)(struct snd_sof_dev *sdev);
 };
 
+/**
+ * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops
+ * @validate:          Function pointer for validating the firmware image
+ * @parse_ext_manifest:        Function pointer for parsing the manifest of the firmware
+ * @load_fw_to_dsp:    Optional function pointer for loading the firmware to the
+ *                     DSP.
+ *                     The function implements generic, hardware independent way
+ *                     of loading the initial firmware and its modules (if any).
+ * @query_fw_configuration: Optional function pointer to query information and
+ *                     configuration from the booted firmware.
+ *                     Executed after the first successful firmware boot.
+ */
+struct sof_ipc_fw_loader_ops {
+       int (*validate)(struct snd_sof_dev *sdev);
+       size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev);
+       int (*load_fw_to_dsp)(struct snd_sof_dev *sdev);
+       int (*query_fw_configuration)(struct snd_sof_dev *sdev);
+};
+
 struct sof_ipc_tplg_ops;
 struct sof_ipc_pcm_ops;
 
@@ -378,11 +411,36 @@ struct sof_ipc_pcm_ops;
  * @tplg:      Pointer to IPC-specific topology ops
  * @pm:                Pointer to PM ops
  * @pcm:       Pointer to PCM ops
+ * @fw_loader: Pointer to Firmware Loader ops
+ * @fw_tracing:        Pointer to Firmware tracing ops
+ *
+ * @tx_msg:    Function pointer for sending a 'short' IPC message
+ * @set_get_data: Function pointer for set/get data ('large' IPC message). This
+ *             function may split up the 'large' message and use the @tx_msg
+ *             path to transfer individual chunks, or use other means to transfer
+ *             the message.
+ * @get_reply: Function pointer for fetching the reply to
+ *             sdev->ipc->msg.reply_data
+ * @rx_msg:    Function pointer for handling a received message
+ *
+ * Note: both @tx_msg and @set_get_data considered as TX functions and they are
+ * serialized for the duration of the instructed transfer. A large message sent
+ * via @set_get_data is a single transfer even if at the hardware level it is
+ * handled with multiple chunks.
  */
 struct sof_ipc_ops {
        const struct sof_ipc_tplg_ops *tplg;
        const struct sof_ipc_pm_ops *pm;
        const struct sof_ipc_pcm_ops *pcm;
+       const struct sof_ipc_fw_loader_ops *fw_loader;
+       const struct sof_ipc_fw_tracing_ops *fw_tracing;
+
+       int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+                     void *reply_data, size_t reply_bytes, bool no_pm);
+       int (*set_get_data)(struct snd_sof_dev *sdev, void *data, size_t data_bytes,
+                           bool set);
+       int (*get_reply)(struct snd_sof_dev *sdev);
+       void (*rx_msg)(struct snd_sof_dev *sdev);
 };
 
 /* SOF generic IPC data */
@@ -394,6 +452,9 @@ struct snd_sof_ipc {
        /* disables further sending of ipc's */
        bool disable_ipc_tx;
 
+       /* Maximum allowed size of a single IPC message/reply */
+       size_t max_payload_size;
+
        struct snd_sof_ipc_msg msg;
 
        /* IPC ops based on version */
@@ -457,8 +518,6 @@ struct snd_sof_dev {
        bool ipc_dump_printed;
 
        /* firmware loader */
-       struct snd_dma_buffer dmab;
-       struct snd_dma_buffer dmab_bdl;
        struct sof_ipc_fw_ready fw_ready;
        struct sof_ipc_fw_version fw_version;
        struct sof_ipc_cc_version *cc_version;
@@ -473,6 +532,7 @@ struct snd_sof_dev {
        struct list_head route_list;
        struct snd_soc_component *component;
        u32 enabled_cores_mask; /* keep track of enabled cores */
+       bool led_present;
 
        /* FW configuration */
        struct sof_ipc_window *info_window;
@@ -481,16 +541,9 @@ struct snd_sof_dev {
        int ipc_timeout;
        int boot_timeout;
 
-       /* DMA for Trace */
-       struct snd_dma_buffer dmatb;
-       struct snd_dma_buffer dmatp;
-       int dma_trace_pages;
-       wait_queue_head_t trace_sleep;
-       u32 host_offset;
-       bool dtrace_is_supported; /* set with Kconfig or module parameter */
-       bool dtrace_is_enabled;
-       bool dtrace_error;
-       bool dtrace_draining;
+       /* firmwre tracing */
+       bool fw_trace_is_supported; /* set with Kconfig or module parameter */
+       void *fw_trace_data; /* private data used by firmware tracing implementation */
 
        bool msi_enabled;
 
@@ -564,8 +617,6 @@ extern struct snd_compress_ops sof_compressed_ops;
 int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev);
 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev);
 int snd_sof_run_firmware(struct snd_sof_dev *sdev);
-int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
-                               struct snd_sof_mod_hdr *module);
 void snd_sof_fw_unload(struct snd_sof_dev *sdev);
 
 /*
@@ -575,15 +626,17 @@ 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_valid(struct snd_sof_dev *sdev);
-int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
-                      void *msg_data, size_t msg_bytes, void *reply_data,
-                      size_t reply_bytes);
-int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
-                            void *msg_data, size_t msg_bytes,
+static inline void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
+{
+       sdev->ipc->ops->rx_msg(sdev);
+}
+int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
+                      void *reply_data, size_t reply_bytes);
+int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
                             void *reply_data, size_t reply_bytes);
-int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev);
+int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+                    size_t reply_bytes);
+
 static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id)
 {
        snd_sof_ipc_get_reply(sdev);
@@ -593,27 +646,26 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i
 /*
  * Trace/debug
  */
-int snd_sof_init_trace(struct snd_sof_dev *sdev);
-void snd_sof_release_trace(struct snd_sof_dev *sdev);
-void snd_sof_free_trace(struct snd_sof_dev *sdev);
 int snd_sof_dbg_init(struct snd_sof_dev *sdev);
 void snd_sof_free_debug(struct snd_sof_dev *sdev);
 int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
                             void *base, size_t size,
                             const char *name, mode_t mode);
-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 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);
 int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
                enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size,
                const char *name, enum sof_debugfs_access_type access_type);
+/* Firmware tracing */
+int sof_fw_trace_init(struct snd_sof_dev *sdev);
+void sof_fw_trace_free(struct snd_sof_dev *sdev);
+void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev);
+void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state);
+int sof_fw_trace_resume(struct snd_sof_dev *sdev);
 
 /*
  * DSP Architectures.
@@ -654,8 +706,6 @@ int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
 int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
                   u32 offset, void *dest, size_t size);
 
-int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id);
-
 int sof_ipc_msg_data(struct snd_sof_dev *sdev,
                     struct snd_pcm_substream *substream,
                     void *p, size_t sz);
@@ -721,4 +771,8 @@ static inline int sof_resume_clients(struct snd_sof_dev *sdev)
 }
 #endif /* CONFIG_SND_SOC_SOF_CLIENT */
 
+/* Main ops for IPC implementations */
+extern const struct sof_ipc_ops ipc3_ops;
+extern const struct sof_ipc_ops ipc4_ops;
+
 #endif
index 3e5b319..b1fcab7 100644 (file)
@@ -32,7 +32,6 @@
 #define VOL_HALF_DB_STEP       50
 
 /* TLV data items */
-#define TLV_ITEMS      3
 #define TLV_MIN                0
 #define TLV_STEP       1
 #define TLV_MUTE       2
@@ -134,7 +133,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
        return 0;
 }
 
-static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS])
+static inline int get_tlv_data(const int *p, int tlv[SOF_TLV_ITEMS])
 {
        /* we only support dB scale TLV type at the moment */
        if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
@@ -224,7 +223,7 @@ static u32 vol_pow32(u32 a, int exp, u32 fwl)
  * Function to calculate volume gain from TLV data.
  * This function can only handle gain steps that are multiples of 0.5 dB
  */
-static u32 vol_compute_gain(u32 value, int *tlv)
+u32 vol_compute_gain(u32 value, int *tlv)
 {
        int dB_gain;
        u32 linear_gain;
@@ -263,20 +262,17 @@ static u32 vol_compute_gain(u32 value, int *tlv)
  * "size" specifies the number of entries in the table
  */
 static int set_up_volume_table(struct snd_sof_control *scontrol,
-                              int tlv[TLV_ITEMS], int size)
+                              int tlv[SOF_TLV_ITEMS], int size)
 {
-       int j;
-
-       /* init the volume table */
-       scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
-       if (!scontrol->volume_table)
-               return -ENOMEM;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
 
-       /* populate the volume table */
-       for (j = 0; j < size ; j++)
-               scontrol->volume_table[j] = vol_compute_gain(j, tlv);
+       if (tplg_ops->control->set_up_volume_table)
+               return tplg_ops->control->set_up_volume_table(scontrol, tlv, size);
 
-       return 0;
+       dev_err(scomp->dev, "Mandatory op %s not set\n", __func__);
+       return -EINVAL;
 }
 
 struct sof_dai_types {
@@ -772,7 +768,8 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_soc_tplg_mixer_control *mc =
                container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
-       int tlv[TLV_ITEMS];
+       int tlv[SOF_TLV_ITEMS];
+       unsigned int mask;
        int ret;
 
        /* validate topology data */
@@ -821,6 +818,16 @@ skip:
                goto err;
        }
 
+       if (scontrol->led_ctl.use_led) {
+               mask = scontrol->led_ctl.direction ? SNDRV_CTL_ELEM_ACCESS_MIC_LED :
+                                                       SNDRV_CTL_ELEM_ACCESS_SPK_LED;
+               scontrol->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+               scontrol->access |= mask;
+               kc->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+               kc->access |= mask;
+               sdev->led_present = true;
+       }
+
        dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
                scontrol->comp_id, scontrol->num_channels);
 
@@ -1001,15 +1008,18 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
        struct snd_soc_dai *cpu_dai;
        int i;
 
+       if (!w->sname) {
+               dev_err(scomp->dev, "Widget %s does not have stream\n", w->name);
+               return -EINVAL;
+       }
+
        list_for_each_entry(rtd, &card->rtd_list, list) {
                dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n",
                         w->name,  w->sname, rtd->dai_link->stream_name);
 
-               if (!w->sname || !rtd->dai_link->stream_name)
-                       continue;
-
                /* does stream match DAI link ? */
-               if (strcmp(w->sname, rtd->dai_link->stream_name))
+               if (!rtd->dai_link->stream_name ||
+                   strcmp(w->sname, rtd->dai_link->stream_name))
                        continue;
 
                switch (w->id) {
@@ -1140,7 +1150,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
        const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
        struct snd_soc_tplg_private *private = &tw->priv;
        int num_tuples = 0;
-       size_t size;
        int ret, i;
 
        if (count > 0 && !object_token_list) {
@@ -1153,8 +1162,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
                num_tuples += token_list[object_token_list[i]].count;
 
        /* allocate memory for tuples array */
-       size = sizeof(struct snd_sof_tuple) * num_tuples;
-       swidget->tuples = kzalloc(size, GFP_KERNEL);
+       swidget->tuples = kcalloc(num_tuples, sizeof(*swidget->tuples), GFP_KERNEL);
        if (!swidget->tuples)
                return -ENOMEM;
 
@@ -1595,7 +1603,6 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
        const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
        struct snd_soc_tplg_private *private = &cfg->priv;
        struct snd_sof_dai_link *slink;
-       size_t size;
        u32 token_id = 0;
        int num_tuples = 0;
        int ret, num_sets;
@@ -1707,22 +1714,23 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
        }
 
        /* allocate memory for tuples array */
-       size = sizeof(struct snd_sof_tuple) * num_tuples;
-       slink->tuples = kzalloc(size, GFP_KERNEL);
+       slink->tuples = kcalloc(num_tuples, sizeof(*slink->tuples), GFP_KERNEL);
        if (!slink->tuples) {
                kfree(slink->hw_configs);
                kfree(slink);
                return -ENOMEM;
        }
 
-       /* parse one set of DAI link tokens */
-       ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
-                             SOF_DAI_LINK_TOKENS, 1, slink->tuples,
-                             num_tuples, &slink->num_tuples);
-       if (ret < 0) {
-               dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
-                       token_list[SOF_DAI_LINK_TOKENS].name, link->name);
-               goto err;
+       if (token_list[SOF_DAI_LINK_TOKENS].tokens) {
+               /* parse one set of DAI link tokens */
+               ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+                                     SOF_DAI_LINK_TOKENS, 1, slink->tuples,
+                                     num_tuples, &slink->num_tuples);
+               if (ret < 0) {
+                       dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
+                               token_list[SOF_DAI_LINK_TOKENS].name, link->name);
+                       goto err;
+               }
        }
 
        /* nothing more to do if there are no DAI type-specific tokens defined */
@@ -2075,6 +2083,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
 
 int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
 {
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        const struct firmware *fw;
        int ret;
 
@@ -2097,6 +2106,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
        }
 
        release_firmware(fw);
+
+       if (ret >= 0 && sdev->led_present)
+               ret = snd_ctl_led_request();
+
        return ret;
 }
 EXPORT_SYMBOL(snd_sof_load_topology);
index ea8e450..6f66264 100644 (file)
-// 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) 2018 Intel Corporation. All rights reserved.
-//
-// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// SPDX-License-Identifier: GPL-2.0-only
 //
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
 
-#include <linux/debugfs.h>
-#include <linux/sched/signal.h>
 #include "sof-priv.h"
-#include "ops.h"
-#include "sof-utils.h"
-
-#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
-#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
-
-static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value,
-                                   struct sof_ipc_trace_filter_elem *elem_list,
-                                   int capacity, int *counter)
-{
-       if (*counter >= capacity)
-               return -ENOMEM;
-
-       elem_list[*counter].key = key;
-       elem_list[*counter].value = value;
-       ++*counter;
-
-       return 0;
-}
-
-static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
-                                   struct sof_ipc_trace_filter_elem *elem,
-                                   int capacity, int *counter)
-{
-       int len = strlen(line);
-       int cnt = *counter;
-       uint32_t uuid_id;
-       int log_level;
-       int pipe_id;
-       int comp_id;
-       int read;
-       int ret;
-
-       /* ignore empty content */
-       ret = sscanf(line, " %n", &read);
-       if (!ret && read == len)
-               return len;
-
-       ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
-       if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
-               dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line);
-               return -EINVAL;
-       }
-
-       if (uuid_id > 0) {
-               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
-                                              uuid_id, elem, capacity, &cnt);
-               if (ret)
-                       return ret;
-       }
-       if (pipe_id >= 0) {
-               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
-                                              pipe_id, elem, capacity, &cnt);
-               if (ret)
-                       return ret;
-       }
-       if (comp_id >= 0) {
-               ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
-                                              comp_id, elem, capacity, &cnt);
-               if (ret)
-                       return ret;
-       }
-
-       ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
-                                      SOF_IPC_TRACE_FILTER_ELEM_FIN,
-                                      log_level, elem, capacity, &cnt);
-       if (ret)
-               return ret;
-
-       /* update counter only when parsing whole entry passed */
-       *counter = cnt;
-
-       return len;
-}
-
-static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
-                             int *out_elem_cnt,
-                             struct sof_ipc_trace_filter_elem **out)
-{
-       static const char entry_delimiter[] = ";";
-       char *entry = string;
-       int capacity = 0;
-       int entry_len;
-       int cnt = 0;
-
-       /*
-        * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
-        * IPC elements, depending on content. Calculate IPC elements capacity
-        * for the input string where each element is set.
-        */
-       while (entry) {
-               capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
-               entry = strchr(entry + 1, entry_delimiter[0]);
-       }
-       *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
-       if (!*out)
-               return -ENOMEM;
-
-       /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
-       while ((entry = strsep(&string, entry_delimiter))) {
-               entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
-               if (entry_len < 0) {
-                       dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry,
-                               entry_len);
-                       return -EINVAL;
-               }
-       }
-
-       *out_elem_cnt = cnt;
-
-       return 0;
-}
-
-static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
-                                      struct sof_ipc_trace_filter_elem *elems)
-{
-       struct sof_ipc_trace_filter *msg;
-       struct sof_ipc_reply reply;
-       size_t size;
-       int ret;
-
-       size = struct_size(msg, elems, num_elems);
-       if (size > SOF_IPC_MSG_MAX_SIZE)
-               return -EINVAL;
-
-       msg = kmalloc(size, GFP_KERNEL);
-       if (!msg)
-               return -ENOMEM;
-
-       msg->hdr.size = size;
-       msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
-       msg->elem_cnt = num_elems;
-       memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
-
-       ret = pm_runtime_get_sync(sdev->dev);
-       if (ret < 0 && ret != -EACCES) {
-               pm_runtime_put_noidle(sdev->dev);
-               dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
-               goto error;
-       }
-       ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
-                                &reply, sizeof(reply));
-       pm_runtime_mark_last_busy(sdev->dev);
-       pm_runtime_put_autosuspend(sdev->dev);
-
-error:
-       kfree(msg);
-       return ret ? ret : reply.error;
-}
-
-static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from,
-                                              size_t count, loff_t *ppos)
-{
-       struct snd_sof_dfsentry *dfse = file->private_data;
-       struct sof_ipc_trace_filter_elem *elems = NULL;
-       struct snd_sof_dev *sdev = dfse->sdev;
-       loff_t pos = 0;
-       int num_elems;
-       char *string;
-       int ret;
-
-       if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
-               dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
-                       TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
-               return -EINVAL;
-       }
-
-       string = kmalloc(count + 1, GFP_KERNEL);
-       if (!string)
-               return -ENOMEM;
-
-       /* assert null termination */
-       string[count] = 0;
-       ret = simple_write_to_buffer(string, count, &pos, from, count);
-       if (ret < 0)
-               goto error;
-
-       ret = trace_filter_parse(sdev, string, &num_elems, &elems);
-       if (ret < 0) {
-               dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret);
-               goto error;
-       }
-
-       if (num_elems) {
-               ret = sof_ipc_trace_update_filter(sdev, num_elems, elems);
-               if (ret < 0) {
-                       dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret);
-                       goto error;
-               }
-       }
-       ret = count;
-error:
-       kfree(string);
-       kfree(elems);
-       return ret;
-}
-
-static const struct file_operations sof_dfs_trace_filter_fops = {
-       .open = simple_open,
-       .write = sof_dfsentry_trace_filter_write,
-       .llseek = default_llseek,
-};
-
-static int trace_debugfs_filter_create(struct snd_sof_dev *sdev)
-{
-       struct snd_sof_dfsentry *dfse;
-
-       dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
-       if (!dfse)
-               return -ENOMEM;
-
-       dfse->sdev = sdev;
-       dfse->type = SOF_DFSENTRY_TYPE_BUF;
-
-       debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
-                           &sof_dfs_trace_filter_fops);
-       /* add to dfsentry list */
-       list_add(&dfse->list, &sdev->dfsentry_list);
-
-       return 0;
-}
-
-static size_t sof_trace_avail(struct snd_sof_dev *sdev,
-                             loff_t pos, size_t buffer_size)
-{
-       loff_t host_offset = READ_ONCE(sdev->host_offset);
-
-       /*
-        * If host offset is less than local pos, it means write pointer of
-        * host DMA buffer has been wrapped. We should output the trace data
-        * at the end of host DMA buffer at first.
-        */
-       if (host_offset < pos)
-               return buffer_size - pos;
-
-       /* If there is available trace data now, it is unnecessary to wait. */
-       if (host_offset > pos)
-               return host_offset - pos;
-
-       return 0;
-}
-
-static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
-                                  loff_t pos, size_t buffer_size)
-{
-       wait_queue_entry_t wait;
-       size_t ret = sof_trace_avail(sdev, pos, buffer_size);
-
-       /* data immediately available */
-       if (ret)
-               return ret;
-
-       if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) {
-               /*
-                * tracing has ended and all traces have been
-                * read by client, return EOF
-                */
-               sdev->dtrace_draining = false;
-               return 0;
-       }
-
-       /* wait for available trace data from FW */
-       init_waitqueue_entry(&wait, current);
-       set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&sdev->trace_sleep, &wait);
-
-       if (!signal_pending(current)) {
-               /* set timeout to max value, no error code */
-               schedule_timeout(MAX_SCHEDULE_TIMEOUT);
-       }
-       remove_wait_queue(&sdev->trace_sleep, &wait);
 
-       return sof_trace_avail(sdev, pos, buffer_size);
-}
-
-static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
-                                      size_t count, loff_t *ppos)
-{
-       struct snd_sof_dfsentry *dfse = file->private_data;
-       struct snd_sof_dev *sdev = dfse->sdev;
-       unsigned long rem;
-       loff_t lpos = *ppos;
-       size_t avail, buffer_size = dfse->size;
-       u64 lpos_64;
-
-       /* make sure we know about any failures on the DSP side */
-       sdev->dtrace_error = false;
-
-       /* check pos and count */
-       if (lpos < 0)
-               return -EINVAL;
-       if (!count)
-               return 0;
-
-       /* check for buffer wrap and count overflow */
-       lpos_64 = lpos;
-       lpos = do_div(lpos_64, buffer_size);
-
-       /* get available count based on current host offset */
-       avail = sof_wait_trace_avail(sdev, lpos, buffer_size);
-       if (sdev->dtrace_error) {
-               dev_err(sdev->dev, "error: trace IO error\n");
-               return -EIO;
-       }
-
-       /* make sure count is <= avail */
-       if (count > avail)
-               count = avail;
-
-       /*
-        * make sure that all trace data is available for the CPU as the trace
-        * data buffer might be allocated from non consistent memory.
-        * Note: snd_dma_buffer_sync() is called for normal audio playback and
-        *       capture streams also.
-        */
-       snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU);
-       /* copy available trace data to debugfs */
-       rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
-       if (rem)
-               return -EFAULT;
-
-       *ppos += count;
-
-       /* move debugfs reading position */
-       return count;
-}
-
-static int sof_dfsentry_trace_release(struct inode *inode, struct file *file)
-{
-       struct snd_sof_dfsentry *dfse = inode->i_private;
-       struct snd_sof_dev *sdev = dfse->sdev;
-
-       /* avoid duplicate traces at next open */
-       if (!sdev->dtrace_is_enabled)
-               sdev->host_offset = 0;
-
-       return 0;
-}
-
-static const struct file_operations sof_dfs_trace_fops = {
-       .open = simple_open,
-       .read = sof_dfsentry_trace_read,
-       .llseek = default_llseek,
-       .release = sof_dfsentry_trace_release,
-};
-
-static int trace_debugfs_create(struct snd_sof_dev *sdev)
-{
-       struct snd_sof_dfsentry *dfse;
-       int ret;
-
-       if (!sdev)
-               return -EINVAL;
-
-       ret = trace_debugfs_filter_create(sdev);
-       if (ret < 0)
-               dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret);
-
-       dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
-       if (!dfse)
-               return -ENOMEM;
-
-       dfse->type = SOF_DFSENTRY_TYPE_BUF;
-       dfse->buf = sdev->dmatb.area;
-       dfse->size = sdev->dmatb.bytes;
-       dfse->sdev = sdev;
-
-       debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
-                           &sof_dfs_trace_fops);
-
-       return 0;
-}
-
-int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
-{
-       struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
-       struct sof_ipc_fw_version *v = &ready->version;
-       struct sof_ipc_dma_trace_params_ext params;
-       struct sof_ipc_reply ipc_reply;
-       int ret;
-
-       if (!sdev->dtrace_is_supported)
-               return 0;
-
-       if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages)
-               return -EINVAL;
-
-       /* set IPC parameters */
-       params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
-       /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
-       if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
-               params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
-               params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
-               params.timestamp_ns = ktime_get(); /* in nanosecond */
-       } else {
-               params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
-               params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
-       }
-       params.buffer.phy_addr = sdev->dmatp.addr;
-       params.buffer.size = sdev->dmatb.bytes;
-       params.buffer.pages = sdev->dma_trace_pages;
-       params.stream_tag = 0;
-
-       sdev->host_offset = 0;
-       sdev->dtrace_draining = false;
-
-       ret = snd_sof_dma_trace_init(sdev, &params);
-       if (ret < 0) {
-               dev_err(sdev->dev,
-                       "error: fail in snd_sof_dma_trace_init %d\n", ret);
-               return ret;
-       }
-       dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
-
-       /* send IPC to the DSP */
-       ret = sof_ipc_tx_message(sdev->ipc,
-                                params.hdr.cmd, &params, sizeof(params),
-                                &ipc_reply, sizeof(ipc_reply));
-       if (ret < 0) {
-               dev_err(sdev->dev,
-                       "error: can't set params for DMA for trace %d\n", ret);
-               goto trace_release;
-       }
-
-       ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START);
-       if (ret < 0) {
-               dev_err(sdev->dev,
-                       "error: snd_sof_dma_trace_trigger: start: %d\n", ret);
-               goto trace_release;
-       }
-
-       sdev->dtrace_is_enabled = true;
-
-       return 0;
-
-trace_release:
-       snd_sof_dma_trace_release(sdev);
-       return ret;
-}
-
-int snd_sof_init_trace(struct snd_sof_dev *sdev)
+int sof_fw_trace_init(struct snd_sof_dev *sdev)
 {
-       int ret;
+       if (!sdev->ipc->ops->fw_tracing) {
+               dev_info(sdev->dev, "Firmware tracing is not available\n");
+               sdev->fw_trace_is_supported = false;
 
-       if (!sdev->dtrace_is_supported)
                return 0;
-
-       /* set false before start initialization */
-       sdev->dtrace_is_enabled = false;
-
-       /* allocate trace page table buffer */
-       ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
-                                 PAGE_SIZE, &sdev->dmatp);
-       if (ret < 0) {
-               dev_err(sdev->dev,
-                       "error: can't alloc page table for trace %d\n", ret);
-               return ret;
-       }
-
-       /* allocate trace data buffer */
-       ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
-                                     DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
-                                     &sdev->dmatb);
-       if (ret < 0) {
-               dev_err(sdev->dev,
-                       "error: can't alloc buffer for trace %d\n", ret);
-               goto page_err;
-       }
-
-       /* create compressed page table for audio firmware */
-       ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb,
-                                       sdev->dmatp.area, sdev->dmatb.bytes);
-       if (ret < 0)
-               goto table_err;
-
-       sdev->dma_trace_pages = ret;
-       dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n",
-               __func__, sdev->dma_trace_pages);
-
-       if (sdev->first_boot) {
-               ret = trace_debugfs_create(sdev);
-               if (ret < 0)
-                       goto table_err;
        }
 
-       init_waitqueue_head(&sdev->trace_sleep);
-
-       ret = snd_sof_init_trace_ipc(sdev);
-       if (ret < 0)
-               goto table_err;
-
-       return 0;
-table_err:
-       sdev->dma_trace_pages = 0;
-       snd_dma_free_pages(&sdev->dmatb);
-page_err:
-       snd_dma_free_pages(&sdev->dmatp);
-       return ret;
+       return sdev->ipc->ops->fw_tracing->init(sdev);
 }
-EXPORT_SYMBOL(snd_sof_init_trace);
 
-int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
-                            struct sof_ipc_dma_trace_posn *posn)
+void sof_fw_trace_free(struct snd_sof_dev *sdev)
 {
-       if (!sdev->dtrace_is_supported)
-               return 0;
-
-       if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) {
-               sdev->host_offset = posn->host_offset;
-               wake_up(&sdev->trace_sleep);
-       }
-
-       if (posn->overflow != 0)
-               dev_err(sdev->dev,
-                       "error: DSP trace buffer overflow %u bytes. Total messages %d\n",
-                       posn->overflow, posn->messages);
+       if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing)
+               return;
 
-       return 0;
+       if (sdev->ipc->ops->fw_tracing->free)
+               sdev->ipc->ops->fw_tracing->free(sdev);
 }
 
-/* an error has occurred within the DSP that prevents further trace */
-void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev)
+void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev)
 {
-       if (!sdev->dtrace_is_supported)
+       if (!sdev->fw_trace_is_supported)
                return;
 
-       if (sdev->dtrace_is_enabled) {
-               sdev->dtrace_error = true;
-               wake_up(&sdev->trace_sleep);
-       }
+       if (sdev->ipc->ops->fw_tracing->fw_crashed)
+               sdev->ipc->ops->fw_tracing->fw_crashed(sdev);
 }
-EXPORT_SYMBOL(snd_sof_trace_notify_for_error);
 
-void snd_sof_release_trace(struct snd_sof_dev *sdev)
+void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
 {
-       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)
+       if (!sdev->fw_trace_is_supported)
                return;
 
-       ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
-       if (ret < 0)
-               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,
-                       "error: fail in snd_sof_dma_trace_release %d\n", ret);
-
-       sdev->dtrace_is_enabled = false;
-       sdev->dtrace_draining = true;
-       wake_up(&sdev->trace_sleep);
+       sdev->ipc->ops->fw_tracing->suspend(sdev, pm_state);
 }
-EXPORT_SYMBOL(snd_sof_release_trace);
 
-void snd_sof_free_trace(struct snd_sof_dev *sdev)
+int sof_fw_trace_resume(struct snd_sof_dev *sdev)
 {
-       if (!sdev->dtrace_is_supported)
-               return;
-
-       snd_sof_release_trace(sdev);
+       if (!sdev->fw_trace_is_supported)
+               return 0;
 
-       if (sdev->dma_trace_pages) {
-               snd_dma_free_pages(&sdev->dmatb);
-               snd_dma_free_pages(&sdev->dmatp);
-               sdev->dma_trace_pages = 0;
-       }
+       return sdev->ipc->ops->fw_tracing->resume(sdev);
 }
-EXPORT_SYMBOL(snd_sof_free_trace);
index cd45487..2482d98 100644 (file)
@@ -85,6 +85,18 @@ config SND_SOC_TEGRA210_I2S
          compatible devices.
          Say Y or M if you want to add support for Tegra210 I2S module.
 
+config SND_SOC_TEGRA186_ASRC
+       tristate "Tegra186 ASRC module"
+       help
+         Config to enable the Asynchronous Sample Rate Converter (ASRC),
+         which converts the sampling frequency of the input signal from
+         one frequency to another. It can handle over a wide range of
+         sample rate ratios (freq_in/freq_out) from 1:24 to 24:1.
+         ASRC has two modes of operation. One where ratio can be programmed
+         in SW and the other where it gets information from ratio estimator
+         module.
+         Say Y or M if you want to add support for Tegra186 ASRC module.
+
 config SND_SOC_TEGRA186_DSPK
        tristate "Tegra186 DSPK module"
        help
index f19d566..70a498d 100644 (file)
@@ -11,6 +11,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o
 snd-soc-tegra210-ahub-objs := tegra210_ahub.o
 snd-soc-tegra210-dmic-objs := tegra210_dmic.o
 snd-soc-tegra210-i2s-objs := tegra210_i2s.o
+snd-soc-tegra186-asrc-objs := tegra186_asrc.o
 snd-soc-tegra186-dspk-objs := tegra186_dspk.o
 snd-soc-tegra210-admaif-objs := tegra210_admaif.o
 snd-soc-tegra210-mvc-objs := tegra210_mvc.o
@@ -29,6 +30,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
 obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o
 obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o
 obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA186_ASRC) += snd-soc-tegra186-asrc.o
 obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
 obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o
 obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
new file mode 100644 (file)
index 0000000..9f12faa
--- /dev/null
@@ -0,0 +1,1046 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra186_asrc.c - Tegra186 ASRC driver
+//
+// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra186_asrc.h"
+#include "tegra_cif.h"
+
+#define ASRC_STREAM_SOURCE_SELECT(id)                                         \
+       (TEGRA186_ASRC_CFG + ((id) * TEGRA186_ASRC_STREAM_STRIDE))
+
+#define ASRC_STREAM_REG(reg, id) ((reg) + ((id) * TEGRA186_ASRC_STREAM_STRIDE))
+
+#define ASRC_STREAM_REG_DEFAULTS(id)                                          \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),                              \
+         (((id) + 1) << 4) },                                                 \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id),                   \
+         0x1 },                                                               \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id),                  \
+         0x0 },                                                               \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_MUTE_UNMUTE_DURATION, id),             \
+         0x400 },                                                             \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, id),                      \
+         0x7500 },                                                            \
+       { ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id),                      \
+         0x7500 }
+
+static const struct reg_default tegra186_asrc_reg_defaults[] = {
+       ASRC_STREAM_REG_DEFAULTS(0),
+       ASRC_STREAM_REG_DEFAULTS(1),
+       ASRC_STREAM_REG_DEFAULTS(2),
+       ASRC_STREAM_REG_DEFAULTS(3),
+       ASRC_STREAM_REG_DEFAULTS(4),
+       ASRC_STREAM_REG_DEFAULTS(5),
+
+       { TEGRA186_ASRC_GLOBAL_ENB, 0},
+       { TEGRA186_ASRC_GLOBAL_SOFT_RESET, 0},
+       { TEGRA186_ASRC_GLOBAL_CG, 0x1 },
+       { TEGRA186_ASRC_GLOBAL_CFG, 0x0 },
+       { TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, 0},
+       { TEGRA186_ASRC_GLOBAL_SCRATCH_CFG, 0x0c207980 },
+       { TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL, 0x00115500 },
+       { TEGRA186_ASRC_GLOBAL_INT_MASK, 0x0},
+       { TEGRA186_ASRC_GLOBAL_INT_SET, 0x0},
+       { TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x0},
+       { TEGRA186_ASRC_GLOBAL_APR_CTRL, 0x0},
+       { TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL, 0x0},
+       { TEGRA186_ASRC_GLOBAL_DISARM_APR, 0x0},
+       { TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL, 0x0},
+       { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS, 0x0},
+       { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL, 0x0},
+       { TEGRA186_ASRC_CYA, 0x0},
+};
+
+static void tegra186_asrc_lock_stream(struct tegra186_asrc *asrc,
+                                     unsigned int id)
+{
+       regmap_write(asrc->regmap,
+                    ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_LOCK_STATUS,
+                                    id),
+                    1);
+}
+
+static int __maybe_unused tegra186_asrc_runtime_suspend(struct device *dev)
+{
+       struct tegra186_asrc *asrc = dev_get_drvdata(dev);
+
+       regcache_cache_only(asrc->regmap, true);
+       regcache_mark_dirty(asrc->regmap);
+
+       return 0;
+}
+
+static int __maybe_unused tegra186_asrc_runtime_resume(struct device *dev)
+{
+       struct tegra186_asrc *asrc = dev_get_drvdata(dev);
+       int id;
+
+       regcache_cache_only(asrc->regmap, false);
+
+       /*
+        * Below sequence is recommended after a runtime PM cycle.
+        * This otherwise leads to transfer failures. The cache
+        * sync is done after this to restore other settings.
+        */
+       regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR,
+                    TEGRA186_ASRC_ARAM_START_ADDR);
+       regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB,
+                    TEGRA186_ASRC_GLOBAL_EN);
+
+       regcache_sync(asrc->regmap);
+
+       for (id = 0; id < TEGRA186_ASRC_STREAM_MAX; id++) {
+               if (asrc->lane[id].ratio_source !=
+                   TEGRA186_ASRC_RATIO_SOURCE_SW)
+                       continue;
+
+               regmap_write(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART,
+                                       id),
+                       asrc->lane[id].int_part);
+
+               regmap_write(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART,
+                                       id),
+                       asrc->lane[id].frac_part);
+
+               tegra186_asrc_lock_stream(asrc, id);
+       }
+
+       return 0;
+}
+
+static int tegra186_asrc_set_audio_cif(struct tegra186_asrc *asrc,
+                                      struct snd_pcm_hw_params *params,
+                                      unsigned int reg)
+{
+       int channels, audio_bits;
+       struct tegra_cif_conf cif_conf;
+
+       memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+       channels = params_channels(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               audio_bits = TEGRA_ACIF_BITS_16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               audio_bits = TEGRA_ACIF_BITS_32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cif_conf.audio_ch = channels;
+       cif_conf.client_ch = channels;
+       cif_conf.audio_bits = audio_bits;
+       cif_conf.client_bits = TEGRA_ACIF_BITS_24;
+
+       tegra_set_cif(asrc->regmap, reg, &cif_conf);
+
+       return 0;
+}
+
+static int tegra186_asrc_in_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params,
+                                     struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+       int ret, id = dai->id;
+
+       /* Set input threshold */
+       regmap_write(asrc->regmap,
+                    ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, dai->id),
+                    asrc->lane[id].input_thresh);
+
+       ret = tegra186_asrc_set_audio_cif(asrc, params,
+               ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, dai->id));
+       if (ret) {
+               dev_err(dev, "Can't set ASRC RX%d CIF: %d\n", dai->id, ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int tegra186_asrc_out_hw_params(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_hw_params *params,
+                                      struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+       int ret, id = dai->id - 7;
+
+        /* Set output threshold */
+       regmap_write(asrc->regmap,
+                    ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, id),
+                    asrc->lane[id].output_thresh);
+
+       ret = tegra186_asrc_set_audio_cif(asrc, params,
+               ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id));
+       if (ret) {
+               dev_err(dev, "Can't set ASRC TX%d CIF: %d\n", id, ret);
+               return ret;
+       }
+
+       /* Set ENABLE_HW_RATIO_COMP */
+       if (asrc->lane[id].hwcomp_disable) {
+               regmap_update_bits(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+                       TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK,
+                       TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE);
+       } else {
+               regmap_update_bits(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+                       TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK,
+                       TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE);
+
+               regmap_write(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_COMP, id),
+                       TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE);
+       }
+
+       /* Set lock */
+       regmap_update_bits(asrc->regmap,
+                          ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+                          1, asrc->lane[id].ratio_source);
+
+       if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_SW) {
+               regmap_write(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id),
+                       asrc->lane[id].int_part);
+               regmap_write(asrc->regmap,
+                       ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id),
+                       asrc->lane[id].frac_part);
+               tegra186_asrc_lock_stream(asrc, id);
+       }
+
+       return ret;
+}
+
+static int tegra186_asrc_get_ratio_source(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *asrc_private =
+               (struct soc_enum  *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+       ucontrol->value.enumerated.item[0] = asrc->lane[id].ratio_source;
+
+       return 0;
+}
+
+static int tegra186_asrc_put_ratio_source(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *asrc_private =
+               (struct soc_enum  *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+       bool change = false;
+
+       asrc->lane[id].ratio_source = ucontrol->value.enumerated.item[0];
+
+       regmap_update_bits_check(asrc->regmap, asrc_private->reg,
+                                TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK,
+                                asrc->lane[id].ratio_source,
+                                &change);
+
+       return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_ratio_int(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+       regmap_read(asrc->regmap,
+                   ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id),
+                   &asrc->lane[id].int_part);
+
+       ucontrol->value.integer.value[0] = asrc->lane[id].int_part;
+
+       return 0;
+}
+
+static int tegra186_asrc_put_ratio_int(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+       bool change = false;
+
+       if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) {
+               dev_err(cmpnt->dev,
+                       "Lane %d ratio source is ARAD, invalid SW update\n",
+                       id);
+               return -EINVAL;
+       }
+
+       asrc->lane[id].int_part = ucontrol->value.integer.value[0];
+
+       regmap_update_bits_check(asrc->regmap,
+                                ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART,
+                                                id),
+                                TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK,
+                                asrc->lane[id].int_part, &change);
+
+       tegra186_asrc_lock_stream(asrc, id);
+
+       return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_ratio_frac(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mreg_control *asrc_private =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE;
+
+       regmap_read(asrc->regmap,
+                   ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id),
+                   &asrc->lane[id].frac_part);
+
+       ucontrol->value.integer.value[0] = asrc->lane[id].frac_part;
+
+       return 0;
+}
+
+static int tegra186_asrc_put_ratio_frac(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mreg_control *asrc_private =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE;
+       bool change = false;
+
+       if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) {
+               dev_err(cmpnt->dev,
+                       "Lane %d ratio source is ARAD, invalid SW update\n",
+                       id);
+               return -EINVAL;
+       }
+
+       asrc->lane[id].frac_part = ucontrol->value.integer.value[0];
+
+       regmap_update_bits_check(asrc->regmap,
+                                ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART,
+                                                id),
+                                TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                                asrc->lane[id].frac_part, &change);
+
+       tegra186_asrc_lock_stream(asrc, id);
+
+       return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_hwcomp_disable(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+       ucontrol->value.integer.value[0] = asrc->lane[id].hwcomp_disable;
+
+       return 0;
+}
+
+static int tegra186_asrc_put_hwcomp_disable(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+       int value = ucontrol->value.integer.value[0];
+
+       if (value == asrc->lane[id].hwcomp_disable)
+               return 0;
+
+       asrc->lane[id].hwcomp_disable = value;
+
+       return 1;
+}
+
+static int tegra186_asrc_get_input_threshold(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+       ucontrol->value.integer.value[0] = (asrc->lane[id].input_thresh & 0x3);
+
+       return 0;
+}
+
+static int tegra186_asrc_put_input_threshold(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+       int value = (asrc->lane[id].input_thresh & ~(0x3)) |
+                   ucontrol->value.integer.value[0];
+
+       if (value == asrc->lane[id].input_thresh)
+               return 0;
+
+       asrc->lane[id].input_thresh = value;
+
+       return 1;
+}
+
+static int tegra186_asrc_get_output_threshold(struct snd_kcontrol *kcontrol,
+                                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+       ucontrol->value.integer.value[0] = (asrc->lane[id].output_thresh & 0x3);
+
+       return 0;
+}
+
+static int tegra186_asrc_put_output_threshold(struct snd_kcontrol *kcontrol,
+                                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *asrc_private =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+       unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+       int value = (asrc->lane[id].output_thresh & ~(0x3)) |
+                   ucontrol->value.integer.value[0];
+
+       if (value == asrc->lane[id].output_thresh)
+               return 0;
+
+       asrc->lane[id].output_thresh = value;
+
+       return 1;
+}
+
+static int tegra186_asrc_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct tegra186_asrc *asrc = dev_get_drvdata(cmpnt->dev);
+       unsigned int id =
+               (w->reg - TEGRA186_ASRC_ENABLE) / TEGRA186_ASRC_STREAM_STRIDE;
+
+       regmap_write(asrc->regmap,
+                    ASRC_STREAM_REG(TEGRA186_ASRC_SOFT_RESET, id),
+                    0x1);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops tegra186_asrc_in_dai_ops = {
+       .hw_params      = tegra186_asrc_in_hw_params,
+};
+
+static const struct snd_soc_dai_ops tegra186_asrc_out_dai_ops = {
+       .hw_params      = tegra186_asrc_out_hw_params,
+};
+
+#define IN_DAI(id)                                             \
+       {                                                       \
+               .name = "ASRC-RX-CIF"#id,                       \
+               .playback = {                                   \
+                       .stream_name = "RX" #id "-CIF-Playback",\
+                       .channels_min = 1,                      \
+                       .channels_max = 12,                     \
+                       .rates = SNDRV_PCM_RATE_8000_192000,    \
+                       .formats = SNDRV_PCM_FMTBIT_S8 |        \
+                               SNDRV_PCM_FMTBIT_S16_LE |       \
+                               SNDRV_PCM_FMTBIT_S24_LE |       \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .capture = {                                    \
+                       .stream_name = "RX" #id "-CIF-Capture", \
+                       .channels_min = 1,                      \
+                       .channels_max = 12,                     \
+                       .rates = SNDRV_PCM_RATE_8000_192000,    \
+                       .formats = SNDRV_PCM_FMTBIT_S8 |        \
+                               SNDRV_PCM_FMTBIT_S16_LE |       \
+                               SNDRV_PCM_FMTBIT_S24_LE |       \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .ops = &tegra186_asrc_in_dai_ops,               \
+       }
+
+#define OUT_DAI(id)                                            \
+       {                                                       \
+               .name = "ASRC-TX-CIF"#id,                       \
+               .playback = {                                   \
+                       .stream_name = "TX" #id "-CIF-Playback",\
+                       .channels_min = 1,                      \
+                       .channels_max = 12,                     \
+                       .rates = SNDRV_PCM_RATE_8000_192000,    \
+                       .formats = SNDRV_PCM_FMTBIT_S8 |        \
+                               SNDRV_PCM_FMTBIT_S16_LE |       \
+                               SNDRV_PCM_FMTBIT_S24_LE |       \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .capture = {                                    \
+                       .stream_name = "TX" #id "-CIF-Capture", \
+                       .channels_min = 1,                      \
+                       .channels_max = 12,                     \
+                       .rates = SNDRV_PCM_RATE_8000_192000,    \
+                       .formats = SNDRV_PCM_FMTBIT_S8 |        \
+                               SNDRV_PCM_FMTBIT_S16_LE |       \
+                               SNDRV_PCM_FMTBIT_S24_LE |       \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .ops = &tegra186_asrc_out_dai_ops,              \
+       }
+
+static struct snd_soc_dai_driver tegra186_asrc_dais[] = {
+       /* ASRC Input */
+       IN_DAI(1),
+       IN_DAI(2),
+       IN_DAI(3),
+       IN_DAI(4),
+       IN_DAI(5),
+       IN_DAI(6),
+       IN_DAI(7),
+       /* ASRC Output */
+       OUT_DAI(1),
+       OUT_DAI(2),
+       OUT_DAI(3),
+       OUT_DAI(4),
+       OUT_DAI(5),
+       OUT_DAI(6),
+};
+
+static const struct snd_soc_dapm_widget tegra186_asrc_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX1", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 0),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX2", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 1),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX3", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 2),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX4", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 3),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX5", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 4),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT_E("TX6", NULL, 0,
+                              ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 5),
+                              TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+                              tegra186_asrc_widget_event,
+                              SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_SPK("Depacketizer", NULL),
+};
+
+#define ASRC_STREAM_ROUTE(id, sname)                                      \
+       { "RX" #id " XBAR-" sname,      NULL,   "RX" #id " XBAR-TX" },     \
+       { "RX" #id "-CIF-" sname,       NULL,   "RX" #id " XBAR-" sname }, \
+       { "RX" #id,                     NULL,   "RX" #id "-CIF-" sname },  \
+       { "TX" #id,                     NULL,   "RX" #id },                \
+       { "TX" #id "-CIF-" sname,       NULL,   "TX" #id },                \
+       { "TX" #id " XBAR-" sname,      NULL,   "TX" #id "-CIF-" sname },  \
+       { "TX" #id " XBAR-RX",          NULL,   "TX" #id " XBAR-" sname },
+
+#define ASRC_ROUTE(id)                                                    \
+       ASRC_STREAM_ROUTE(id, "Playback")                                  \
+       ASRC_STREAM_ROUTE(id, "Capture")
+
+#define ASRC_RATIO_ROUTE(sname)                                                   \
+       { "RX7 XBAR-" sname,            NULL,   "RX7 XBAR-TX" },           \
+       { "RX7-CIF-" sname,             NULL,   "RX7 XBAR-" sname },       \
+       { "RX7",                        NULL,   "RX7-CIF-" sname },        \
+       { "Depacketizer",               NULL,   "RX7" },
+
+static const struct snd_soc_dapm_route tegra186_asrc_routes[] = {
+       ASRC_ROUTE(1)
+       ASRC_ROUTE(2)
+       ASRC_ROUTE(3)
+       ASRC_ROUTE(4)
+       ASRC_ROUTE(5)
+       ASRC_ROUTE(6)
+       ASRC_RATIO_ROUTE("Playback")
+       ASRC_RATIO_ROUTE("Capture")
+};
+
+static const char * const tegra186_asrc_ratio_source_text[] = {
+       "ARAD",
+       "SW",
+};
+
+#define ASRC_SOURCE_DECL(name, id)                                     \
+       static const struct soc_enum name =                             \
+               SOC_ENUM_SINGLE(ASRC_STREAM_SOURCE_SELECT(id),          \
+                               0, 2, tegra186_asrc_ratio_source_text)
+
+ASRC_SOURCE_DECL(src_select1, 0);
+ASRC_SOURCE_DECL(src_select2, 1);
+ASRC_SOURCE_DECL(src_select3, 2);
+ASRC_SOURCE_DECL(src_select4, 3);
+ASRC_SOURCE_DECL(src_select5, 4);
+ASRC_SOURCE_DECL(src_select6, 5);
+
+#define SOC_SINGLE_EXT_FRAC(xname, xregbase, xmax, xget, xput)         \
+{                                                                      \
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,                           \
+       .name   = (xname),                                              \
+       .info   = snd_soc_info_xr_sx,                                   \
+       .get    = xget,                                                 \
+       .put    = xput,                                                 \
+                                                                       \
+       .private_value = (unsigned long)&(struct soc_mreg_control)      \
+       {                                                               \
+               .regbase        = xregbase,                             \
+               .regcount       = 1,                                    \
+               .nbits          = 32,                                   \
+               .invert         = 0,                                    \
+               .min            = 0,                                    \
+               .max            = xmax                                  \
+       }                                                               \
+}
+
+static const struct snd_kcontrol_new tegra186_asrc_controls[] = {
+       /* Controls for integer part of ratio */
+       SOC_SINGLE_EXT("Ratio1 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 0),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       SOC_SINGLE_EXT("Ratio2 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 1),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       SOC_SINGLE_EXT("Ratio3 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 2),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       SOC_SINGLE_EXT("Ratio4 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 3),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       SOC_SINGLE_EXT("Ratio5 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 4),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       SOC_SINGLE_EXT("Ratio6 Integer Part",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 5),
+                      0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+                      tegra186_asrc_get_ratio_int,
+                      tegra186_asrc_put_ratio_int),
+
+       /* Controls for fractional part of ratio */
+       SOC_SINGLE_EXT_FRAC("Ratio1 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 0),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       SOC_SINGLE_EXT_FRAC("Ratio2 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 1),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       SOC_SINGLE_EXT_FRAC("Ratio3 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 2),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       SOC_SINGLE_EXT_FRAC("Ratio4 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 3),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       SOC_SINGLE_EXT_FRAC("Ratio5 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 4),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       SOC_SINGLE_EXT_FRAC("Ratio6 Fractional Part",
+                           ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 5),
+                           TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+                           tegra186_asrc_get_ratio_frac,
+                           tegra186_asrc_put_ratio_frac),
+
+       /* Source of ratio provider */
+       SOC_ENUM_EXT("Ratio1 Source", src_select1,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       SOC_ENUM_EXT("Ratio2 Source", src_select2,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       SOC_ENUM_EXT("Ratio3 Source", src_select3,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       SOC_ENUM_EXT("Ratio4 Source", src_select4,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       SOC_ENUM_EXT("Ratio5 Source", src_select5,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       SOC_ENUM_EXT("Ratio6 Source", src_select6,
+                    tegra186_asrc_get_ratio_source,
+                    tegra186_asrc_put_ratio_source),
+
+       /* Disable HW managed overflow/underflow issue at input and output */
+       SOC_SINGLE_EXT("Stream1 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 0), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       SOC_SINGLE_EXT("Stream2 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 1), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       SOC_SINGLE_EXT("Stream3 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 2), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       SOC_SINGLE_EXT("Stream4 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 3), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       SOC_SINGLE_EXT("Stream5 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 4), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       SOC_SINGLE_EXT("Stream6 HW Component Disable",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 5), 0, 1, 0,
+                      tegra186_asrc_get_hwcomp_disable,
+                      tegra186_asrc_put_hwcomp_disable),
+
+       /* Input threshold for watermark fields */
+       SOC_SINGLE_EXT("Stream1 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 0), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       SOC_SINGLE_EXT("Stream2 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 1), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       SOC_SINGLE_EXT("Stream3 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 2), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       SOC_SINGLE_EXT("Stream4 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 3), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       SOC_SINGLE_EXT("Stream5 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       SOC_SINGLE_EXT("Stream6 Input Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0,
+                      tegra186_asrc_get_input_threshold,
+                      tegra186_asrc_put_input_threshold),
+
+       /* Output threshold for watermark fields */
+       SOC_SINGLE_EXT("Stream1 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 0), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+
+       SOC_SINGLE_EXT("Stream2 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 1), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+
+       SOC_SINGLE_EXT("Stream3 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 2), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+
+       SOC_SINGLE_EXT("Stream4 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 3), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+
+       SOC_SINGLE_EXT("Stream5 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 4), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+
+       SOC_SINGLE_EXT("Stream6 Output Threshold",
+                      ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 5), 0, 3, 0,
+                      tegra186_asrc_get_output_threshold,
+                      tegra186_asrc_put_output_threshold),
+};
+
+static const struct snd_soc_component_driver tegra186_asrc_cmpnt = {
+       .dapm_widgets           = tegra186_asrc_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(tegra186_asrc_widgets),
+       .dapm_routes            = tegra186_asrc_routes,
+       .num_dapm_routes        = ARRAY_SIZE(tegra186_asrc_routes),
+       .controls               = tegra186_asrc_controls,
+       .num_controls           = ARRAY_SIZE(tegra186_asrc_controls),
+};
+
+static bool tegra186_asrc_wr_reg(struct device *dev, unsigned int reg)
+{
+       if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+               reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+       switch (reg) {
+       case TEGRA186_ASRC_CFG ... TEGRA186_ASRC_RATIO_COMP:
+       case TEGRA186_ASRC_RX_CIF_CTRL:
+       case TEGRA186_ASRC_TX_CIF_CTRL:
+       case TEGRA186_ASRC_ENABLE:
+       case TEGRA186_ASRC_SOFT_RESET:
+       case TEGRA186_ASRC_GLOBAL_ENB ... TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL:
+       case TEGRA186_ASRC_GLOBAL_INT_MASK ... TEGRA186_ASRC_GLOBAL_INT_CLEAR:
+       case TEGRA186_ASRC_GLOBAL_APR_CTRL ... TEGRA186_ASRC_CYA:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool tegra186_asrc_rd_reg(struct device *dev, unsigned int reg)
+{
+       if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+               reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+       if (tegra186_asrc_wr_reg(dev, reg))
+               return true;
+
+       switch (reg) {
+       case TEGRA186_ASRC_RX_STATUS:
+       case TEGRA186_ASRC_TX_STATUS:
+       case TEGRA186_ASRC_STATUS ... TEGRA186_ASRC_OUTSAMPLEBUF_CFG:
+       case TEGRA186_ASRC_RATIO_UPD_RX_STATUS:
+       case TEGRA186_ASRC_GLOBAL_STATUS ... TEGRA186_ASRC_GLOBAL_INT_STATUS:
+       case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool tegra186_asrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+       if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+               reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+       switch (reg) {
+       case TEGRA186_ASRC_RX_STATUS:
+       case TEGRA186_ASRC_TX_STATUS:
+       case TEGRA186_ASRC_SOFT_RESET:
+       case TEGRA186_ASRC_RATIO_INT_PART:
+       case TEGRA186_ASRC_RATIO_FRAC_PART:
+       case TEGRA186_ASRC_STATUS:
+       case TEGRA186_ASRC_RATIO_LOCK_STATUS:
+       case TEGRA186_ASRC_RATIO_UPD_RX_STATUS:
+       case TEGRA186_ASRC_GLOBAL_SOFT_RESET:
+       case TEGRA186_ASRC_GLOBAL_STATUS:
+       case TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS:
+       case TEGRA186_ASRC_GLOBAL_INT_STATUS:
+       case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config tegra186_asrc_regmap_config = {
+       .reg_bits               = 32,
+       .reg_stride             = 4,
+       .val_bits               = 32,
+       .max_register           = TEGRA186_ASRC_CYA,
+       .writeable_reg          = tegra186_asrc_wr_reg,
+       .readable_reg           = tegra186_asrc_rd_reg,
+       .volatile_reg           = tegra186_asrc_volatile_reg,
+       .reg_defaults           = tegra186_asrc_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(tegra186_asrc_reg_defaults),
+       .cache_type             = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra186_asrc_of_match[] = {
+       { .compatible = "nvidia,tegra186-asrc" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match);
+
+static int tegra186_asrc_platform_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct tegra186_asrc *asrc;
+       void __iomem *regs;
+       unsigned int i;
+       int err;
+
+       asrc = devm_kzalloc(dev, sizeof(*asrc), GFP_KERNEL);
+       if (!asrc)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, asrc);
+
+       regs = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       asrc->regmap = devm_regmap_init_mmio(dev, regs,
+                                            &tegra186_asrc_regmap_config);
+       if (IS_ERR(asrc->regmap)) {
+               dev_err(dev, "regmap init failed\n");
+               return PTR_ERR(asrc->regmap);
+       }
+
+       regcache_cache_only(asrc->regmap, true);
+
+       regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CFG,
+                    TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION);
+
+       /* Initialize default output srate */
+       for (i = 0; i < TEGRA186_ASRC_STREAM_MAX; i++) {
+               asrc->lane[i].ratio_source = TEGRA186_ASRC_RATIO_SOURCE_SW;
+               asrc->lane[i].int_part = 1;
+               asrc->lane[i].frac_part = 0;
+               asrc->lane[i].hwcomp_disable = 0;
+               asrc->lane[i].input_thresh =
+                       TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG;
+               asrc->lane[i].output_thresh =
+                       TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG;
+       }
+
+       err = devm_snd_soc_register_component(dev, &tegra186_asrc_cmpnt,
+                                             tegra186_asrc_dais,
+                                             ARRAY_SIZE(tegra186_asrc_dais));
+       if (err) {
+               dev_err(dev, "can't register ASRC component, err: %d\n", err);
+               return err;
+       }
+
+       pm_runtime_enable(dev);
+
+       return 0;
+}
+
+static int tegra186_asrc_platform_remove(struct platform_device *pdev)
+{
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct dev_pm_ops tegra186_asrc_pm_ops = {
+       SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend,
+                          tegra186_asrc_runtime_resume, NULL)
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                    pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra186_asrc_driver = {
+       .driver = {
+               .name = "tegra186-asrc",
+               .of_match_table = tegra186_asrc_of_match,
+               .pm = &tegra186_asrc_pm_ops,
+       },
+       .probe = tegra186_asrc_platform_probe,
+       .remove = tegra186_asrc_platform_remove,
+};
+module_platform_driver(tegra186_asrc_driver)
+
+MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>");
+MODULE_DESCRIPTION("Tegra186 ASRC ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra186_asrc.h b/sound/soc/tegra/tegra186_asrc.h
new file mode 100644 (file)
index 0000000..094fcc7
--- /dev/null
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra186_asrc.h - Definitions for Tegra186 ASRC driver
+ *
+ * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA186_ASRC_H__
+#define __TEGRA186_ASRC_H__
+
+/* ASRC stream related offset */
+#define TEGRA186_ASRC_CFG                              0x0
+#define TEGRA186_ASRC_RATIO_INT_PART                   0x4
+#define TEGRA186_ASRC_RATIO_FRAC_PART                  0x8
+#define TEGRA186_ASRC_RATIO_LOCK_STATUS                        0xc
+#define TEGRA186_ASRC_MUTE_UNMUTE_DURATION             0x10
+#define TEGRA186_ASRC_TX_THRESHOLD                     0x14
+#define TEGRA186_ASRC_RX_THRESHOLD                     0x18
+#define TEGRA186_ASRC_RATIO_COMP                       0x1c
+#define TEGRA186_ASRC_RX_STATUS                                0x20
+#define TEGRA186_ASRC_RX_CIF_CTRL                      0x24
+#define TEGRA186_ASRC_TX_STATUS                                0x2c
+#define TEGRA186_ASRC_TX_CIF_CTRL                      0x30
+#define TEGRA186_ASRC_ENABLE                           0x38
+#define TEGRA186_ASRC_SOFT_RESET                       0x3c
+#define TEGRA186_ASRC_STATUS                           0x4c
+#define TEGRA186_ASRC_STATEBUF_ADDR                    0x5c
+#define TEGRA186_ASRC_STATEBUF_CFG                     0x60
+#define TEGRA186_ASRC_INSAMPLEBUF_ADDR                 0x64
+#define TEGRA186_ASRC_INSAMPLEBUF_CFG                  0x68
+#define TEGRA186_ASRC_OUTSAMPLEBUF_ADDR                        0x6c
+#define TEGRA186_ASRC_OUTSAMPLEBUF_CFG                 0x70
+
+/* ASRC Global registers offset */
+#define TEGRA186_ASRC_GLOBAL_ENB                       0x2f4
+#define TEGRA186_ASRC_GLOBAL_SOFT_RESET                        0x2f8
+#define TEGRA186_ASRC_GLOBAL_CG                                0x2fc
+#define TEGRA186_ASRC_GLOBAL_CFG                       0x300
+#define TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR              0x304
+#define TEGRA186_ASRC_GLOBAL_SCRATCH_CFG               0x308
+#define TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL            0x30c
+#define TEGRA186_ASRC_RATIO_UPD_RX_STATUS              0x310
+#define TEGRA186_ASRC_GLOBAL_STATUS                    0x314
+#define TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS      0x318
+#define TEGRA186_ASRC_GLOBAL_INT_STATUS                        0x324
+#define TEGRA186_ASRC_GLOBAL_INT_MASK                  0x328
+#define TEGRA186_ASRC_GLOBAL_INT_SET                   0x32c
+#define TEGRA186_ASRC_GLOBAL_INT_CLEAR                 0x330
+#define TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG                0x334
+#define TEGRA186_ASRC_GLOBAL_APR_CTRL                  0x1000
+#define TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL      0x1004
+#define TEGRA186_ASRC_GLOBAL_DISARM_APR                        0x1008
+#define TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL    0x100c
+#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS           0x1010
+#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL      0x1014
+#define TEGRA186_ASRC_CYA                              0x1018
+
+#define TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE                0xaaaa
+#define TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG  0x00201002
+#define TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG 0x00201002
+
+#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_28BIT_PRECISION          0
+#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION          1
+
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT                31
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK         (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE       (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE      (0 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT                  0
+#define TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK                   (1 << TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_EN_SHIFT                          0
+#define TEGRA186_ASRC_STREAM_EN                                        (1 << TEGRA186_ASRC_STREAM_EN_SHIFT)
+#define TEGRA186_ASRC_GLOBAL_EN_SHIFT                          0
+#define TEGRA186_ASRC_GLOBAL_EN                                        (1 << TEGRA186_ASRC_GLOBAL_EN_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT           0
+#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_MASK            (0xffff << TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT)
+#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT                0
+#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_MASK         (0xffff << TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT)
+#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT       0
+#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_MASK                (0xffff << TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK               0x1f
+#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK              0xffffffff
+
+#define TEGRA186_ASRC_STREAM_STRIDE                            0x80
+#define TEGRA186_ASRC_STREAM_MAX                               0x6
+#define TEGRA186_ASRC_STREAM_LIMIT                             0x2f0
+
+#define TEGRA186_ASRC_RATIO_SOURCE_ARAD                                0x0
+#define TEGRA186_ASRC_RATIO_SOURCE_SW                          0x1
+
+#define TEGRA186_ASRC_ARAM_START_ADDR                          0x3f800000
+
+struct tegra186_asrc_lane {
+       unsigned int int_part;
+       unsigned int frac_part;
+       unsigned int ratio_source;
+       unsigned int hwcomp_disable;
+       unsigned int input_thresh;
+       unsigned int output_thresh;
+};
+
+struct tegra186_asrc {
+       struct tegra186_asrc_lane lane[TEGRA186_ASRC_STREAM_MAX];
+       struct regmap *regmap;
+};
+
+#endif
index bccf8b8..e1f90da 100644 (file)
@@ -280,6 +280,20 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
        DAI(MIXER1 TX3),
        DAI(MIXER1 TX4),
        DAI(MIXER1 TX5),
+       /* XBAR -> ASRC -> XBAR */
+       DAI(ASRC1 RX1),
+       DAI(ASRC1 TX1),
+       DAI(ASRC1 RX2),
+       DAI(ASRC1 TX2),
+       DAI(ASRC1 RX3),
+       DAI(ASRC1 TX3),
+       DAI(ASRC1 RX4),
+       DAI(ASRC1 TX4),
+       DAI(ASRC1 RX5),
+       DAI(ASRC1 TX5),
+       DAI(ASRC1 RX6),
+       DAI(ASRC1 TX6),
+       DAI(ASRC1 RX7),
 };
 
 static const char * const tegra210_ahub_mux_texts[] = {
@@ -388,6 +402,12 @@ static const char * const tegra186_ahub_mux_texts[] = {
        "MIXER1 TX3",
        "MIXER1 TX4",
        "MIXER1 TX5",
+       "ASRC1 TX1",
+       "ASRC1 TX2",
+       "ASRC1 TX3",
+       "ASRC1 TX4",
+       "ASRC1 TX5",
+       "ASRC1 TX6",
 };
 
 static const unsigned int tegra210_ahub_mux_values[] = {
@@ -513,6 +533,13 @@ static const unsigned int tegra186_ahub_mux_values[] = {
        MUX_VALUE(1, 2),
        MUX_VALUE(1, 3),
        MUX_VALUE(1, 4),
+       /* ASRC */
+       MUX_VALUE(3, 24),
+       MUX_VALUE(3, 25),
+       MUX_VALUE(3, 26),
+       MUX_VALUE(3, 27),
+       MUX_VALUE(3, 28),
+       MUX_VALUE(3, 29),
 };
 
 /* Controls for t210 */
@@ -623,6 +650,13 @@ MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26);
 MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27);
 MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28);
 MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29);
+MUX_ENUM_CTRL_DECL_186(t186_asrc11_tx, 0x6c);
+MUX_ENUM_CTRL_DECL_186(t186_asrc12_tx, 0x6d);
+MUX_ENUM_CTRL_DECL_186(t186_asrc13_tx, 0x6e);
+MUX_ENUM_CTRL_DECL_186(t186_asrc14_tx, 0x6f);
+MUX_ENUM_CTRL_DECL_186(t186_asrc15_tx, 0x70);
+MUX_ENUM_CTRL_DECL_186(t186_asrc16_tx, 0x71);
+MUX_ENUM_CTRL_DECL_186(t186_asrc17_tx, 0x72);
 
 /* Controls for t234 */
 MUX_ENUM_CTRL_DECL_234(t234_mvc1_tx, 0x44);
@@ -651,6 +685,13 @@ MUX_ENUM_CTRL_DECL_234(t234_admaif17_tx, 0x60);
 MUX_ENUM_CTRL_DECL_234(t234_admaif18_tx, 0x61);
 MUX_ENUM_CTRL_DECL_234(t234_admaif19_tx, 0x62);
 MUX_ENUM_CTRL_DECL_234(t234_admaif20_tx, 0x63);
+MUX_ENUM_CTRL_DECL_234(t234_asrc11_tx, 0x64);
+MUX_ENUM_CTRL_DECL_234(t234_asrc12_tx, 0x65);
+MUX_ENUM_CTRL_DECL_234(t234_asrc13_tx, 0x66);
+MUX_ENUM_CTRL_DECL_234(t234_asrc14_tx, 0x67);
+MUX_ENUM_CTRL_DECL_234(t234_asrc15_tx, 0x68);
+MUX_ENUM_CTRL_DECL_234(t234_asrc16_tx, 0x69);
+MUX_ENUM_CTRL_DECL_234(t234_asrc17_tx, 0x6a);
 
 /*
  * The number of entries in, and order of, this array is closely tied to the
@@ -813,6 +854,19 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
        TX_WIDGETS("MIXER1 TX3"),
        TX_WIDGETS("MIXER1 TX4"),
        TX_WIDGETS("MIXER1 TX5"),
+       WIDGETS("ASRC1 RX1", t186_asrc11_tx),
+       WIDGETS("ASRC1 RX2", t186_asrc12_tx),
+       WIDGETS("ASRC1 RX3", t186_asrc13_tx),
+       WIDGETS("ASRC1 RX4", t186_asrc14_tx),
+       WIDGETS("ASRC1 RX5", t186_asrc15_tx),
+       WIDGETS("ASRC1 RX6", t186_asrc16_tx),
+       WIDGETS("ASRC1 RX7", t186_asrc17_tx),
+       TX_WIDGETS("ASRC1 TX1"),
+       TX_WIDGETS("ASRC1 TX2"),
+       TX_WIDGETS("ASRC1 TX3"),
+       TX_WIDGETS("ASRC1 TX4"),
+       TX_WIDGETS("ASRC1 TX5"),
+       TX_WIDGETS("ASRC1 TX6"),
 };
 
 static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
@@ -909,6 +963,19 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
        TX_WIDGETS("MIXER1 TX3"),
        TX_WIDGETS("MIXER1 TX4"),
        TX_WIDGETS("MIXER1 TX5"),
+       WIDGETS("ASRC1 RX1", t234_asrc11_tx),
+       WIDGETS("ASRC1 RX2", t234_asrc12_tx),
+       WIDGETS("ASRC1 RX3", t234_asrc13_tx),
+       WIDGETS("ASRC1 RX4", t234_asrc14_tx),
+       WIDGETS("ASRC1 RX5", t234_asrc15_tx),
+       WIDGETS("ASRC1 RX6", t234_asrc16_tx),
+       WIDGETS("ASRC1 RX7", t234_asrc17_tx),
+       TX_WIDGETS("ASRC1 TX1"),
+       TX_WIDGETS("ASRC1 TX2"),
+       TX_WIDGETS("ASRC1 TX3"),
+       TX_WIDGETS("ASRC1 TX4"),
+       TX_WIDGETS("ASRC1 TX5"),
+       TX_WIDGETS("ASRC1 TX6"),
 };
 
 #define TEGRA_COMMON_MUX_ROUTES(name)                                  \
@@ -975,7 +1042,13 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
        { name " Mux",          "ADX4 TX1",     "ADX4 TX1 XBAR-RX" },   \
        { name " Mux",          "ADX4 TX2",     "ADX4 TX2 XBAR-RX" },   \
        { name " Mux",          "ADX4 TX3",     "ADX4 TX3 XBAR-RX" },   \
-       { name " Mux",          "ADX4 TX4",     "ADX4 TX4 XBAR-RX" },
+       { name " Mux",          "ADX4 TX4",     "ADX4 TX4 XBAR-RX" },   \
+       { name " Mux",          "ASRC1 TX1",    "ASRC1 TX1 XBAR-RX" },  \
+       { name " Mux",          "ASRC1 TX2",    "ASRC1 TX2 XBAR-RX" },  \
+       { name " Mux",          "ASRC1 TX3",    "ASRC1 TX3 XBAR-RX" },  \
+       { name " Mux",          "ASRC1 TX4",    "ASRC1 TX4 XBAR-RX" },  \
+       { name " Mux",          "ASRC1 TX5",    "ASRC1 TX5 XBAR-RX" },  \
+       { name " Mux",          "ASRC1 TX6",    "ASRC1 TX6 XBAR-RX" },
 
 #define TEGRA210_MUX_ROUTES(name)                                              \
        TEGRA_COMMON_MUX_ROUTES(name)
@@ -1135,6 +1208,13 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
        TEGRA186_MUX_ROUTES("MIXER1 RX8")
        TEGRA186_MUX_ROUTES("MIXER1 RX9")
        TEGRA186_MUX_ROUTES("MIXER1 RX10")
+       TEGRA186_MUX_ROUTES("ASRC1 RX1")
+       TEGRA186_MUX_ROUTES("ASRC1 RX2")
+       TEGRA186_MUX_ROUTES("ASRC1 RX3")
+       TEGRA186_MUX_ROUTES("ASRC1 RX4")
+       TEGRA186_MUX_ROUTES("ASRC1 RX5")
+       TEGRA186_MUX_ROUTES("ASRC1 RX6")
+       TEGRA186_MUX_ROUTES("ASRC1 RX7")
 };
 
 static const struct snd_soc_component_driver tegra210_ahub_component = {
index a734048..78faa8b 100644 (file)
@@ -133,11 +133,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
                else
                        jack_name = "Headphones Jack";
 
-               err = snd_soc_card_jack_new(card, jack_name,
-                                           SND_JACK_HEADPHONE,
-                                           &tegra_machine_hp_jack,
-                                           tegra_machine_hp_jack_pins,
-                                           ARRAY_SIZE(tegra_machine_hp_jack_pins));
+               err = snd_soc_card_jack_new_pins(card, jack_name,
+                                                SND_JACK_HEADPHONE,
+                                                &tegra_machine_hp_jack,
+                                                tegra_machine_hp_jack_pins,
+                                                ARRAY_SIZE(tegra_machine_hp_jack_pins));
                if (err) {
                        dev_err(rtd->dev,
                                "Headphones Jack creation failed: %d\n", err);
@@ -153,11 +153,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        if (machine->gpiod_hp_det && machine->asoc->add_headset_jack) {
-               err = snd_soc_card_jack_new(card, "Headset Jack",
-                                           SND_JACK_HEADSET,
-                                           &tegra_machine_headset_jack,
-                                           tegra_machine_headset_jack_pins,
-                                           ARRAY_SIZE(tegra_machine_headset_jack_pins));
+               err = snd_soc_card_jack_new_pins(card, "Headset Jack",
+                                                SND_JACK_HEADSET,
+                                                &tegra_machine_headset_jack,
+                                                tegra_machine_headset_jack_pins,
+                                                ARRAY_SIZE(tegra_machine_headset_jack_pins));
                if (err) {
                        dev_err(rtd->dev,
                                "Headset Jack creation failed: %d\n", err);
@@ -173,11 +173,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        if (machine->gpiod_mic_det && machine->asoc->add_mic_jack) {
-               err = snd_soc_card_jack_new(rtd->card, "Mic Jack",
-                                           SND_JACK_MICROPHONE,
-                                           &tegra_machine_mic_jack,
-                                           tegra_machine_mic_jack_pins,
-                                           ARRAY_SIZE(tegra_machine_mic_jack_pins));
+               err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+                                                SND_JACK_MICROPHONE,
+                                                &tegra_machine_mic_jack,
+                                                tegra_machine_mic_jack_pins,
+                                                ARRAY_SIZE(tegra_machine_mic_jack_pins));
                if (err) {
                        dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
                        return err;
index 5751fb3..b3cd0a3 100644 (file)
@@ -79,11 +79,11 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
                struct snd_soc_component *component = codec_dai->component;
                int shrt = 0;
 
-               err = snd_soc_card_jack_new(rtd->card, "Mic Jack",
-                                           SND_JACK_MICROPHONE,
-                                           machine->mic_jack,
-                                           tegra_wm8903_mic_jack_pins,
-                                           ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
+               err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+                                                SND_JACK_MICROPHONE,
+                                                machine->mic_jack,
+                                                tegra_wm8903_mic_jack_pins,
+                                                ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
                if (err) {
                        dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
                        return err;
index b1a3254..438e2fa 100644 (file)
@@ -471,8 +471,8 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
 
        /* Add hook switch - can be used to control the codec from userspace
         * even if line discipline fails */
-       ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
-                                   &ams_delta_hook_switch, NULL, 0);
+       ret = snd_soc_card_jack_new_pins(card, "hook_switch", SND_JACK_HEADSET,
+                                        &ams_delta_hook_switch, NULL, 0);
        if (ret)
                dev_warn(card->dev,
                                "Failed to allocate resources for hook switch, "
index 2c146b9..377be2e 100644 (file)
@@ -2047,6 +2047,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
                return PCM_SDMA;
        else if (strstr(tmp, "udmap"))
                return PCM_UDMA;
+       else if (strstr(tmp, "bcdma"))
+               return PCM_UDMA;
 
        return PCM_EDMA;
 }
index 4077e15..6a96987 100644 (file)
@@ -630,17 +630,18 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
        codec_node = of_parse_phandle(node, "ti,cpb-codec", 0);
        if (!codec_node) {
                dev_err(priv->dev, "CPB codec node is not provided\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto put_dai_node;
        }
 
        domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB];
        ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki");
        if (ret)
-               return ret;
+               goto put_codec_node;
 
        ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk");
        if (ret)
-               return ret;
+               goto put_codec_node;
 
        /*
         * Common Processor Board, two links
@@ -650,8 +651,10 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
        comp_count = 6;
        compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
                                GFP_KERNEL);
-       if (!compnent)
-               return -ENOMEM;
+       if (!compnent) {
+               ret = -ENOMEM;
+               goto put_codec_node;
+       }
 
        comp_idx = 0;
        priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
@@ -702,6 +705,12 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
        (*conf_idx)++;
 
        return 0;
+
+put_codec_node:
+       of_node_put(codec_node);
+put_dai_node:
+       of_node_put(dai_node);
+       return ret;
 }
 
 static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
@@ -726,23 +735,25 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
        codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0);
        if (!codeca_node) {
                dev_err(priv->dev, "IVI codec-a node is not provided\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto put_dai_node;
        }
 
        codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0);
        if (!codecb_node) {
                dev_warn(priv->dev, "IVI codec-b node is not provided\n");
-               return 0;
+               ret = 0;
+               goto put_codeca_node;
        }
 
        domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI];
        ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki");
        if (ret)
-               return ret;
+               goto put_codecb_node;
 
        ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk");
        if (ret)
-               return ret;
+               goto put_codecb_node;
 
        /*
         * IVI extension, two links
@@ -754,8 +765,10 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
        comp_count = 8;
        compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
                                GFP_KERNEL);
-       if (!compnent)
-               return -ENOMEM;
+       if (!compnent) {
+               ret = -ENOMEM;
+               goto put_codecb_node;
+       }
 
        comp_idx = 0;
        priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
@@ -816,6 +829,15 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
        (*conf_idx)++;
 
        return 0;
+
+
+put_codecb_node:
+       of_node_put(codecb_node);
+put_codeca_node:
+       of_node_put(codeca_node);
+put_dai_node:
+       of_node_put(dai_node);
+       return ret;
 }
 
 static int j721e_soc_probe(struct platform_device *pdev)
index da809c7..805ffbf 100644 (file)
@@ -182,10 +182,10 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
 
        /* Headset jack detection only if it is supported */
        if (priv->jack_detection) {
-               ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                           SND_JACK_HEADSET, &hs_jack,
-                                           hs_jack_pins,
-                                           ARRAY_SIZE(hs_jack_pins));
+               ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                                SND_JACK_HEADSET, &hs_jack,
+                                                hs_jack_pins,
+                                                ARRAY_SIZE(hs_jack_pins));
                if (ret)
                        return ret;
 
index 1da05a6..950eec4 100644 (file)
@@ -155,10 +155,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
        if (priv->jack_detect > 0) {
                hs_jack_gpios[0].gpio = priv->jack_detect;
 
-               ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
-                                           SND_JACK_HEADSET, &priv->hs_jack,
-                                           hs_jack_pins,
-                                           ARRAY_SIZE(hs_jack_pins));
+               ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+                                                SND_JACK_HEADSET,
+                                                &priv->hs_jack, hs_jack_pins,
+                                                ARRAY_SIZE(hs_jack_pins));
                if (ret)
                        return ret;
 
index 40e29dd..2790c89 100644 (file)
@@ -27,12 +27,12 @@ static struct clk *tlv320aic23_mclk;
 
 static int osk_startup(struct snd_pcm_substream *substream)
 {
-       return clk_enable(tlv320aic23_mclk);
+       return clk_prepare_enable(tlv320aic23_mclk);
 }
 
 static void osk_shutdown(struct snd_pcm_substream *substream)
 {
-       clk_disable(tlv320aic23_mclk);
+       clk_disable_unprepare(tlv320aic23_mclk);
 }
 
 static int osk_hw_params(struct snd_pcm_substream *substream,
index a2629cc..322c398 100644 (file)
@@ -277,7 +277,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
        /* AV jack detection */
        err = snd_soc_card_jack_new(rtd->card, "AV Jack",
                                    SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
-                                   &rx51_av_jack, NULL, 0);
+                                   &rx51_av_jack);
        if (err) {
                dev_err(card->dev, "Failed to add AV Jack\n");
                return err;
index 0f76bc6..7d1492c 100644 (file)
@@ -139,7 +139,6 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component,
        struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
        struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
        struct device *dev = &aio->chip->pdev->dev;
-       int ret;
 
        if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
                dev_err(dev, "Codec ID is not supported(%d)\n",
@@ -161,11 +160,7 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component,
        aio_port_reset(sub);
        aio_src_reset(sub);
 
-       ret = uniphier_aio_compr_prepare(component, cstream);
-       if (ret)
-               return ret;
-
-       return 0;
+       return uniphier_aio_compr_prepare(component, cstream);
 }
 
 static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
index 3e654e7..1ea1729 100644 (file)
@@ -433,12 +433,9 @@ void mop500_ab8500_remove(struct snd_soc_card *card)
 {
        struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card);
 
-       if (drvdata->clk_ptr_sysclk != NULL)
-               clk_put(drvdata->clk_ptr_sysclk);
-       if (drvdata->clk_ptr_ulpclk != NULL)
-               clk_put(drvdata->clk_ptr_ulpclk);
-       if (drvdata->clk_ptr_intclk != NULL)
-               clk_put(drvdata->clk_ptr_intclk);
+       clk_put(drvdata->clk_ptr_sysclk);
+       clk_put(drvdata->clk_ptr_ulpclk);
+       clk_put(drvdata->clk_ptr_intclk);
 
        snd_soc_card_set_drvdata(card, drvdata);
 }